ソースに絡まるエスカルゴ

貧弱プログラマの外部記憶装置です。

【python】pygameでスプライトアニメーションと画像の変形、回転

 以前にpygameでタッチパネルを使う簡単なサンプルの記事を書きました。

 今回はもうちょっとpygameをちゃんと使ってみようと思い、タイトルにあるようにスプライトアニメーションと画像の変形、回転についての記事です。
 本記事で作った画像素材やサンプルコードをまとめて公開しておくので、気になったり素材が欲しい方は以下のリンクからダウンロードしてください。

 では、始めます。


1:スプライトアニメーション
 スプライトアニメーションとは画像を一枚ずつ読み込み、それを切り替えていくことでアニメーションさせる方法です。
 複数枚画像を用意する必要はありますが、パラパラ漫画のような要領で作っていくことができるので直感的でわかりやすいと思います。

 やり方としては、以下のようにスプライトクラスを作成して実現します。

import pygame

# スプライトアニメーションさせるクラス
class MySprite(pygame.sprite.Sprite):
    # コンストラクタ
    def __init__(self):
        super(MySprite, self).__init__()

        # 画像をリストで読み込み
        self.images = list()
        self.images.append(pygame.image.load('hoge0001.png'))
        self.images.append(pygame.image.load('hoge0002.png'))
        self.images.append(pygame.image.load('hoge0003.png'))
        # 以下略…で画像を全て読み込む

        # 画像はindexで管理すると楽
        self.index = 0
        self.image = self.images[self.index]
        self.rect = self.image.get_rect() # 描写範囲(必須)

    # 1フレーム事に実行される関数
    def update(self):
        # ループ処理
        if self.index >= len(self.images):
            self.index = 0

        self.image = self.images[self.index]
        self.index += 1

 基本的にはコンストラクタで画像を読み込み、update関数で画像を切り替えていくという感じです。

 例としてimageフォルダを作成してその中に「fish_0000.png」~「fish_0015.png」までの連番画像を入れたものを24fpsでアニメーションさせてみたサンプルが以下になります。

・sprite_test.py

# -*- coding: utf-8 -*-
import pygame

WINDOW_SIZE = WIDTH, HEIGHT = 600, 400 # ウインドウサイズ
BACKGROUND_COLOR = (0,0,0) # 背景色(黒)
FPS = 24 # フレームレート

# スプライトアニメーションさせるクラス
class FishSprite(pygame.sprite.Sprite):
    # コンストラクタ
    def __init__(self):
        super(FishSprite, self).__init__()

        # 画像をリストで読み込み
        self.images = list()
        for i in range(0, 15):
            number_str = str(i)
            if len(number_str) == 1:
                number_str = '0' + number_str
            self.images.append(pygame.image.load('./image/fish_00' + number_str + '.png'))

        self.index = 0
        self.image = self.images[self.index]
        self.rect = self.image.get_rect() # 描写範囲(必須)

    # 1フレーム事に実行される関数
    def update(self):
        if self.index >= len(self.images):
            self.index = 0
        self.image = self.images[self.index]
        self.index += 1

def main():
    pygame.init()
    screen = pygame.display.set_mode(WINDOW_SIZE) # ウインドウサイズの設定

    # スプライトのオブジェクトを作ってグループ化
    fish = FishSprite()
    my_group = pygame.sprite.Group(fish)

    screen.fill(BACKGROUND_COLOR) # 背景色を設定
    clock = pygame.time.Clock()   # クロックを開始

    while True:
        for event in pygame.event.get():
            # ESCキーが押されたら終了
            if (event.type == pygame.KEYDOWN) and (event.key == pygame.K_ESCAPE):
                pygame.quit()
                quit()

        # 背景色で塗りつぶし
        screen.fill(BACKGROUND_COLOR)

        # 画像を更新して描写
        my_group.update()
        my_group.draw(screen)

        # 表示全体を更新
        pygame.display.update()
        clock.tick(FPS) # フレームレート分待機

if __name__ == '__main__':
    main()

 実行すると以下のように魚っぽいシルエットがお尻を振り振りしているアニメーションが表示されます。「ESCキー」を押すと終了します。
f:id:rikoubou:20200710164347g:plain


2:画像の変形、回転
 スプライトアニメーションができたので、今度はこの画像を拡大縮小して変形させたり、回転させたりしてみます。

 先ほどのスプライトアニメーションのクラスに、以下のようにchange_image_scale関数とrotate_center_image関数を追加すればいけます。

# スプライトアニメーションさせるクラス
class FishSprite(pygame.sprite.Sprite):
    # コンストラクタ
    def __init__(self, width_rate, height_rate, position):
        super(FishSprite, self).__init__()

        # 画像をリストで読み込み
        self.images = list()
        # self.images.append(pygame.image.load('./image/test.png')) # テスト用の画像
        for i in range(0, 15):
            number_str = str(i)
            if len(number_str) == 1:
                number_str = '0' + number_str
            self.images.append(pygame.image.load('./image/fish_00' + number_str + '.png'))

        self.index = 0
        self.image = self.images[self.index]
        self.rect = self.image.get_rect() # 描写範囲(必須)

        self.width_rate = width_rate
        self.height_rate = height_rate
        self.image_center_position = position

        self.image_angle = 0

    # 1フレーム事に実行される関数
    def update(self):
        if self.index >= len(self.images):
            self.index = 0
        self.image = self.images[self.index]
        self.index += 1

        self.change_image_scale()  # 画像サイズを変更
        self.rotate_center_image() # 画像を回転

    # 画像のサイズを変更する関数
    def change_image_scale(self):
        x_size = self.image.get_width()  * self.width_rate
        y_size = self.image.get_height() * self.height_rate
        self.image = pygame.transform.scale(self.image, (int(x_size), int(y_size)))

    # 画像の中心で回転させる関数
    def rotate_center_image(self):
        # 画像の傾きを設定
        self.image_angle -= 10
        if self.image_angle <= -360:
            self.image_angle = 0

        # 画像を回転
        rot_image = pygame.transform.rotate(self.image, self.image_angle)
        rot_rect = rot_image.get_rect()
        rot_rect.center = self.image_center_position # 中心位置を設定(移動)

        # 結果を格納
        self.image = rot_image
        self.rect = rot_rect

 コンストラクタの引数が色々と増えていますが、画像の縦横の比率と画像を表示させる位置を設定できるようにしてあります。

 実際のサンプルコードは以下になります。

・transform_rotate_test.py

# -*- coding: utf-8 -*-
import pygame

WINDOW_SIZE = WIDTH, HEIGHT = 600, 400 # ウインドウサイズ
BACKGROUND_COLOR = (0,0,0) # 背景色(黒)
FPS = 24 # フレームレート

# スプライトアニメーションさせるクラス
class FishSprite(pygame.sprite.Sprite):
    # コンストラクタ
    def __init__(self, width_rate, height_rate, position):
        super(FishSprite, self).__init__()

        # 画像をリストで読み込み
        self.images = list()
        # self.images.append(pygame.image.load('./image/test.png')) # テスト用の画像
        for i in range(0, 15):
            number_str = str(i)
            if len(number_str) == 1:
                number_str = '0' + number_str
            self.images.append(pygame.image.load('./image/fish_00' + number_str + '.png'))

        self.index = 0
        self.image = self.images[self.index]
        self.rect = self.image.get_rect() # 描写範囲(必須)

        self.width_rate = width_rate
        self.height_rate = height_rate
        self.image_center_position = position

        self.image_angle = 0

    # 1フレーム事に実行される関数
    def update(self):
        if self.index >= len(self.images):
            self.index = 0
        self.image = self.images[self.index]
        self.index += 1

        self.change_image_scale()  # 画像サイズを変更
        self.rotate_center_image() # 画像を回転

    # 画像のサイズを変更する関数
    def change_image_scale(self):
        x_size = self.image.get_width()  * self.width_rate
        y_size = self.image.get_height() * self.height_rate
        self.image = pygame.transform.scale(self.image, (int(x_size), int(y_size)))

    # 画像の中心で回転させる関数
    def rotate_center_image(self):
        # 画像の傾きを設定
        self.image_angle -= 10
        if self.image_angle <= -360:
            self.image_angle = 0

        # 画像を回転
        rot_image = pygame.transform.rotate(self.image, self.image_angle)
        rot_rect = rot_image.get_rect()
        rot_rect.center = self.image_center_position # 中心位置を設定(移動)

        # 結果を格納
        self.image = rot_image
        self.rect = rot_rect

def main():
    pygame.init()
    screen = pygame.display.set_mode(WINDOW_SIZE) # ウインドウサイズの設定

    # スプライトのオブジェクトを作ってグループ化
    fish1 = FishSprite(0.5, 0.1, (100, 100))
    fish2 = FishSprite(0.1, 0.6, (300, 150))
    fish3 = FishSprite(0.3, 0.3, (500, 250))

    my_group = pygame.sprite.Group(fish1)
    my_group.add(fish2)
    my_group.add(fish3)

    screen.fill(BACKGROUND_COLOR) # 背景色を設定
    clock = pygame.time.Clock()   # クロックを開始

    while True:
        for event in pygame.event.get():
            # ESCキーが押されたら終了
            if (event.type == pygame.KEYDOWN) and (event.key == pygame.K_ESCAPE):
                pygame.quit()
                quit()

        # 背景色で塗りつぶし
        screen.fill(BACKGROUND_COLOR)

        # 画像を更新して描写
        my_group.update()
        my_group.draw(screen)

        # 表示全体を更新
        pygame.display.update()
        clock.tick(FPS) # フレームレート分待機

if __name__ == '__main__':
    main()

 実行すると以下のように縦横比や大きさ、表示位置が違うものが3つ表示されて回転します。
f:id:rikoubou:20200710164615g:plain

 画像の中心で回転しているのがわかりにくいですが、実際にちゃんと画像の中心を軸として回転しています。


 以上がpygameでスプライトアニメーションと画像の変形、回転する方法です。

 スプライトアニメーションをさせているキャラクターをキー入力で動かす、ということもこの方法を応用すればできるようになると思います。

 個人的にゲームを作るという目的はないのですが、ちょっとした面白いことには色々応用できると思うのでこれからもいじっていきたいです。


・参考資料