ゲーム開発で使用したテクニック～歩行アニメーション～

個人でゲーム開発をしている中で、実装に苦労した部分が多かったので、テクニックとして紹介していこうと思います。

今回は「歩行アニメーション」について書きます。

## 歩行画像の準備
RPGツクール系で使用される32×32を1升とした12升で1枚の画像を用意しました。
正面、右向き、左向き、後ろ向きがそれぞれ3パターンあるものです。
## 画像の読み込み
```python
img_name_p1 = self.get_image_path(self.PLAYER_FILENAME_1)
self.P_IMG_1 = pg3.image.load(img_name_p1)
self.P_IMG_1 = pg3.transform.scale(self.P_IMG_1, self.IMG_SCALE)
```
まずは画像を読み込みます。
この時点では1枚の画像として読み込んでいます。
RPGツクール系だとサイズの異なるものがあります。
取り込むときのサイズ感は「self.IMG_SCALE」で調整しました。
今回は「96×128」では小さすぎたので、1.75倍の「168×224」に拡大しました。
## 画像の切り取り
```python
P_IMG_D_0 = (0,0,56,56)     # 下向き左
P_IMG_D_1 = (56,0,56,56)    # 下向き真ん中
P_IMG_D_2 = (112,0,56,56)   # 下向き右
P_IMG_U_0 = (0,168,56,56)   # 上向き左
P_IMG_U_1 = (56,168,56,56)  # 上向き真ん中
P_IMG_U_2 = (112,168,56,56) # 上向き右
P_IMG_L_0 = (0,56,56,56)    # 左向き左
P_IMG_L_1 = (56,56,56,56)   # 左向き真ん中
P_IMG_L_2 = (112,56,56,56)  # 左向き右
P_IMG_R_0 = (0,112,56,56)   # 右向き左
P_IMG_R_1 = (56,112,56,56)  # 右向き真ん中
P_IMG_R_2 = (112,112,56,56) # 右向き右
```
この画像を使用するとき、画像を切り取って表示します。
予め切り取り位置を定義しました。
最初のサイズ感に合わせて設定しないと、意図しないところで切り取られてしまいます。
今回は元の画像を1.75倍しているので、1升のサイズが「32×1.75=56」となります。
```python
def get_change_direction_img(self, x, y):
    """
    アイコンの向きが変わったときに対応した画像のトリミング位置を返す
    """
    img_obj = (self.P_IMG_D_0, self.P_IMG_D_1, self.P_IMG_D_2)
    icon_x,icon_y = self.ICON_POS
    direction = "D"
    # 上を向いた
    if icon_x == x and icon_y == y + 1:
        img_obj = (self.P_IMG_U_0, self.P_IMG_U_1, self.P_IMG_U_2)
        direction = "U"
    # 下を向いた
    elif icon_x == x and icon_y == y - 1:
        img_obj = (self.P_IMG_D_0, self.P_IMG_D_1, self.P_IMG_D_2)
        direction = "D"
    # 左を向いた
    elif icon_x == x + 1 and icon_y == y:
        img_obj = (self.P_IMG_L_0, self.P_IMG_L_1, self.P_IMG_L_2)
        direction = "L"
    # 右を向いた
    elif icon_x == x - 1 and icon_y == y:
        img_obj = (self.P_IMG_R_0, self.P_IMG_R_1, self.P_IMG_R_2)
        direction = "R"
    self.DIRECTION = direction
    return img_obj,direction
```
プレイヤーの向きが変わった時に、対応した切り取り位置を返す関数です。
「self.ICON_POS」がプレイヤーの現在地です。
プレイヤーの現在地と与えられたXY座標から、どの向きになったかを判定しています。
## アニメーション
```python
def animation(self, direction, imgobj, Player):
    """
    歩行アニメーションを実行する処理
    """
    icon_x,icon_y = Player.ICON_POS
    pos0,pos1,pos2 = imgobj
    triming = None

    # 4歩分繰り返す
    for i in range(0, 4):
        if direction == "U":
            icon_y -= 0.25
        elif direction == "D":
            icon_y += 0.25
        elif direction == "L":
            icon_x -= 0.25
        elif direction == "R":
            icon_x += 0.25

        if i == 0:
            triming = pos0
        elif i == 1 or i == 3:
            triming = pos1
        elif i == 2:
            triming = pos2

        # 移動前のプレイヤーを消去する
        self.delete_icon(Player)

        # 移動したプレイヤーを描画する
        Player.set_position((icon_x, icon_y))
        self.screen.blit(Player.P_IMG, (icon_x, icon_y), triming)
        
        pg3.display.update()
        pg3.time.delay(Player.WALK_DELAY)
```
実際にアニメーションさせる関数です。
引数は前述の「get_change_direction_img」の戻り値を使用しています。
1升の分の移動を4分割したかったので、for文で4回ループさせています。
1升分なので、「1/4=0.25」が1ループあたりの移動量ということになります。
ループ回数によって、どの切り取り位置を使用するか変わってくるので、切り取り位置をtrimingに代入しています。
ただ描画しただけでは、画像が連続して表示されてしまいますので、「delete_icon」で移動のたびに元の場所からプレイヤーを削除します。
削除といっても描画したものを消すことはできないので、「delete_icon」ではプレイヤーの上から背景色で上書きしています。
「set_position」はプレイヤーの位置情報を更新する関数です。「Player.ICON_POS」が移動後の座標に更新されます。
blit関数の第3引数に切り取り位置を指定します。「Player.P_IMG」は12升分ありますが、切り取り位置を指定することで、そこだけ切り抜いて表示してくれます。
「Player.WALK_DELAY」で歩くスピードを調整しています。小さくすれば速くなりますし、大きくすれば遅くなります。

「歩行アニメーション」に関しては以上となります。

## リリース済みのゲームはこちら
・PC向け無料ゲーム
『LAbyrinth』（2Dの迷路探索ゲーム）
●Freem!
https://www.freem.ne.jp/win/game/33791
●Unityroom
https://unityroom.com/games/2025-labyrinth-isukaka