コンテンツにスキップ

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")