もどる

この記事は「うずら」とAIが協力して作成しました。
なるべく正確さを心がけていますが、最新の公式ドキュメントなどもあわせて確認してみてね!

python2026.3.11

4-1. 赤い彗星:敵の種類を増やそう

実践:赤い彗星
強敵出現

皆さん、こんにちは!世界最高に親切な技術講師うずらです。
前回は、ゲームに「ゲームオーバー」の概念を導入し、スリル満点の宇宙シューティングゲームへと進化させましたね。
実践:激突!ゲームオーバー

今回は、さらにゲームを面白くするために、敵キャラクターに「個性」を与えてみましょう!
ただの雑魚敵だけでなく、速い敵や得点の高い敵など、様々なバリエーションが登場すると、ゲームはもっと奥深くなります。

まるで赤い彗星のように、一際目を引く強敵が登場するかもしれませんよ。
目に見える形でPythonの力を学んでいきましょう!

1. 3秒でわかるまとめ

敵の種類を増やし、それぞれに速度や見た目、得点などの特徴を持たせることで、ゲームに深みと戦略性を加えることができます。

2. 敵にも「個性」を与えよう!

これまでの敵は、すべて同じ見た目で同じ速度で動いていました。これでは少し単調ですよね。
ゲームの世界では、敵の種類が豊富だと、プレイヤーは「次はどんな敵が出てくるんだろう?」とワクワクしたり、「この敵にはこう対処しよう!」と戦略を考えたりするようになります。

Pythonでゲームを作る際も、敵の情報を「辞書(dict)」を使って管理することで、簡単に個性を設定できます。

名前付き辞書の魔法

今回は、通常の敵(雑魚敵)と、少しだけ速い敵(高速敵)の2種類を作成します。

3. 敵の種類を定義する:spawn_enemy関数の改造

まずは、敵を出現させるspawn_enemy関数を改造して、敵の種類を決める部分を追加しましょう。

前回のコードからの変更点を見ていきます。

# --- 定数設定 ---
SCREEN_WIDTH = 160
SCREEN_HEIGHT = 120
GAME_TITLE = "ASTRO SURVIVOR"
STAR_COUNT = 40

# キャラクターのサイズ
PLAYER_W = 8
PLAYER_H = 8
PLAYER_SPEED = 3
ENEMY_W = 8 # ★ 追加: 敵の横幅
ENEMY_H = 8 # ★ 追加: 敵の縦幅

# ... (中略) ...

# 敵を出現させる
def spawn_enemy():
    is_fast = random.random() < 0.3 # 30%の確率で高速な敵
    kind = 'fast' if is_fast else 'zako' # 種類を決定

    enemies.append({
        'x': random.randint(0, SCREEN_WIDTH - ENEMY_W),
        'y': -10,
        'vx': 0,
        'vy': random.uniform(1.5, 2.5) if is_fast else random.uniform(0.5, 0.8), # 速度
        'hp': 1,
        'type': kind, # 種類を辞書に追加
        'w': ENEMY_W, 'h': ENEMY_H, # サイズも辞書に追加
        'score': 20 if is_fast else 10, # 得点も種類によって変更
        'color': COL_RED if is_fast else COL_GRAY # 色も種類によって変更
    })

変更点を詳しく見ていきましょう。

  1. ENEMY_W, ENEMY_H 定数の追加
    PLAYER_W, PLAYER_H の下に ENEMY_W = 8ENEMY_H = 8 を追加しました。これにより、敵のデフォルトサイズを定義します。

補足だよ

