【Kivy】Pythonで迷路開発Part7

【Kivy】Pythonで迷路開発Part7を表すサムネイル

こんにちは、にわこまです。

今回は、プレイヤーを動かす関数を作成します。壁との当たり判定が少し複雑になっています。条件分岐が多いため、考えがぐちゃぐちゃにならないようにしましょう。

 

誤字脱字や分からない点が、ございましたらご連絡お願いいたします。

 

 

スポンサードサーチ


【Kivy】Pythonで迷路開発Part7

Part7を表す画像

今回のシリーズは、2Dの迷路を開発することを目的としています。(プレイヤーを操作してスタートからゴールまでの時間を競います。)

 

今回のシリーズの主な内容

1.迷路の盤面を生成するプログラムの開発

2.Kivyで迷路アプリケーション開発

 

迷路の盤面を生成するプログラムでは、手書きで迷路を作るような処理を、プログラムに直していきます。

Kivyでの開発は、プレイヤーと壁との当たり判定に気を付けて開発を行います。

 

 

今回は

今回は、プレイヤーを動かす関数を作成します。

 

 

プレイヤーを動かす関数1

プレイヤーを動かす関数を表す画像

プレイヤーの移動はプレイヤーに大きさがあるため、壁との当たり判定複雑になっています。しかし、上下左右どれかの方向の当たり判定を理解できれば、他の方向の当たり判定も理解することができます。

 

まずは、プレイヤーがウィンドウ内だけを移動することができる関数を作成します。

プレイヤーを動かす関数(_on_keyboard_down)です。labyrinthgame.pyに以下のコードを追記します。

# 上記省略

class FieldWidget(Widget):
    def __init__(self, **kwargs):
        # 省略
    def _keyboard_closed(self):
        # 省略
    def _on_keyboard_down(self, keyboard, keycode, text, modifiers):
        if(self.evt == None):
            self.evt = Clock.schedule_interval(self.countUp, 1)
        xpoint, ypoint, rightpoint, toppoint = self.calcPos()
        print("x, y, right, top = {0}, {1}, {2}, {3}".format(xpoint, ypoint, rightpoint, toppoint))
        if(keycode[1] == 'left' and self.x <= (self.player.x-self.diff)):
            self.player.center_x -= self.diff
        elif(keycode[1] == 'up' and self.top >= (self.player.top+self.diff)):
            self.player.center_y += self.diff
        elif(keycode[1] == 'right' and self.right >= (self.player.right+self.diff)):
            self.player.center_x += self.diff
        elif(keycode[1] == 'down' and self.y <= (self.player.y-self.diff)):
            self.player.center_y -= self.diff
        xpoint, ypoint, rightpoint, toppoint = self.calcPos()
        if((ypoint, xpoint) == self.goalpos and (toppoint, rightpoint) == self.goalpos and self.evt != None):
            self.evt.cancel()
            self.time = 0
            self._keyboard_closed()
    def calcPos(self):
        # 省略
    def drawBlocks(self, *args):
        # 省略
    def countUp(self, *args):
        # 省略
    pass

# 下記省略

 

 

コードの解説

13、15、17、19行目は、それぞれその方向に「+self.diff」または「-self.diff」したとき、ウィンドウの範囲を超えないか確認しています。

 

13行目の左方向は、ウィンドウの最左(self.x)よりマイナスself.diffしたプレイヤーの最左(self.player.x)が小さくならないか判別しています。

14行目では、プレイヤーを左側にself.diff分だけ移動させています。

 

15行目の上方向は、ウィンドウの最上(self.top)よりプラスself.diffしたプレイヤーの最上(self.player.top)が大きくならないか判別しています。

16行目では、プレイヤーを上側にself.diff分だけ移動させています。

 

17行目の右方向は、ウィンドウの最右(self.right)よりプラスself.diffしたプレイヤーの最右(self.player.right)が大きくならないか判別しています。

18行目では、プレイヤーを右側にself.diff分だけ移動しています。

 

19行目の下方向は、ウィンドウの最下(self.y)よりマイナスself.diffしたプレイヤーの最下(self.player.y)が小さくならないか判別しています。

20行目では、プレイヤーを下側にself.diff分だけ移動させています。

 

 

動作確認

3つの動作確認を行います。

・kivyの座標からnp.arrayの座標へ変換する関数(calcPos)(Part6)

・ゴールへ到着したときカウントが止まる処理(countUp)(Part6)

・プレイヤーがウィンドウを超えて移動しないこと

 

コマンドプロンプトを開き、ファイルを保存したフォルダまで移動します。移動したら以下のコマンドを入力し、実行します。

python labyrinthgame.py

 

以下のように、ウィンドウの範囲を超えてプレイヤーが移動しないことを確認すると共に、四つ角へ移動したとき座標が「左上=(0, 0, 0, 0)」、「右上=(9, 0, 9, 0)」、「左下=(0, 7, 0, 7)」、「右下=(9, 7, 9, 7)」になることを確認します。

