アヒルのある日

株式会社AHIRUの社員ブログです。毎週更新!社員が自由に思いついたことを書きます。

プレイヤー操作とAIの切替方法

こんにちは
みにくい社長です。

最近はAI(人工知能)という言葉をよく耳にするようになりました。
ゲーム業界では馴染のある言葉で、昔からメインキャラクタ以外のキャラクタは全て何かしらのAIで動いています。
メインキャラクタをAIに任せるゲームもあります。AIを作って結果を確認するようなタイプのゲームです。
また、キャラクタに限らず、ステージ内にある仕掛けやカメラなど、ゲームの中の世界はAIであふれています。
なのでゲームを開発している時には、操作とAIを切り替えられるようにしておくと、何かと便利なことがあります。
例えば、

  • メインキャラクタの操作をAIに任せてデバッグする
  • 敵を自分で操作して、ボスの動きを考える
  • 固定カメラを自分で操作することで、見えていなかった部分がどうなっているか確認する

などの使い方があります。

実装はとても簡単です。
まず、プレイヤーの毎フレームの更新処理を見ていきましょう。

void update()
{
    // 入力キーの取得
    bool isPushA = Input.getKey(A);

    // 状態遷移
    if(isPushA)
    {
        state = ATTACK; // 攻撃
    }
    else
    {
        state = WAIT;   // 待機
    }

    // 状態に応じた処理
    switch(state)
    {
        case ATTACK:
            // 攻撃アニメーション再生
            // 攻撃コリジョン表示など
            break;
        case WAIT:
            // 待機アニメーション再生
            break;
    }
}

ざっくりと、こんな感じですね。
キー入力に応じて状態を変更し、状態に応じた処理を行います。
では、敵の場合の更新処理はどうなるでしょうか。

void update()
{
    // 状態遷移
    var distance = (position - player.position).Length;
    if(distance < 100)
    {
        state = ATTACK; // 攻撃
    }
    else
    {
        state = WAIT;   // 待機
    }

    // 状態に応じた処理
    switch(state)
    {
        case ATTACK:
            // 攻撃アニメーション再生
            // 攻撃コリジョン表示など
            break;
        case WAIT:
            // 待機アニメーション再生
            break;
    }
}

というように、環境に応じて状態を変更し、状態に応じた処理を行います。

開発中によく起こるのが、プレイヤーと敵を担当しているプログラマが異なる為、この2つのプログラムが異なった形になってしまうということです。
しかし、この2つのプログラムはほとんど同じような処理をしているので、共通化することが可能です。
敵のプログラムの状態遷移の部分を下記のように修正してみましょう。

// 状態遷移
var distance = (position - player.position).Length;
bool isPushA = distance < 100;   
if(isPushA)
{
    state = ATTACK; // 攻撃
}
else
{
    state = WAIT;   // 待機
}

こうすると、2つのプログラムがほぼ同じにできることがわかると思います。
さらに、操作しているかどうかのフラグを使えば共通化できます。

// 入力のキー取得
if(isControlKey)
{
    getInputManual();
}
else
{
    getInputAI();
}
// 状態遷移
if(isPushA)
{
    state = ATTACK; // 攻撃
}
else
{
    state = WAIT;   // 待機
}

これで完成です。プレイヤー操作の時はgetInputManual、AIの時はgetInputAIで入力情報を取得、決定します。
それ以外の部分は共通処理にすることができます。
このようにしておくと、キー入力を記録して再生することもできます。
もちろん記録と再生の仕組みは別に用意する必要がありますが、用意できたら再生は簡単です。

// 入力のキー取得
if(isControlKey)
{
    if(useLog)
    {
        getInputLog();
    }
    else
    {
        getInputManual();
    }
}
else
{
    getInputAI();
}

キー入力と状態遷移をしっかり分離しておくことが重要です。
そうしておくことで、簡単に操作方法を変更することができるようになります。

またねー