ENEMY_WENEMY_Hの補足
前回のコードから ENEMY_WENEMY_H の定義が抜けてしまっていましたが、今回の修正で再度追加しました。敵のサイズを定数として定義することで、コード全体で一貫したサイズを利用でき、変更が必要な場合も一箇所を修正するだけで済みます。
  1. 敵の種類の決定 (is_fast, kind)
    is_fast = random.random() < 0.3
    random.random() は0.0以上1.0未満の浮動小数点数をランダムに生成します。
    これが0.3未満(30%の確率)であればis_fastTrueになります。つまり、30%の確率で高速な敵が出現します。

    kind = 'fast' if is_fast else 'zako'
    これはPythonの「条件式(三項演算子)」と呼ばれる書き方です。
    もし is_fast がTrueなら 'fast'、そうでなければ 'zako'
    という意味になります。これで、敵の種類を'fast''zako'のどちらかに決定し、kind変数に格納します。

  2. 辞書への情報追加
    enemies.append({...}) の部分に、以下のキーと値を追加しました。

    • 'type': kind: 敵の種類を文字列で保存します。
    • 'vy': ...: is_fastに応じて速度 (vy) を変えます。高速敵は 1.5 から 2.5、雑魚敵は 0.5 から 0.8 の間でランダムに設定されます。
    • 'w': ENEMY_W, 'h': ENEMY_H: 敵の幅と高さを辞書に保存します。今はどちらの種類も同じサイズですが、将来的に種類ごとにサイズを変えたい場合に便利です。
    • 'score': ...: 倒した時の得点をis_fastに応じて変えます。高速敵は 20 点、雑魚敵は 10 点です。
    • 'color': ...: 敵の色もis_fastに応じて変えます。高速敵は COL_RED(赤)、雑魚敵は COL_GRAY(灰色)になります。

豆知識

random.uniform()で浮動小数点数の乱数
random.randint(a, b)は整数を生成しますが、random.uniform(a, b)a以上b以下の浮動小数点数を生成します。これにより、敵の速度に細かいバリエーションを持たせることができます。

これで、出現する敵が種類ごとに異なる速度、得点、色を持つようになりました。

4. 個性に合わせて敵を描画する:draw関数の改造

敵に種類と色という個性が付いたので、draw関数でそれぞれの敵を適切に描画するように変更しましょう。

# 毎フレーム実行される描画処理 (結果を画面に表示する。計算はしない)
def draw():
    pyxel.cls(COL_BLACK)
    
    # ... (中略) ...

    # 敵
    for e in enemies:
        if e['type'] == 'bullet': # ★ 追加: 敵弾の描画(今回は通常の敵に集中)
            pyxel.circ(e['x'], e['y'], 2, COL_YELLOW)
        else:
            col = e['color'] # 辞書から色を取得
            pyxel.rect(e['x'], e['y'], e['w'], e['h'], col) # 辞書からサイズを取得
            if e['type'] == 'zako': # 雑魚敵のデザイン
                pyxel.rect(e['x']+2, e['y']+2, 4, 4, COL_NAVY)
            else: # 高速敵のデザイン ('fast')
                pyxel.pset(e['x']+3, e['y']+3, COL_WHITE)
            
    # UI
    pyxel.text(5, 5, f"SCORE:{score}", COL_WHITE)

for e in enemies: ループの中が大きく変わりました。

  • if e['type'] == 'bullet':
    この部分は、将来的に敵が「弾」を発射するようになったときに、その弾もenemiesリストに含めて管理するための準備です。今回はまだ敵弾は登場しませんので、この条件がTrueになることはありません。通常の敵の描画について見ていきましょう。

  • else: の中
    col = e['color'] で、辞書に保存されている'color'の値を使って描画色を指定します。これで、高速敵は赤、雑魚敵は灰色で描画されます。

    pyxel.rect(e['x'], e['y'], e['w'], e['h'], col)
    敵の描画も、辞書に保存されている'w''h'を使ってサイズを指定します。

    さらに、if e['type'] == 'zako': という条件分岐を使って、敵の種類によって異なるデザインを描画しています。

    • 'zako'(雑魚敵)の場合: 中央にCOL_NAVY(濃い青)の小さな四角を描画し、少しメカっぽいデザインにします。
    • else'fast'、高速敵)の場合: 中央にCOL_WHITE(白)の点を打ち、シンプルなデザインながらも、赤色と相まって「赤い彗星」のような印象を与えます。

これで、見た目にも敵の個性が表現されるようになりました!

5. 敵の当たり判定と挙動の調整:update関数の改造

敵の種類を増やしたことで、update関数での敵の挙動や当たり判定にも一部変更が必要です。特にe['w']e['h']を使うように修正します。

# 毎フレーム実行される更新処理 (計算などはここで行う。描画は禁止)
def update():
    global player_x, player_y, score, game_over
    global bullets, enemies

    # --- 1. ゲームオーバー / クリアチェック ---
    if game_over:
        return

    # ... (中略) ...

    # 敵の更新
    surviving_enemies = []
    for e in enemies:
        # 敵の種類によって動きを変える (今回は'bullet'の場合のみ)
        if e['type'] == 'bullet': # ★ 追加
            e['x'] += e['vx']
            e['y'] += e['vy']
        else:
            e['y'] += e['vy']
            
        if e['y'] > SCREEN_HEIGHT:
            continue

        # 当たり判定のサイズも敵の種類によって変更
        hit_w = e['w'] if e['type'] != 'bullet' else 4 # ★ 変更
        # プレイヤーと敵の当たり判定
        if check_collision(player_x, player_y, PLAYER_W, PLAYER_H, e['x'], e['y'], hit_w, hit_w):
            game_over = True
            
        hit = False
        # 通常敵の場合のみ弾との当たり判定を行う
        if e['type'] != 'bullet': # ★ 追加
            for b_idx in reversed(range(len(bullets))):
                bx, by = bullets[b_idx][0], bullets[b_idx][1]
                if (e['x'] < bx < e['x'] + e['w'] and # ★ 変更
                    e['y'] < by < e['y'] + e['h']): # ★ 変更
                    del bullets[b_idx]
                    hit = True
                    break
        
        if hit:
            score += e['score']
            continue
        
        surviving_enemies.append(e)
    enemies = surviving_enemies
  • 敵の動きの変更 (if e['type'] == 'bullet':)
    draw関数と同様に、'bullet'タイプの敵はx方向にも移動する可能性がありますが、今回の主題である通常の敵('zako''fast')は引き続きy方向のみに移動します。

  • 当たり判定の修正 (hit_w = e['w'] if e['type'] != 'bullet' else 4)
    敵の当たり判定の幅を、辞書から取得したe['w']を使うように変更しました。'bullet'タイプの場合はサイズを4に固定しています。これにより、将来的に敵の種類によって当たり判定のサイズを変える柔軟性が生まれました。

  • 弾との当たり判定 (if e['type'] != 'bullet':)
    弾と敵の当たり判定も、'bullet'タイプ(敵が発射した弾)とは行わないように条件を追加しました。
    また、当たり判定の範囲もENEMY_WENEMY_Hの定数ではなく、辞書に保存されているe['w']e['h']を使用するように変更しています。

これで、敵の種類に応じた動きや当たり判定も可能になり、コードがより柔軟になりました。

実際にブラウザで動かしてみてください。
赤い彗星のような高速な敵がランダムで登場し、ゲームがさらにエキサイティングになったことを確認できます。

実際に動かしてみよう!

矢印キーで宇宙船を操作し、スペースキーで弾を発射できます。赤い敵は通常の敵より速く、得点も高いです。

今回のまとめ

今回は、randomモジュールと辞書を組み合わせて、敵に「種類」と「個性」を与える方法を学びました。

  • random.random() でランダムな確率に基づいて敵の種類を決定。
  • 辞書に 'type''vy'(速度)、'score'(得点)、'color'(色)、'w''h'(サイズ)などの情報を追加し、種類によって異なる値を設定。
  • draw関数でe['type']e['color']の値に応じて敵の見た目を変更。
  • update関数でe['w']e['h']の値に応じて当たり判定の範囲を調整。

敵の種類が増えることで、ゲームの戦略性が増し、飽きさせない面白さが生まれます。
次に待つのは、どんな驚きの要素でしょうか?

次回は、敵を倒したときや、自機が被弾したときに、キラキラと爆発するような「パーティクル」というエフェクトを追加して、ゲームをより華やかに演出してみましょう!

演出の極意爆砕演出


最後まで読んでくれてありがとう!🌱
ノートみたいに、いつでも見返してね。