型ヒント
これまで、変数や関数の引数には、どんな型の値でも気にせず代入してきました。多くのプログラミング言語では、変数に代入可能な値の型を制限しています。そうすることで想定外の値が代入されないようにしています。
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
を使えるようにします。そして、@dataclass
をclass
文の前に書きます。そしてclass
文の直下に、データ型付きでインスタンス変数を書きます。こうすると、コンストラクターなどのメソッドを、プログラム実行時に自動で生成してくれます。
実際にどのようなものか、プログラムの例を示します。
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.image
にpygame.Surface
オブジェクトを、self.rect
にpygame.Rect
オブジェクトを設定します。そうすると、スプライト グループの描画処理のときにimage
をrect
の位置に描画してくれます。
実際にどのようなものか、スクリーンショットとプログラムの例を示します。
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本開発しました。学んだ知識を応用して、さまざまなプログラムを書いたり、ゲームを作ったりしてください。