左上にプレイヤーがいる画像
右上にプレイヤーがいる画像
左下にプレイヤーがいる画像
右下にプレイヤーがいる画像

 

さらに、ゴールへ到達した以降はカウントが増えないことも確認します。

ゴールにプレイヤーがいる画像

 

以上で動作確認は完了です。

 

 

スポンサードサーチ


プレイヤーを動かす関数2

プレイヤーを動かす関数を表す画像

当たり判定は、1方向につき3種類あります。

1.その方向が壁である。

2.その方向が道であり、プレイヤーの最下と最上(最左と最右)が同じ座標にある。

3.その方向が道であり、プレイヤーの最下と最上(最左と最右)が同じ座標にない。

図にして示します。(左方向について)

プレイヤーの当たり判定について

1と3に関しては、壁との距離がself.diff以上あればその方にself.diff分だけ移動することができます。

 

プレイヤーを動かす関数(_on_keyboard_down)のソースコードです。labyrinthgame.pyに以下のコードを追記します。

# 上記省略

class FieldWidget(Widget):
    def __init__(self, **kwargs):
        # 省略
    def _keyboard_closed(self):
        # 省略
    def _on_keyboard_down(self, keyboard, keycode, text, modifiers):
        if(self.evt == None):
            self.evt = Clock.schedule_interval(self.countUp, 1)
        xpoint, ypoint, rightpoint, toppoint = self.calcPos()
        #print("x, y, right, top = {0}, {1}, {2}, {3}".format(xpoint, ypoint, rightpoint, toppoint))
        if(keycode[1] == 'left' and self.x <= (self.player.x-self.diff)):
            if(self.field[ypoint, xpoint-1] == 0):
                if(xpoint*self.xpos <= self.player.x-self.diff):
                    self.player.center_x -= self.diff
            elif(self.field[ypoint, xpoint-1] != 0):
                if(ypoint == toppoint):
                    self.player.center_x -= self.diff
                elif(ypoint != toppoint):
                    if(xpoint*self.xpos <= self.player.x-self.diff):
                        self.player.center_x -= self.diff
        elif(keycode[1] == 'up' and self.top >= (self.player.top+self.diff)):
            if(self.field[toppoint-1, rightpoint] == 0):
                if((self.row-toppoint)*self.ypos >= self.player.top+self.diff):
                    self.player.center_y += self.diff
            elif(self.field[toppoint-1, rightpoint] != 0):
                if(xpoint == rightpoint):
                    self.player.center_y += self.diff
                elif(xpoint != rightpoint):
                    if((self.row-toppoint)*self.ypos >= self.player.top+self.diff):
                        self.player.center_y += self.diff
        elif(keycode[1] == 'right' and self.right >= (self.player.right+self.diff)):
            if(self.field[toppoint, rightpoint+1] == 0):
                if((rightpoint+1)*self.xpos >= self.player.right+self.diff):
                    self.player.center_x += self.diff
            elif(self.field[toppoint, rightpoint+1] != 0):
                if(ypoint == toppoint):
                    self.player.center_x += self.diff
                elif(ypoint != toppoint):
                    if((rightpoint+1)*self.xpos >= self.player.right+self.diff):
                        self.player.center_x += self.diff
        elif(keycode[1] == 'down' and self.y <= (self.player.y-self.diff)):
            if(self.field[ypoint+1, xpoint] == 0):
                if((self.row-ypoint-1)*self.ypos <= self.player.y-self.diff):
                    self.player.center_y -= self.diff
            elif(self.field[ypoint+1, xpoint] != 0):
                if(xpoint == rightpoint):
                    self.player.center_y -= self.diff
                elif(xpoint != rightpoint):
                    if((self.row-ypoint-1)*self.ypos <= self.player.y-self.diff):
                        self.player.center_y -= self.diff
        xpoint, ypoint, rightpoint, toppoint = self.calcPos()
        if((ypoint, xpoint) == self.goalpos and (toppoint, rightpoint) == self.goalpos and self.evt != None):
            self.evt.cancel()
            self.time = 0
            self._keyboard_closed()
    def calcPos(self):
        for i in range(self.row):
            if(i*self.ypos <= self.player.y and self.player.y < i*self.ypos+self.ypos):
                ypoint = self.row - 1 - i
                if(i*self.ypos <= self.player.top and self.player.top < i*self.ypos+self.ypos):
                    toppoint = ypoint
                else:
                    toppoint = ypoint + 1
                break
        for i in range(self.col):
            if(i*self.xpos <= self.player.x and self.player.x < i*self.xpos+self.xpos):
                xpoint = i
                if(xpoint*self.xpos <= self.player.right and self.player.right < xpoint*self.xpos+self.xpos):
                    rightpoint = xpoint
                else:
                    rightpoint = xpoint + 1
                break
        return xpoint, ypoint, rightpoint, toppoint
    def drawBlocks(self, *args):
        # 省略
    def countUp(self, *args):
        # 省略
    pass

