4 シューティングゲーム

シューティングゲームを作成します(shooting.py)。
4.1 土台
シューティングゲームを作成します。 画面、プレイヤー、敵を作成します。
import os
os.environ['SDL_VIDEO_WINDOW_POS'] = "100,100"
import pgzrun
from pgzero.builtins import *
import random
from dataclasses import dataclass
# 画面サイズ
WIDTH = 400
HEIGHT = 600
# プレイヤーと敵の初期位置
player = Actor('alien', (200, 500))
enemy = Actor('cherry', (200, 100))
更新処理ではキーボードでプレイヤーを左右に動かせるようにします。 関数にまとめておきます。
def update_player():
# プレイヤーの移動
if keyboard.left and player.x > 0:
player.x -= 5
if keyboard.right and player.x < WIDTH:
player.x += 5
敵は少しずつ下におりてきて、画面下に消えたら上に戻るようにします。関数にまとめておきます。
def update_enemy():
# 敵の移動
enemy.y += 2
if enemy.y > HEIGHT: # 画面の下に消えたら上に戻る
enemy.y = 0
enemy.x = random.randint(0, WIDTH)
update関数で呼び出します。
# ゲームの更新処理
def update():
update_player()
update_enemy()
描画処理では画面をクリアし、playerとenemyを描画します。
# ゲームの描画処理
def draw():
screen.clear()
player.draw()
enemy.draw()
pgzrun.go()
4.2 ゲームオーバー処理の追加
ゲームオーバーと得点を管理するためGameクラスを作ります。
# ゲーム管理
@dataclass
class Game:
game_over:bool
score:int
初期化します。
game = Game(False, 0)
updateの最初で、game_overになったら何もしないようにします。
if game.game_over:
return # ゲームオーバー時は何もしない
また、updateの最後で衝突したらgame_overにします。
# プレイヤーと敵の衝突判定
if player.colliderect(enemy):
game.game_over = True
ゲームオーバーの画面を出す関数を作成します。
# ゲームオーバー画面
def draw_game_over():
pos = (WIDTH // 2, HEIGHT // 2)
screen.draw.text("GAME OVER", center=pos, fontsize=60, color="red")
pos = (WIDTH // 2, HEIGHT // 2 + 50)
screen.draw.text(f"SCORE: {game.score}", center=pos, fontsize=40, color="white")
draw関数の中でゲームオーバーの画面を出すようにします。
def draw():
screen.clear()
if game.game_over:
draw_game_over()
else:
player.draw()
enemy.draw()
draw関数の最後で得点を表示します。
screen.draw.text(f"SCORE: {game.score}", topleft=(10, 10), fontsize=30, color="white")
4.3 敵の左右移動
敵が左右に移動できるようにします。左と右、どちらに移動するかをenemy.directionに初期値ランダムで入れます。
enemy.direction = random.choice([-1, 1])
update_enemy関数内で左右に移動させます。画面端まで来たら方向を反転します。
# 左右移動
enemy.x += enemy.direction * 3
if enemy.x <= 0 or enemy.x >= WIDTH: # 画面端で方向を反転
enemy.direction *= -1
それだけでは移動が規則的なので、ランダムに左右に反転させます。
# ランダムに左右を反転
if random.random() < 0.02: # 一定の確率で反転
enemy.direction *= -1
4.4 弾を撃つ処理
弾丸を撃てるようにします。
まず、弾丸のクラスを作成します。属性はxとyです。 また、弾丸の範囲を示すRectを返すメソッド rect() を作成しておきます。
@dataclass
class Bullet:
x: float
y: float
def rect(self):
return Rect((self.x, self.y), (5, 10))
弾丸のリストを作成し、最初は空にしておきます。ここにBulletを追加していきます。
# 弾丸のリスト
bullets = []
弾丸を発射する関数fire_bulletを作成します。これはbulletsに弾丸を追加します。初期座標はプレイヤーの座標の少し上です。
# 弾丸を発射する関数
def fire_bullet():
if not game.game_over: # ゲームオーバー中は発射しない
b = Bullet(player.x, player.y - 20)
bullets.append(b) # 弾丸を追加
これをクロックで一定間隔毎に呼び出します。schedule_intervalで一定時間毎に呼び出せます。
# 自動発射のタイマーを設定
clock.schedule_interval(fire_bullet, 0.25) # 何秒ごとに弾丸を発射するか
弾丸の移動処理を行うupdate_bulley関数を作成します。全ての弾丸を上に10移動させ、画面から消えたらリストから削除します。
def update_bullet():
# 弾丸の移動
for bullet in bullets:
bullet.y -= 10
if bullet.y < 0:
bullets.remove(bullet)
これをupdate関数内で呼び出します。
update_bullet()
弾丸と敵の衝突判定を行います。衝突していたら、弾丸を削除し、敵を上部に戻し、x、y、directionを初期化します。
# 弾丸と敵の衝突判定
for bullet in bullets:
if enemy.colliderect(bullet.rect()):
bullets.remove(bullet)
enemy.y = 0 # 敵を上部に戻す
enemy.x = random.randint(0, WIDTH)
enemy.direction = random.choice([-1, 1]) # ランダムな移動方向に変更
game.score += 1 # スコア加算
break
draw関数で全弾丸を描画します。
for bullet in bullets:
screen.draw.filled_rect(bullet.rect(), 'cyan')
4.5 背景
背景で星が流れるようにします。 星のクラス Starを作成します。属性はx、yとwidth、heightです。rectメソッドでその範囲を返すようにします。
@dataclass
class Star:
x: float
y: float
width: float
height:float
def rect(self):
return Rect((self.x, self.y), (self.width, self.height))
星のリストを作成します。星50個、場所、幅と高さをランダムに設定します。
# 星のリストを作成(星50個。幅と高さをランダムに設定)
stars = [Star(random.randint(0, WIDTH),
random.randint(0, HEIGHT),
random.uniform(1, 2),
random.uniform(1, 2)) for _ in range(50)]
星の更新関数update_starを作成します。星は少し下に移動します。 画面下に到達したら再び上部に戻ります。
def update_star():
# 星の移動
for star in stars:
star.y += 2 # 星の移動速度を反映
if star.y > HEIGHT: # 画面下に到達したら再び上部に戻る
star.y = 0
star.x = random.randint(0, WIDTH) # ランダムなX座標
これをupdate関数で呼び出します。
update_star()
draw関数で星を描画します。
# 星を描画
for star in stars:
screen.draw.filled_rect(star.rect(), "white")