Pygameのスプライト機能を用いて、複数の画像オブジェクトを効率よく管理する方法とソースコードを解説します。
【1】スプライトの基本的な使い方
Pygameのスプライトは、ゲーム内のグラフィカルな要素(プレイヤーや敵など)を管理するための非常に便利な機能です。スプライトを使用することで、例えば以下のように複数のゲームオブジェクトの描画、更新、衝突判定などを効率的に管理できます。
【今回使用する画像】
Pygameでスプライトを扱うには、以下のようにpygame.sprite.Spriteクラスを継承します。
import pygame class Player(pygame.sprite.Sprite): def __init__(self, image_path, pos): super().__init__() self.image = pygame.image.load(image_path).convert_alpha() self.rect = self.image.get_rect(topleft=pos) # update()`メソッドを使用して、オブジェクトの状態を更新します。 # 通常、位置の変更やアニメーションの更新などを行います。 def update(self): # ここに更新ロジックを追加 # オブジェクトの画像を画面上に描画 screen.blit(self.image, self.rect)
動画解説版
【1-1】サンプルコード①
サンプルプログラムのソースコードです。5人の女の子が動き回ります。
【1-2】サンプルコード①の解説
このコードは、Pygameを使ってスプライト(画像)を画面上で動かすシンプルなプログラムです。以下に各部分の詳細な解説を行います。
インポートと初期設定
# -*- coding: utf-8 -*-
import sys
import pygame
from pygame.locals import *
- sysモジュールをインポートしています。
pygame
モジュールとそのローカル定数をインポートしています。
画面サイズと画像ファイルパスの設定
SCREEN = Rect(0, 0, 600, 400) # 画面サイズ
GIRL1_IMG_PATH = "/Users/github/sample/python/pygame/sprite/girl1.png"
GIRL2_IMG_PATH = "/Users/github/sample/python/pygame/sprite/girl2.png"
GIRL3_IMG_PATH = "/Users/github/sample/python/pygame/sprite/girl3.png"
GIRL4_IMG_PATH = "/Users/github/sample/python/pygame/sprite/girl4.png"
GIRL5_IMG_PATH = "/Users/github/sample/python/pygame/sprite/girl5.png"
SCREEN
は画面のサイズを設定するための矩形オブジェクトです。- 各スプライト画像のファイルパスを定義しています。
クラスの定義と初期化(スプライト)
Pygameを使ってスプライト(画像オブジェクト)を管理するクラス Girl
を定義しています。
class Girl(pygame.sprite.Sprite):
def __init__(self, filepath, pos, vxy, angle=0):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(filepath).convert_alpha()
if angle != 0:
self.image = pygame.transform.rotate(self.image, angle)
Girl
クラスはpygame.sprite.Sprite
を継承しています。これにより、Pygameのスプライト機能を利用できます。__init__
メソッドは、スプライトの初期化を行います。filepath
は画像ファイルのパスです。pos
はスプライトの初期位置(x, y)です。vxy
はスプライトの移動速度(vx, vy)です。angle
はスプライトの回転角度です(デフォルトは0)。
self.image
に画像を読み込み、必要に応じて回転させます。
矩形オブジェクトの作成
x, y = pos
vx, vy = vxy
w = self.image.get_width()
h = self.image.get_height()
self.rect = Rect(x, y, w, h)
pos
から位置を、vxy
から速度を取得します。- 画像の幅 (
w
) と高さ (h
) を取得し、Rect
オブジェクトを作成します。Rect
はスプライトの位置とサイズを管理します。
移動速度と角度の設定
self.vx = vx
self.vy = vy
self.angle = angle
- スプライトの移動速度 (
vx
,vy
) と回転角度 (angle
) を設定します。
更新メソッド
def update(self):
self.rect.move_ip(self.vx, self.vy)
if self.rect.left < 0 or self.rect.right > SCREEN.width:
self.vx = -self.vx
if self.rect.top < 0 or self.rect.bottom > SCREEN.height:
self.vy = -self.vy
self.rect = self.rect.clamp(SCREEN)
update
メソッドはスプライトの位置を更新します。move_ip
メソッドでスプライトを移動させます。- スプライトが画面の端に達した場合、速度の符号を反転させて跳ね返ります。
clamp
メソッドでスプライトが画面外に出ないようにします。
描画メソッド
def draw(self, screen):
screen.blit(self.image, self.rect)
draw
メソッドはスプライトを画面に描画します。screen.blit
メソッドで画像を指定された位置 (self.rect
) に描画します。
メイン関数
def main():
pygame.init()
screen = pygame.display.set_mode(SCREEN.size)
# スプライトの作成
girl1 = Girl(GIRL1_IMG_PATH,(200, 200), (2, 0), 0)
girl2 = Girl(GIRL2_IMG_PATH,(200, 200), (0, 2), -20)
girl3 = Girl(GIRL3_IMG_PATH,(200, 200), (2, 3), 0)
girl4 = Girl(GIRL4_IMG_PATH,(200, 200), (1, 2), 20)
girl5 = Girl(GIRL5_IMG_PATH,(200, 200), (2, 1), 0)
clock = pygame.time.Clock()
running = True
while running:
clock.tick(30)
screen.fill((0, 20, 0))
# 各スプライトの更新
girl1.update()
girl2.update()
girl3.update()
girl4.update()
girl5.update()
# 各スプライトの描画
girl1.draw(screen)
girl2.draw(screen)
girl3.draw(screen)
girl4.draw(screen)
girl5.draw(screen)
pygame.display.update()
# イベント処理
for event in pygame.event.get():
if event.type == QUIT:
running = False
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
running = False
if __name__ == "__main__":
main()
main
関数でPygameを初期化し、画面を設定します。Girl
クラスのインスタンスを作成し、それぞれ異なる画像、位置、速度、角度を設定します。- メインループ内でフレームレートを設定し、画面を更新します。
update
メソッドで各スプライトの位置を更新し、draw
メソッドで画面に描画します。- イベント処理でウィンドウの閉じるボタンやEscキーが押された場合にループを終了します。
【2】スプライトのグループ化
スプライトをグループ化すると、複数のスプライトを効率的に管理できます。
グループ化する方法はいくつかありますが、今回はpygame.sprite.RenderUpdates()
を使う方法を解説します。
pygame.sprite.RenderUpdates()
は、Pygameでスプライトの描画を効率的に管理するためのクラスです。このクラスは、スプライトの更新と描画を行う際に、変更された部分だけを再描画することでパフォーマンスを向上させます。
基本的な使い方
- スプライトグループの作成
group = pygame.sprite.RenderUpdates()
RenderUpdates
クラスのインスタンスを作成します。このグループにスプライトを追加することで、効率的な描画が可能になります。
- スプライトの追加
group.add(sprite)
- 作成したスプライトをグループに追加します。複数のスプライトを一度に追加することもできます。
- スプライトの更新
group.update()
- グループ内のすべてのスプライトの位置や状態を更新します。各スプライトの
update
メソッドが呼び出されます。
- グループ内のすべてのスプライトの位置や状態を更新します。各スプライトの
- スプライトの描画
dirty_rects = group.draw(screen) pygame.display.update(dirty_rects)
- グループ内のすべてのスプライトを画面に描画し、変更された領域(矩形)のリストを返します。このリストを使って、画面の更新を効率的に行います。 この手法は、画面更新時、全体を描き換えずにスプライトが更新された一部分だを描き換えることでパフォーマンスを向上させます。全体を書き換えたい場合は「pygame.display.update()」とします(dirty_rectsを与えない)。
【2-1】サンプルコード②
サンプルコード①をpygame.sprite.RenderUpdates()
でスプライトをグループ化すると以下のようになります。
【2-2】サンプルコード②の解説
サンプルコード①と異なる部分のみ解説します。
スプライトグループの作成と追加
group = pygame.sprite.RenderUpdates()
group.add(girl1)
group.add(girl2)
group.add(girl3)
group.add(girl4)
group.add(girl5)
pygame.sprite.RenderUpdates()
:スプライトを管理するグループを作成します。group.add(...)
:作成したスプライトをグループに追加します。
メインループ
running = True
while running:
clock.tick(30)
screen.fill((0, 20, 0))
group.update()
# スプライトグループを描画
dirty_rects = group.draw(screen)
# 画面更新
pygame.display.update(dirty_rects)
group.update()
:スプライトグループの位置を更新します。group.draw(screen)
:スプライトグループを画面に描画します。変数dirty_rectsには、変更された領域(矩形)のリストが格納されます。- リスト
dirty_rects
をpygame.display.update
に渡し、変更された部分だけを再描画することで、画面の更新を効率的に行います。
【3-1】スプライトのグループ化 その2
Pygameでスプライトグループ化するとき、pygame.sprite.Sprite.__init__(self, self.containers)
を使う方法があります。
これは、Pygameのスプライトクラスの初期化メソッドに、スプライトが所属するグループを指定するためのものです。これにより、スプライトを作成する際に自動的に指定されたグループに追加されます
この方法のメリットは以下のとおりです。
- コードの簡潔化:スプライトを作成するたびにgroup.add(…)`でグループに追加する必要がなくなります。
- 一貫性の確保:スプライトが常に指定されたグループに追加されるため、管理が容易になります。
【3-1】サンプルコード③
サンプルコード②をpygame.sprite.Sprite.__init__(self, self.containers)
でスプライトをグループ化すると以下のようになります。
【3-2】サンプルコード③の解説
サンプルコード②との変更箇所を解説します。
1. スプライトの初期化
Girlクラスの初期メソッドの以下の箇所です。
pygame.sprite.Sprite.__init__(self, self.containers)
pygame.sprite.Sprite.__init__(self, self.containers)
は、スプライトクラスの親クラスであるpygame.sprite.Sprite
の初期化メソッドを呼び出します。self.containers
は、スプライトが所属するグループを指定するための引数です。この引数には、スプライトグループ(例えばpygame.sprite.Group
やpygame.sprite.RenderUpdates
)が渡されます。
2. スプライトグループの設定
# スプライトグループの作成
girl_group = pygame.sprite.RenderUpdates()
# Girlクラスにスプライトグループを割り当てる
Girl.containers = girl_group
girl_group = pygame.sprite.RenderUpdates()
:スプライトグループを作成します。Girl.containers = girl_group
:Girl
クラスのcontainers
属性に、作成したスプライトグループ(girl_group)を割り当てます。これにより、Girl
クラスのインスタンスが作成されると、自動的にこのグループに追加されます。
3. スプライトの作成と追加
girl1 = Girl(GIRL1_IMG_PATH, (200, 200), (2, 0), 0)
girl1 = Girl(GIRL1_IMG_PATH, (200, 200), (2, 0), 0)
:Girl
クラスのインスタンスを作成します。このとき、__init__
メソッド内でpygame.sprite.Sprite.__init__(self, self.containers)
が呼び出され、girl1
は自動的にgirl_group
に追加されます。
Pygameでスプライト同士の衝突判定と跳ね返りを実装する方法をソースコード付きで詳しく解説します。
【4】スプライト同士の衝突判定と跳ね返り
今のままだと、画像オブジェクト同士の衝突時、そのまますり抜けてしまいます。
Pygameでは、self.rect.colliderect
を使ってスプライト同士の衝突判定と跳ね返りを実装できます。
self.rect.colliderect(other_rect)
は、2つの矩形(Rect
オブジェクト)が衝突しているかどうかを判定するメソッドです。
衝突している場合は True
を返し、そうでない場合は False
を返します。
【4-1】サンプルコード④
5つの画像オブジェクトが衝突したら跳ね返るサンプルコードです。
【4-2】サンプルコード④の解説
上記コードのうち、衝突判定と跳ね返りを実装している部分は以下のとおりです。
def update(self):
self.rect.move_ip(self.vx, self.vy)
if self.rect.left < 0 or self.rect.right > SCREEN.width:
self.vx = -self.vx
if self.rect.top < 0 or self.rect.bottom > SCREEN.height:
self.vy = -self.vy
self.rect = self.rect.clamp(SCREEN)
for sprite in self.containers:
if sprite != self and self.rect.colliderect(sprite.rect):
self.vx = -self.vx
self.vy = -self.vy
update
メソッドでは、スプライトの位置を更新し、画面の端に達した場合や他のスプライトと衝突した場合に速度を反転させます。つまり、スプライトは画面の端や他のスプライトに衝突すると跳ね返る動きをします。self.rect.move_ip(self.vx, self.vy)
:スプライトの位置を現在の速度(vx
,vy
)に基づいて更新します。if self.rect.left < 0 or self.rect.right > SCREEN.width:
- スプライトが画面の左端または右端に達した場合、
vx
の符号を反転させて反射させます。
- スプライトが画面の左端または右端に達した場合、
if self.rect.top < 0 or self.rect.bottom > SCREEN.height:
- スプライトが画面の上端または下端に達した場合、
vy
の符号を反転させて反射させます。
- スプライトが画面の上端または下端に達した場合、
self.rect = self.rect.clamp(SCREEN)
:- スプライトの位置を画面内に制限します。
- 他のスプライトとの衝突処理:
for sprite in self.containers:
で同じコンテナ内の他のスプライトをループします。つまり、self.containers
内の他のスプライトをループし、自分自身以外のスプライトと衝突しているかをself.rect.colliderect(sprite.rect)
で判定します。if sprite != self and self.rect.colliderect(sprite.rect):
自分自身以外のスプライトと衝突した場合、vx
とvy
の符号を反転させて反射させます。
関連ページ
Pygameの使い方については以下ページで解説しています。
Python全般については以下ページで解説しています。
コメント
11行目のところが構文が違うと返されます。
※匿名様
コメントありがとうございます。
Python3だとエラーが出るようなので修正いたしました。
変更ありがとうございます。
変更したあとのものを実行すると下のエラーが起きます。
‘Sprite’ object has no attribute ‘img’
これはpyhotn3にはないのでしょうか?
※匿名様
コメントありがとうございます。
当方の環境でも同様のエラーが出ましたので修正致しました。