# 下記省略

 

 

コードの解説

13行目~22行目は、左方向へプレイヤーを動かす処理です。

14、17行目の「if文」は、現在の座標の左隣が壁であるか判別しています。

18、20行目の「if文」は、プレイヤーの最下と最上が同じ座標にあるか判別しています。

15、21行目の「if文」は、左の壁よりマイナスself.diffしたプレイヤーの最左(self.player.x)が小さくならないか判別しています。

 

23行目~32行目は、上方向へプレイヤーを動かす処理です。

24、27行目の「if文」は、現在の座標の上隣が壁であるか判別しています。

28、30行目の「if文」は、プレイヤーの最左と最右が同じ座標にあるか判別しています。

25、31行目の「if文」は、上の壁よりプラスself.diffしたプレイヤーの最上(self.player.top)が大きくならないか判別しています。

 

33行目~42行目は、右方向へプレイヤーを動かす処理です。

34、37行目の「if文」は、現在の座標の右隣が壁であるか判別しています。

38、40行目の「if文」は、プレイヤーの最下と最上が同じ座標にあるか判別しています。

35、41行目の「if文」は、右の壁よりプラスself.diffしたプレイヤーの最右(self.player.right)が大きくならないか判別しています。

 

43行目~52行目は、下方向へプレイヤーを動かす処理です。

44、47行目の「if文」は、現在の座標の下隣が壁であるか判別しています。

48、50行目の「if文」は、プレイヤーの最左と最右が同じ座標にあるか判別しています。

45、51行目の「if文」は、下の壁よりマイナスself.diffしたプレイヤーの最下(self.player.y)が小さくならないか判別しています。

 

 

動作確認

迷路のいろんな壁に衝突して、壁を貫通しないか動作確認を行います。コマンドプロンプトを開きファイルを保存したフォルダまで移動します。移動したら以下のコマンドを入力し実行します。

python labyrinthgame.py

 

迷路のいろんな壁に衝突して壁を貫通しなければ、正しく動作しています。

以上で動作確認は完了です。

 

 

Reset

Resetを表す画像

Resetは、迷路のプレイ中またはゴールした後にスタートからやり直すための関数です。

計測時間を初期値へ戻したり、キーボード入力を使えるように戻したりします。

 

Reset(on_press_resetbtn)のソースコードです。labyrintgame.pyに以下のコードを追記します。

# 上記省略

class PlayWidget(BoxLayout):
    def on_press_resetbtn(self, *args):
        if(type(self.ids.fw.evt) == kivy._clock.ClockEvent):
            self.ids.fw.evt.cancel()
            self.ids.fw.evt = None
        self.ids.fw.time = 0
        self.ids.timelb.text = "Start"
        self.ids.fw.drawBlocks()
        self.ids.fw._keyboard.bind(on_key_down=self.ids.fw._on_keyboard_down)
    pass

# 下記省略

 

 

コードの解説

5行目の「if文」は、FieldWidgetのevtにClockオブジェクトが代入されているか判別しています。つまり、計測を開始しているか判別しています。

6行目の「self.ids.fw.evt.cancel()」は、計測を中止しています。

7行目の「self.ids.fw.evt = None」は、FieldWidgetのevtを初期値に戻しています。

8行目の「self.ids.fw.time = 0」は、計測時間を代入する変数を初期値に戻しています。

9行目の「self.ids.timelb.text = “Start”」は、計測時間を表示するラベルを初期文字列に戻しています。

10行目の「self.ids.fw.drawBlocks()」は、プレイヤーをスタート地点に戻すため再度盤面を表示しています。

11行目は、キーボード入力ができるように戻しています。

 

 

動作確認

プレイヤーがスタート地点に戻るかなどを確認します。コマンドプロンプトを開き、ファイルを保存したフォルダまで移動します。移動したら以下のコマンドを入力、実行します。

python labyrinthgame.py

 

迷路をプレイ中やゴールした後にリセットボタンを押して、プレイヤーがスタート地点に戻ることや計測時間を表示するラベルにStartが表示されていることを確認します。確認できれば、正しく動作しています。

 

以上で動作確認は完了です。

また、labyrinthgame.pyの完成です。

 

 

スポンサードサーチ


まとめ

まとめの画像

今回は、プレイヤーを動かす関数とリセットボタンの処理を作成しました。

プレイヤーの移動に関しては、1方向に3つの条件分岐がありました。

 

今回作成したコードを以下に示します。

 

迷路ゲームを完成させることが出来ました。

比較的簡単な迷路ばかりであったため、エクストラとして難しい迷路開発を考えてみてください。

 

 

最後までお読みいただきありがとうございます。


スポンサードサーチ