見出し画像

Step106 【Pythonで縦スクロールシューティングを作る#5】:スコア表示の追加

前回の記事では、

  • 自機弾 vs 敵

  • 敵 vs 自機

当たり判定 を実装しました。

これで弾が敵に当たると敵が消え、敵に触れるとゲームオーバーになる・・・
そんな、シューティングゲームとしての基本的な動きが完成したように思いますね。

でもまだまだ。
細かなところにもこだわっていきましょう!
今回はそこに 「スコア表示」 を追加していきます(=゚ω゚)ノ


1. 今回のゴール

今回の記事のゴールは以下の3点です。

  • 敵を倒した数をスコアとして管理する

  • スコアを画面に表示する

  • ゲームオーバー後もスコアが確認できるようにする


2. スコア管理の考え方

スコア管理はとてもシンプルです。

  • score という変数を用意する

  • 敵を倒したタイミングで score += 10 などと加算する

  • pygame.font を使って画面に描画する

今回は単純に 敵1体につき10点 としますが、このあたりはご自由にどうぞ。


3. 完成コード

以下が、前回のコードにスコア処理を追加した完全コード です。

import pygame
import sys
import os
import random

# ----------------------------
# 初期設定
# ----------------------------
pygame.init()

SCREEN_WIDTH = 480
SCREEN_HEIGHT = 640
FPS = 60

screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Shooting Game")

clock = pygame.time.Clock()

# フォント
font = pygame.font.SysFont(None, 64)
small_font = pygame.font.SysFont(None, 28)
score_font = pygame.font.SysFont(None, 32)

# ----------------------------
# パス設定
# ----------------------------
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
IMAGE_DIR = os.path.join(BASE_DIR, "assets", "images")

# ----------------------------
# 画像読み込み
# ----------------------------
player_image = pygame.image.load(
    os.path.join(IMAGE_DIR, "player.png")
).convert_alpha()

bullet_image = pygame.image.load(
    os.path.join(IMAGE_DIR, "bullet.png")
).convert_alpha()

enemy_image = pygame.image.load(
    os.path.join(IMAGE_DIR, "enemy.png")
).convert_alpha()

PLAYER_SIZE = 32
BULLET_SIZE = 24
ENEMY_SIZE = 64

player_image = pygame.transform.scale(player_image, (PLAYER_SIZE, PLAYER_SIZE))
bullet_image = pygame.transform.scale(bullet_image, (BULLET_SIZE, BULLET_SIZE))
enemy_image = pygame.transform.scale(enemy_image, (ENEMY_SIZE, ENEMY_SIZE))

# ----------------------------
# 自機設定
# ----------------------------
PLAYER_SPEED = 5

def reset_game():
    """ゲームを初期状態に戻す"""
    player_rect = pygame.Rect(
        SCREEN_WIDTH // 2 - PLAYER_SIZE // 2,
        SCREEN_HEIGHT - PLAYER_SIZE - 20,
        PLAYER_SIZE,
        PLAYER_SIZE
    )

    bullets = []
    enemies = []

    last_shot_time = 0
    last_enemy_spawn_time = 0
    score = 0
    game_over = False

    return player_rect, bullets, enemies, last_shot_time, last_enemy_spawn_time, score, game_over


player_rect, bullets, enemies, last_shot_time, last_enemy_spawn_time, score, game_over = reset_game()

# ----------------------------
# 弾設定
# ----------------------------
BULLET_SPEED = 8
SHOT_INTERVAL = 200

# ----------------------------
# 敵設定
# ----------------------------
ENEMY_SPEED = 2
ENEMY_SPAWN_INTERVAL = 1000

# ----------------------------
# メインループ
# ----------------------------
while True:
    current_time = pygame.time.get_ticks()

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

        if event.type == pygame.KEYDOWN:
            if game_over and event.key == pygame.K_r:
                (
                    player_rect,
                    bullets,
                    enemies,
                    last_shot_time,
                    last_enemy_spawn_time,
                    score,
                    game_over,
                ) = reset_game()

    keys = pygame.key.get_pressed()

    # ----------------------------
    # 更新処理
    # ----------------------------
    if not game_over:
        # 自機移動
        if keys[pygame.K_LEFT]:
            player_rect.x -= PLAYER_SPEED
        if keys[pygame.K_RIGHT]:
            player_rect.x += PLAYER_SPEED
        if keys[pygame.K_UP]:
            player_rect.y -= PLAYER_SPEED
        if keys[pygame.K_DOWN]:
            player_rect.y += PLAYER_SPEED

        player_rect.clamp_ip(screen.get_rect())

        # ショット
        if keys[pygame.K_SPACE]:
            if current_time - last_shot_time >= SHOT_INTERVAL:
                bullets.append(
                    pygame.Rect(
                        player_rect.centerx - BULLET_SIZE // 2,
                        player_rect.top,
                        BULLET_SIZE,
                        BULLET_SIZE,
                    )
                )
                last_shot_time = current_time

        for bullet in bullets[:]:
            bullet.y -= BULLET_SPEED
            if bullet.bottom < 0:
                bullets.remove(bullet)

        # 敵出現
        if current_time - last_enemy_spawn_time >= ENEMY_SPAWN_INTERVAL:
            enemy_x = random.randint(0, SCREEN_WIDTH - ENEMY_SIZE)
            enemies.append(pygame.Rect(enemy_x, -ENEMY_SIZE, ENEMY_SIZE, ENEMY_SIZE))
            last_enemy_spawn_time = current_time

        for enemy in enemies[:]:
            enemy.y += ENEMY_SPEED
            if enemy.top > SCREEN_HEIGHT:
                enemies.remove(enemy)

        # 当たり判定:弾 vs 敵
        for bullet in bullets[:]:
            for enemy in enemies[:]:
                if bullet.colliderect(enemy):
                    bullets.remove(bullet)
                    enemies.remove(enemy)
                    score += 10
                    break

        # 当たり判定:敵 vs 自機
        for enemy in enemies:
            if enemy.colliderect(player_rect):
                game_over = True
                break

    # ----------------------------
    # 描画
    # ----------------------------
    screen.fill((0, 0, 0))

    screen.blit(player_image, player_rect)
    for bullet in bullets:
        screen.blit(bullet_image, bullet)
    for enemy in enemies:
        screen.blit(enemy_image, enemy)

    # スコア表示
    score_text = score_font.render(f"SCORE : {score}", True, (255, 255, 255))
    screen.blit(score_text, (10, 10))

    # ゲームオーバー表示
    if game_over:
        text = font.render("GAME OVER", True, (255, 255, 255))
        sub = small_font.render("Press R to Restart", True, (200, 200, 200))
        screen.blit(text, (SCREEN_WIDTH // 2 - text.get_width() // 2, SCREEN_HEIGHT // 2 - 60))
        screen.blit(sub, (SCREEN_WIDTH // 2 - sub.get_width() // 2, SCREEN_HEIGHT // 2 + 10))

    pygame.display.update()
    clock.tick(FPS)

4. コード解説① ➤スコア変数の追加

score = 0

スコアは 整数値で管理 します。
ゲームをリスタートした際に 0 に戻すのも重要なポイントです。


5. コード解説② ➤敵を倒したらスコア加算

score += 10

当たり判定(弾 vs 敵)が成立した瞬間に、スコアを加算しています。


6. コード解説③ ➤スコアの表示

score_text = score_font.render(f"SCORE : {score}", True, (255, 255, 255))
screen.blit(score_text, (10, 10))

左上に常にスコアが表示されるようにしています。
数値がリアルタイムで変化するのが確認できるはずです。


7. 今回のまとめ

今回の記事では、

  • スコア変数の管理

  • 敵撃破時のスコア加算

  • 画面へのスコア表示

を実装しました。


8. 次回予告

次回は、

  • オープニング画面

  • ゲーム開始待ち状態

  • ゲーム状態管理(タイトル/プレイ中/ゲームオーバー)

といった、画面遷移の整理 を行っていく予定です。
ここまで来ると、完成形がかなり見えてきますね(*^^*)

次の記事


いいなと思ったら応援しよう!

DOI@日々学びの本質を考え続ける探求者 よろしければ応援お願いします! いただいたチップは引き続きプログラミングや学びについて、皆さんの利益になるようなよい記事を書くことで恩返しをさせていただきます!