SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

レトロ風ゲームを作って学ぶPython入門

【作って学ぶPython】クラスとスプライトを学んでメンテナンスしやすいコードを書こう!

レトロ風ゲームを作って学ぶPython入門 第6回

  • X ポスト
  • このエントリーをはてなブックマークに追加

型ヒント

 これまで、変数や関数の引数には、どんな型の値でも気にせず代入してきました。多くのプログラミング言語では、変数に代入可能な値の型を制限しています。そうすることで想定外の値が代入されないようにしています。

 Pythonでも、変数や関数に型の情報を加えることができます。型ヒントと呼ばれるもので、変数の宣言時に変数: 型と書きます。また、関数の戻り値を -> 型と書きます。

 プログラムの例を示します。

型ヒントの例
# 整数、浮動小数点数、テキスト、真偽値、Noneの型ヒント
num1: int = 1
num2: float = 1.0
text: str = "cat"
is_success: bool = True
no_data: None = None

# 複数の値を持つ型の型ヒント
data1: list[int] = [10, 20, 30]     # 整数のみ
data2: list[int|str|None] = ["ゴブリン", 10, None]  # 整数かテキストかNone

data3: tuple[int, int, int] = (10, 20, 30)  # タプルは全ての値の型を指定
data4: dict[str, int] = {"hp": 10, "mp": 5} # 辞書はキーと値の型を指定

# 関数の型ヒント
# 戻り値は「 -> 型」と書く
def add(arg1: int, arg2: int) -> int:
    res = arg1 + arg2
    return res

 型ヒントを書くことで、プログラムに想定外の値が入っていないかを、プログラムの実行前に確かめることができます。

 VSCodeのPython拡張機能を入れている状態なら、型ヒントをもとに、開いているプログラムを自動で確認してくれます。

 また、型ヒントを書いたプログラムを実行前にまとめて確認するときは、mypyというパッケージを使うことが多いです。

  • mypy · PyPI
    • https://pypi.org/project/mypy/
    • https://www.mypy-lang.org/

 このパッケージをインストールすると、ターミナルでmypyコマンドが使えるようになります。mypy ファイル名なら、そのファイルを確認します。mypy .ならカレント ディレクトリ内のファイルを一括で確認してくれます。

データ クラス

 データ クラスを使えば、データを保持する目的のクラスを簡単に作れます。

 まず、from dataclasses import dataclassで、dataclassを使えるようにします。そして、@dataclassclass文の前に書きます。そしてclass文の直下に、データ型付きでインスタンス変数を書きます。こうすると、コンストラクターなどのメソッドを、プログラム実行時に自動で生成してくれます。

 実際にどのようなものか、プログラムの例を示します。

dataclass_1.py
from dataclasses import dataclass

@dataclass  # データ クラス
class Chara:
    name: str       # インスタンス変数
    hp: int = 10    # インスタンス変数

hero = Chara("勇者")    # インスタンスを生成
print(hero)    # 「Chara(name='勇者', hp=10)」と表示

hero2 = Chara("戦士", 20)   # インスタンスを生成
print(hero2)   # 「Chara(name='戦士', hp=20)」と表示

hero3 = Chara(name="賢者", hp = 5)  # インスタンスを生成
print(hero3)   # 「Chara(name='賢者', hp=5)」と表示

 クラスの記述量が大幅に減りました。データ クラスを使えば、非常に短くプログラムを書けます。ゲームのデータなどを作るときに活用するとよいです。

Pygameのスプライト

 Pygameではスプライトを利用できます。スプライトは、ゲーム開発でよく出てくる、複数の小さな画像を高速に描画するための仕組みです。

 Pygameのスプライトでは、複数のスプライトをグループ化して、一括して描画できます。Pygameのスプライトは、pygame.sprite.Spriteを継承したクラスとして作ります。

 継承したスプライトでは、コンストラクターとupdate()メソッドを実装します。そして、pygame.sprite.RenderUpdates()で生成したスプライト グループに登録します。

 継承したスプライトでは、インスタンス変数self.imagepygame.Surfaceオブジェクトを、self.rectpygame.Rectオブジェクトを設定します。そうすると、スプライト グループの描画処理のときにimagerectの位置に描画してくれます。

 実際にどのようなものか、スクリーンショットとプログラムの例を示します。

スプライト
import pygame

