Star☆Unityメモ

Unityのちょっとしたことをメモって後で見返せれたらいいなサイト

Unity:敵の移動(偽A*,A-star),攻撃(ローグライクを自分なりの解釈で作ってみる.)

概要

  1. ローグライクシリーズその4弾
  2. 敵を移動させたい
  3. 近くにプレイヤーがいたら攻撃したい

完成図

f:id:zvn_X:20170122044350g:plain

A*(A-star)って何?

A*とは探索アルゴリズムのことを指します.
いい加減に解釈するとA地点からB地点までの最短距離を導き出してくれます.

偽A-Star実装

今回私はゴールまでの道順ではなく,Enemyの周りのブロックの内,どの場所がプレイヤーに一番近いかを探索します.

まず,Enemy,stage,playerのposition情報を記録している多次元配列を確認します. 配列ごとにレイヤーが分けられていますが今回はわかりやすいようにこの3つのレイヤーを一つに重ねます. すると以下のような位置関係になります

0:床 1:壁 2:Player 3:Monster

1 2 3 4 5 6
1 1 1 1 1 1
1 3 0 0 2 1
1 0 0 0 0 1
1 0 0 1 0 1
1 0 0 0 0 1
1 1 1 1 1 1
  1. monster(3)の位置の周りに壁がないかをstage配列から検索します.
    この場合monster(3)から見て,上と左が壁になっているので2点には移動できないものと考えて良いでしょう.
  2. monster(3)からみて右と下の座標を取得します.
    今回の場合,
    右「x:3,y:2」
    下「x:2,y:3」

  3. plyaer(2)のポジション「x5,y2」とmonsterから見た右と下のpositionの差を求めます

abs(monsterPositionX - PlayerPositionX) + abs(monsterPositionY - PlayerPositionY);<br>

この計算によりPlayerまでの移動にかかるコストを求めることが出来ます
今回の例だと

monsterからみた右のマス
abs(3-5) + abs(2-2) = コスト2

monsterからみた下のマス
abs(2-5) + abs(3-2) = コスト4

これによりmonsterは右のマスに移動することでPlyaerに近づくことができる.

実際に移動してみる

移動の際は物質的な移動と配列の更新が必要です

//物理的な移動
Hashtable moveHash = new Hashtable();
moveHash.Add("position", new Vector3(enemyPositionX + moveX, -0.5f, enemyPositionZ + moveZ));
moveHash.Add("time", 0.4f);
moveHash.Add("delay", 0.0f);
iTween.MoveTo(monsterList[i], moveHash);

//配列の更新
sm.monsterArray[enemyPositionX, enemyPositionZ] = 0;
sm.monsterArray[enemyPositionX + moveX, enemyPositionZ + moveZ] = i + 1;

本当は1マス分だけではなくPlayerに近づくための最短ルートをすべてきちんと出すべきだが,今回は簡易版でこの場をしのぎます
ある程度ゲームが完成した後に更に深掘り下げて実装してきます

きちんと作りたい方は下記のリンクがとても参考になると思います
というか参考になりましたありがとうございます. qiita.com

モンスターの攻撃

モンスターの攻撃を実装してみます.
複雑に考えずにmonsterの周りのマスにプレイヤーがいれば攻撃,いなければ移動します. 攻撃の際にモンスターを回転させる+攻撃アニメーションを再生する.プレイヤーのステータスを変更する事ができればとりあえず良さそうです

//playerがenemyから見た下方向に位置していたら
if (sm.playerArray[enemyPositionX, enemyPositionZ -1] == 2) {
// monsterを回転
    iTween.RotateTo(monsterList[i], iTween.Hash("y", 180, "islocal", true, "time", 0.2f));
// monsterのAttackアニメーションを再生
    monsterList[i].GetComponent<Animator>().SetTrigger("Attack");
// playerStatusにダメージを渡す
    sm.player.GetComponent<PlayerStatus>().Receive(1);
// 0.4秒待つ
    yield return new WaitForSeconds(0.4f);
                }