キャラクターAIを作ってみよう

image.png

今日の目標

今日の体験実習では、2Dボンバーマン風ゲームのキャラクターAIのプログラムを作りながら、C++によるゲーム開発の実際の雰囲気を体験してほしいと思います。

C++によるプログラム開発の流れ

  1. Build → ソースをビルドして実行
  2. Edit → 指定の行番号の定義を『書き換え』
  3. Test → 動作を確認し、発見・改善

POINT: 小さな変更を加え、すぐにビルド&実行。これを繰り返すのが開発サイクル!

ゲームAIの種類

  1. キャラクターAI
    • キャラクターAIは文字通り、NPCを動かすために利用される
    • 格闘ゲームの敵キャラクターや、RPGで主人公を支援する仲間の動作を決める
  2. メタAI
    • ゲーム進行を補助する
    • 一定の条件を満たしたらイベントを開始
    • ステージに応じて敵やアイテムの配置を変更したりと、ゲーム画面の裏で色々な処理を行う
  3. ナビゲーションAI
    • キャラクターAIやメタAIに対し、各種情報を提供して動作をサポートする
    • 敵キャラクターに主人公の位置や進行方向、障害物に関する情報を与える
    • この仕組みがないと、キャラクターAIは正確な動作を行えない

その1.キャラクターを選ぼう

ゲームに登場するキャラクターは、その種類によっていろいろなパラメータを持っています。
プログラミング内でそれらを表現するときは、すべて数値や文字などの値として表現します。
これらのゲーム中で利用する値に名前をつけたものを「変数」と呼びます。

変数の例:

体験用プログラムでも同様に、敵キャラクターをEnemyという変数で表しています。
Enemyはその中に更に細かい設定用のパラメータ(変数を持っています)。
その中の、表示画像を表す変数を変更してみましょう。

Enemyの表示画像の変更

image.png

行番号:74 行目を見て書き換え

42  if (isGraphic) {
 43      //#01 敵の画像を選択する!  
 44      std::string enemyImage = GetEnemyImage(KABOCHA);
 45      enemyImage_ = LoadGraph(enemyImage.c_str());
 46  }

その2.初期出現位置を変更してみよう

初期出現位置の設定

初期位置をステージの右下に設定してみよう!
キャラクターは、座標系に設置されます。(座標も変数で表すよ!)
2Dならxy平面、3Dならxyz空間に(10,20)とか(40,50,1)のように設定されます。

image.png



座標は、x座標とy座標の値を持っています。
初期位置は、ちょっとした計算によって以下のように設定されています。

image.png

x,y方向に何マス目は26行目辺りで設定されているよ!

書き換えてみよう

行番号:26 行目を見て書き換え

24  //#02 敵の初期出現位置を変更してみよう
 25  //ステージの右下に設定するには?
 26  const Pointf INIT_POS{ 5, 5 };  // #02
 
 //26行目を書き換えると、66行目の計算に反映される
 66  pos_ = { INIT_POS.x * CHA_WIDTH, INIT_POS.y * CHA_HEIGHT };

その3.初期進行方向を変更してみよう

次の目標

次の目標として、ステージの右下から、ステージの左端まで移動させたい
Enemy飲む気はどのように決められているのか見てみよう。(どうせ方向も変数なんでしょ!)

方向を表す変数

初期方向はINIT_DIRという変数に設定されています。

image.png


image.png

image.png

その4.速度を与えて動かそう

ゲームの移動処理の基本

ゲームのキャラクタは、方向を示すベクトルと、スピード移動時間(フレーム間時間)で、次のフレームの位置を計算します。
これは、小学生の時に習った、

$\text{移動距離} = \text{速度} \times \text{時間}$

の式に方向がついたものを使います。って言っても、

image.png

移動に、方向が加わるだけであとは小学校で習ったのと一緒です。
上の図でいうと、
AからBに1フレームで距離16移動している
スピードは?
下の図のようにx軸方向、y軸方向にはどのぐらい移動してるかな?
みたいな話です。

ベクトル演算を使ってゲームキャラクターを移動する方法|Mizeee

ゲームのフレーム(画面更新)の仕組み

ゲームは1秒間に何回画面を更新している?
一般的には 60FPS (Frame Per Second) が標準です。FPSとは「1秒あたりのフレーム数」を表し、フレームとは「画面を1回描き直すこと」です。

方向とフレーム間時間を考慮した移動処理

image.png

1フレームごとにこんな感じに、計算していきます。

速度の変数を変更する

向きが決まったら、その方向にEnemyを進めてみよう。
現在は、(わざとらしく)スピード設定が0になっている。

行番号:16 行目を見て書き換え

14  //#04 敵の移動速度を変更してみよう
 15  //ちょうどよい速さはどのぐらいかな?
 16  const float SPEED = 0.0f;  // #04

その5.キャラクターAI(移動処理の追加)

移動パターンを考える

ゲームのキャラクタは、ゲーム内で得た情報を使っていろいろな情報を使って、その場面場面に適した動きを切り替えます(状態遷移)これらを自動的に行うのがAIの仕事になります。

簡単な移動のパターンを考えてみよう

現在のEnemyは、プレイヤーの情報などを知る手段がないので、自分ができる範囲(自分の位置、ステージの情報)を使ってできる移動はこのような感じになると思います。

往復運動

Enemyは、移動していて外周にあたった、という情報のみを知ることができます。
isHitWall()
 true(真)のとき :壁にぶつかった
 false(偽)のとき:ぶつかってない

前に出てきた条件分岐の処理を応用することで、往復運動を実現できます。

  1. 移動方向 forward_ を持ち、その方向に毎フレーム進む
  2. 壁判定isHitWall() が true になると「壁に当たった」と認識
  3. 反転(180度ターン):現在の方向を逆向きに変更
  4. 継続移動:逆向きのまま動き続け、再び壁に当たるまで直進

つまり、右に進んで壁にぶつかったら左に進み、左の壁にぶつかったらまた右に進む、をくり返すことで往復運動を実現します。

反転の処理

行番号:206行目を見て書き換え

// #05 方向転換ロジック
void Enemy::Trurn180()
{
	if (forward_ == UP)
		forward_ = NONE;
	else if (forward_ == LEFT)
		forward_ = NONE;
	else if (forward_ == DOWN)
		forward_ = NONE;
	else if (forward_ == RIGHT)
		forward_ = NONE;

}

行番号:53 行目を見て有効化

51  //#06 敵の進行方向を変える関数を実装しよう
 52  // 外周を回ってみよう!壁に当たったら方向転換。
 53  if (isHitWall_) { TurnRight(); }  // #06

応用

同様に、TurnLeftかTurnRightを使って、外周を回り続けるには?


Revision #10
Created 26 July 2025 07:16:33 by youe2
Updated 2 June 2026 18:00:13 by youe2