# キャラクター クラス
class Chara(pygame.sprite.Sprite):
    # コンストラクター
    def __init__(self, images):
        super().__init__()
        self.images = images    # 画像のリスト
        self.image = images[0]  # 0番目を選ぶ
        self.rect = self.image.get_rect()   # 画像の四角形

    # 更新
    def update(self):
        time = pygame.time.get_ticks()  # 経過時間
        i = time // 500 % 2             # 0, 1のいずれか
        self.image = self.images[i]     # 画像を変更

pygame.init()       # Pygameを初期化
screen = pygame.display.set_mode((800, 600))    # 画面作成
running = True      # 実行継続フラグ

# スプライト グループの準備と画像の読み込み
sprites = pygame.sprite.RenderUpdates()     # 描画更新用スプライト グループ
image0 = pygame.image.load("chara0.png")    # 画像読み込み
image1 = pygame.image.load("chara1.png")    # 画像読み込み

# スプライトの作成と準備
for y in range(8):
    for x in range(10):
        chara = Chara([image0, image1]) # インスタンスを生成
        chara.add(sprites)              # グループに追加
        chara.rect.x = x * (64 + 16)    # X位置
        chara.rect.y = y * (64 + 10)    # Y位置

while running:
    for event in pygame.event.get():    # イベント
        if event.type == pygame.QUIT:
            running = False     # 終了

    pygame.display.update()     # 画面を更新
    screen.fill(pygame.Color(0, 0, 0))  # 画面を塗りつぶす
    sprites.update()        # スプライト更新
    sprites.draw(screen)    # スプライト描画
    pygame.display.flip()   # 画面フリップ
pygame.quit()

 冒頭では、キャラクター クラスCharaを定義しています。コンストラクターではSurfaceのリストを受け取り、初期化をおこないます。更新をおこなうupdate()関数では、500ミリ秒に一度、2枚のSurfaceを入れ替えています。

 中盤ではまず、sprites = pygame.sprite.RenderUpdates()で、描画更新用のスプライト グループspritesを作ります。

 そして、80個のスプライトを作り、スプライト グループに登録します。chara = Chara(~)でキャラクター クラスを作ったあと、chara.add(sprites)の処理でspritesに登録します。

 終盤は通常のPygameのループです。sprites.update()sprites.draw(screen)の2つの処理で、80個のスプライトをまとめて更新して描画します。

 pygame.sprite.Spriteクラスには、update()以外にもメソッドがあります。これらのメソッドを表で示します。

スプライトのメソッド
メソッド 説明
update() 更新処理、オーバーライド用
add(groups) スプライトをグループに追加
remove(groups) スプライトをグループから削除
kill() スプライトを全てのグループから削除
alive() 真偽値を返す。いずれかのグループに属しているか
groups() 所属しているグループのリストを返す

 スプライトの機能を使うことで、キャラクターのアニメーションなどを手軽に管理できるようになります。

 今回のレトロ風RPGでは、最大で2体しかキャラクターを画面に表示しませんでした。アクションゲームのように多くのキャラクターを画面に表示するときは、スプライトを使ってプログラムを書くとよいでしょう。

まとめ

 今回はクラスとスプライトについて学びました。

 複雑なプログラムを書くときは、クラスを使い、データと処理をまとめて管理していくことが多いです。Pythonのプログラムはクラスなしでも書けますが、クラスを使った方がメンテナンスをしやすいプログラムになります。

 より大きなゲームを作るときには、こうしたクラスの機能を使うとよいです。

 駆け足でしたが、Pythonを学びながら、仕様を削ぎ落とした小さなゲームを1本開発しました。学んだ知識を応用して、さまざまなプログラムを書いたり、ゲームを作ったりしてください。

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
レトロ風ゲームを作って学ぶPython入門連載記事一覧

もっと読む

この記事の著者

柳井 政和(ヤナイ マサカズ)

クロノス・クラウン合同会社 代表社員http://crocro.com/オンラインソフトを多数公開。プログラムを書いたり、ゲームを作ったり、記事を執筆したり、マンガを描いたり、小説を書いたりしています。「めもりーくりーなー」でオンラインソフト大賞に入賞。最近は、小説家デビューして小説も書いています(『裏切りのプログラム』他)。面白いことなら何でもOKのさすらいの企画屋です。 

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/19460 2024/07/17 11:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング