Skip to content
第03回 敵の出現と移動

1 / 4 敵クラスと管理クラスの設計 ​

このステップの目的

敵1体分の状態を持つEnemyクラスと、敵の集合を管理するEnemyManagerの構造を定義します。

弾の管理構造を敵キャラクターへ適用する ​

第02回で作成したプレイヤー弾(PlayerShot)は、弾1発分の位置と有効フラグを保持していました。
敵キャラクターも同様に、敵1体分の情報と振る舞いをカプセル化したEnemyクラスを定義します。

敵は個体ごとに出現位置や速度が異なります。直進に加え、左右に揺れる動きもあります。そのため、移動パターンを切り替える状態をメンバ変数に持たせます。

Enemy.hcpp
#pragma once

#include "DxGame/Rect.h"
#include "DxGame/Vector2.h"

namespace dxgame::shooting
{
    class ImageManager;

    // 敵一体の位置、移動、表示、実行状態を管理する
    // 敵1体の位置、移動、表示、実行状態を管理する
    class Enemy
    {
    public:
        // 下方向へ直進するか左右へ波打ちながら進むかを表す
        enum class Behaviour
        {
            Straight,
            Wave
        };

        // 中心座標、速度、移動方法を設定して敵を出現させる
        void Spawn(const Vector2& startPosition, float speed, Behaviour spawnBehaviour);

        // 敵を移動させ、画面の下へ出たら停止する
        void Update(float deltaTime);

        // 実行中の敵を画像で描く
        void Draw(const ImageManager& images) const;

        // 更新と描画の対象ならtrueを返す
        bool IsActive() const;

        // 敵を更新と描画の対象外にする
        void Deactivate();

        // 表示用Rectを現在位置へ移動して返す
        Rect GetDrawRect() const;

    private:
        Vector2 position = {};
        float baseX = 0.0f;
        float moveSpeed = 0.0f;
        float age = 0.0f;
        Behaviour behaviour = Behaviour::Straight;
        bool isActive = false;
    };
}

baseXは波状移動の中心となるX座標です。ageには出現後の経過時間を記録し、波の計算に使います。

敵の初期化と停止 ​

Spawn()では、出現時に必要なパラメータを受け取ってすべてのメンバ変数を初期化し、isActiveをtrueに設定します。

Enemy.cppcpp
void Enemy::Spawn(const Vector2& startPosition, float speed, Behaviour spawnBehaviour)
{
    position = startPosition;
    baseX = startPosition.x;
    moveSpeed = speed;
    age = 0.0f;
    behaviour = spawnBehaviour;
    isActive = true;
}

bool Enemy::IsActive() const
{
    return isActive;
}

void Enemy::Deactivate()
{
    isActive = false;
}

画面外への到達や、後の回で実装する撃破処理によって停止させる場合は、Deactivate()を呼び出してisActive = falseに戻します。

敵を統括するEnemyManagerの構造 ​

敵も弾と同じく多数が出現するため、固定長配列を用いたEnemyManagerで一括管理します。

EnemyManager.hcpp
#pragma once

#include <array>
#include <cstddef>

#include "Shooting/Enemy.h"

namespace dxgame::shooting
{
    // 敵の管理クラス
    class EnemyManager
    {
    public:
        // すべての敵を停止し、出現順と最初の出現待ち時間を初期化
        void Reset();

        // すべての敵を更新する 出現タイマーを進め、必要なら敵を生成する
        // すべての敵を更新する(出現タイマーを進め、必要に応じて敵を生成)
        void Update(float deltaTime);

        // アクティブなすべての敵を描画する
        void Draw(const class ImageManager& images) const;

    private:
        // 停止中の要素を再利用しゲーム中にはメモリを確保しない
        static constexpr std::size_t Capacity = 24;

        // 停止中の敵を1体選び出現順に応じた設定で生成する
        // 空きがない場合は何もしない
        void SpawnOne();

        std::array<Enemy, Capacity> enemies = {};
        float spawnTimer = 0.0f;
        int spawnIndex = 0;
    };
}

敵の最大同時出現数は24体(Capacity = 24)として設定します。
EnemyManagerは、毎フレームの更新と描画をループでまとめて実行します。タイマーを管理し、一定間隔で新しい敵を出現させる役割も持ちます。

チェックリスト
  • Enemyクラスが位置、速度、経過時間、移動挙動、有効フラグを保持している
  • Behaviour列挙型によって敵の挙動を表現できる設計になっている
  • EnemyManagerが固定配列(Capacity)を用いて敵をプール管理している
  • 敵単体の責任(Enemy)と全体の集合管理の責任(EnemyManager)の違いを説明できる