Pyxelで倉庫番ゲームを作る(後編)

###始めに
この記事は[Pyxelで倉庫番ゲームを作る(前編)](https://qiita.com/2448/items/dea71fc35024b9a242a3)からの続きになります。前回の記事の最後にお伝えしたように今回は箱を実装します。

##箱の実装
箱にもクラスを作りましょう。「座標」と「この箱は動くか、動かないか」(←後で使う)を表すクラス変数を作ります。そして箱はたくさん増えるかも知れないのでリストにクラスを格納します。

```box.py
#追加
class Box:
    def __init__(self,x,y):
        #座標
        self.x=x
        self.y=y
        #この箱は動くか、動かないか(←後で使う)
        self.move=0

class App:
    #追加
    #初期座標が16,16と40,16の箱が完成する
    box=[Box(16,16),Box(40,16)]

    #画面描画
    def draw(self):
        #追加
        #箱の描画
        #boxリストの要素数分だけループする
        for box in self.box:
            pyxel.blt(box.x,box.y,0,8,16,8,8,0)
```
実行結果
![pyxel-190317-093122.gif](https://qiita-image-store.s3.amazonaws.com/0/372537/882a1810-c39f-9f5c-a3e7-b43752a695d8.gif)

次は箱を押せるようにしましょう。まず進行方向に箱があるかどうかを確認する必要があるのですが、箱は壁とは違いタイルマップ上に配置していない(背景ではない)ため、タイルマップのデータを取得し判断するといった方法が出来ません。なのでプレイヤーも箱も8ドット刻みで動くということを生かして、プレイヤーの座標を進行方向に8ドット進めたものと箱の座標が一致したら箱を押す(箱が動く)という処理にします。
・・・今さらですが壁も8ドット刻みで配置してあるのでタイルマップからデータを取得して判断する必要もないんですよね...。まぁそれはそれ、これはこれということで。

```.py
for box in self.box:
    #進行方向に箱があればクラス変数moveをTrueにする
    if (self.player.x+self.move_x*8==box.x) and (self.player.y+self.move_y*8==box.y):
        box.move=1
    #ないならFalseにする
    else
        box.move=0
```

倉庫番のルールとして「一度に押せる箱は一つだけ」というものがあり、先ほどの処理だけではそのルールを無視してしまいますし、更に壁に向かって押しても箱が移動してしまうのでそれを阻止します。クラス変数のmoveがFalseになるだけでなくプレイヤーも移動しないようにmove_countを0にしなければいけませんね。

```.py
for box_2 in self.box:
    #プレイヤーが押そうとした箱の移動方向に箱がある
    if (box.x+self.move_x*8==box_2.x) and\
       (box.y+self.move_y*8==box_2.y) or\
       #もしくはプレイヤーが押そうとした箱の移動方向に壁がある(タイルマップからデータを取得して判断)
       #なら箱もプレイヤーも移動しない
       (pyxel.tilemap(0).get(math.floor(box.x/7)+self.move_x,math.floor(box.y/7)+self.move_y)>=65):
        box.move=0
        self.move_count=0
        break
```

これらのものを全部繋げるとこうなります。

```box.py
class App:
    #移動関数
    def move(self,x,y):
            #追加
            for box in self.box:
                #進行方向に箱があればクラス変数moveをTrueにする
                if (self.player.x+self.move_x*8==box.x) and (self.player.y+self.move_y*8==box.y):
                    box.move=1
                    for box_2 in self.box:
                           #プレイヤーが押そうとした箱の移動方向に箱がある、もしくはプレイヤーが押そうとした箱の移動方向に壁がある(タイルマップからデータを取得して判断)なら箱もプレイヤーも移動しない
                            if (box.x+self.move_x*8==box_2.x) and (box.y+self.move_y*8==box_2.y)\
                            or (pyxel.tilemap(0).get(math.floor(box.x/7)+self.move_x,math.floor(box.y/7)+self.move_y)>=65):
                                box.move=0
                                self.move_count=0
                                break
                #箱を押していない時
                else:
                    box.move=0

    #ゲーム管理
    def update(self):
        #ここら辺は以前と同じ
        #カウントが8以上で移動する
        if self.move_count>0:
            self.move_count-=1
            self.player.x+=self.move_x
            self.player.y+=self.move_y
        #ここに追加
            for box in self.box:
                if box.move:
                    box.x+=self.move_x
                    box.y+=self.move_y  
```
実行結果
![pyxel-190318-091622.gif](https://qiita-image-store.s3.amazonaws.com/0/372537/83c04072-55e9-b2a7-32d5-f4c82026e9d6.gif)


##仕上げ
ゴール地点(青い箱)の上に箱を置いた時、箱の色を変化させます(正確には画像の表示を差し替える)。壁と同じようにタイルマップからデータを取得してゴール地点に置かれたかどうかを判断します。そして全ての箱がゴール地点に置かれたらゲームを終了します。

```box.py
    #画面描画
    def draw(self):
        #追加 一部命令は前回書いたものと同じなので注意
        self.clear_count=0
        #箱の描画 ゴール地点に置かれた箱は画像が変化する
        for box in self.box:
            if pyxel.tilemap(0).get(round(box.x/8),round(box.y/8))==33:
                pyxel.blt(box.x,box.y,0,8,16,8,8,0)
                self.clear_count+=1
            else:
                pyxel.blt(box.x,box.y,0,0,16,8,8,0)

        #全ての箱をゴール地点に置くとゲームクリア
        if self.clear_count==len(self.box):
            pyxel.rect(11,32,52,36,15)
            pyxel.text(11,32,"GAME CLEAR!",pyxel.frame_count % 16)

```
実行結果
![pyxel-190318-091826.gif](https://qiita-image-store.s3.amazonaws.com/0/372537/8bfb98c1-ef8c-f6ed-d882-ffdbe5a35094.gif)
クリアしてもゲームは止まりません:sweat_smile:

##終わり
倉庫番ゲームの最低限のことは完成しました。しかし実際のゲームはもっと色々なステージがありますし、そのステージを作って呼び出すのも簡単にしてみたいですね。Pythonはファイルの読み込みもできるようなのでもしかしたらこれでステージを作れるかも？

※一部記事を修正しました。しらかみゅさんありがとうございます！
[Pyxel公式マニュアル](https://github.com/kitao/pyxel/blob/master/README.ja.md)