Unityで敵キャラを登場させて、目的地に移動させる

今回は、Unityで目的地に移動する敵キャラを作ってみようと思います。

まずは敵キャラをAsset Storeで探してインポートしてください。

3Dモデル→人型→その他→Zombie

をインポートしてみました。
インポートしたZombieのプレハブをRig→Humanoidで人型にしてシーンに設置します。

敵1

スポンサーリンク

敵キャラクターにコンポーネントを追加する

現時点ではAnimator Controllerの設定をしていなかったり、ゾンビを動かすCharacter Controllerを追加していないので、石造のように動きません。

まずはCharacter Controllerを設定して、当たり判定の範囲を調整しましょう。

敵2

これでスクリプトからCharacter Controllerの機能を使ってゾンビを操作する準備は整いました。
次はアニメーション遷移をMecanimで作成していきます。

Zombieフォルダ内にZombie Controllerという名前のAnimator Controllerを作成します。

敵3

Zombie Controllerをダブルクリックしアニメーション遷移画面を表示させます。

敵4

アニメーションパラメータのSpeedを追加し、右クリック→Create State→EmptyでIdleとWalkの二つの状態を作ります。

敵5

IdleからWalk、WalkからIdleへMake Transitionしアニメーション遷移を作成します。
それぞれの遷移条件をSpeedが1の時を境に遷移するように設定します。

これでアニメーション遷移とその条件の設定が終わりました。この辺りは

Unityのアニメーションの切り替えシステムとスクリプト
Unityのアニメーションを切り替える為のアニメーターの設定とアニメーションの遷移をさせるスクリプトの作成を行います
の記事でやったのでそちらを参考にしてください。

敵キャラを動かす方法を考える

それではスクリプトを作成し敵キャラを移動させます。
敵キャラの場合は主人公キャラと違って、キーを押したら動くわけではないので、目的地を設定しそこに移動するようにします。

移動が終わったら次の目的地を設定し、また移動する。
という事を繰り返します。

まずは最初の目的地を設定しそこに移動するスクリプトを組んでみます。

AssetsエリアのscriptフォルダにMoveEnemyという名前のスクリプトを作成します。
このMoveEnemyで敵キャラが動くスクリプトを書いていきます。

MoveEnemyスクリプトを敵キャラにドラッグ&ドロップするか、敵キャラのインスペクタ上でAdd Componentからスクリプトの追加をします。

適当な位置に敵キャラであるゾンビを配置したらどのようなスクリプトを組むか考えてみます。

まずは最初の目的地を指定します。

そこにCharacterControllerの機能を使って歩くアニメーションにさせつつ目的地に移動させます。
目的地についたら歩くアニメーションを終了し、その場でただ立っているアニメーションをします。

ゾンビをx:17、y:0、z:18に設置したとして、ゾンビの目的地をx:25、y:0、z:25にします。

この流れをスクリプトに書いていく必要があります。

敵キャラを動かすスクリプトを作成する

ではスクリプトを組んでいきます。

パラメータをそれぞれ宣言します。

オブジェクト開始時に様々な初期処理を実行しておきます。
最初に設定したとおり目的地destinationにVector3(25, 0, 25)を設定します。

次は移動の処理です。

敵キャラが接地(地面の上にいる)していたらアニメーターのSpeedの値を2.0にしWalkアニメーションに切り替えます。

1.0より大きい値であればなんでもかまわないです。

ただ1.0だとWalk→Idleへの条件とも合致するせいか何も反応しなくなりました。
directionは移動する方向を入れる変数で、

(目的地 - 現在のキャラの位置).normalized

で目的地の方向が算出できます。normalizedは正規化した値を出すので距離は最短で方向だけを取り出す事が出来ます。

この方向にスピード値をかけて移動させていきます。
次にキャラクターを目的地の方向に向かせたいので、

transform.LookAt(Vector3(destination.x, transform.position.y, destination.z));

とします。

transform.LookAtで指定した場所を向かせる事が出来るので、引数にVector3で向かせたい位置を指定しています。

xとzの値は目的地の値を指定し、yの値だけはキャラクターのy値を指定します。

yは上下方向なので目的地のy値を指定すると目的地が山の上にあると、キャラクターが上を向いて進んでいくということになってしまう為です。

これでキャラクターの方向を目的地の方向に向ける事が出来ましたが、
元々はdirectionを使ってLookAtすればいいような気がしたんですが、どうにもうまく出来なかった為違うやり方にしました・・・。

ちなみにDebug.LogはUnityのConsoleに変数の値がちゃんと入っているかどうか?
の確認に使う事が出来ます。

あとは重力分をVelocityに換算し、CharacterControllerの機能を使って移動させるだけです。
ここまでのソースコードは以下の通り

ではUnityの実行ボタンを押して確認してみます。

敵キャラ移動スクリプトの問題点

移動は出来ましたが、目的地に到着しても目的地あたりでうごめくのがわかるかと思います。

敵6

到着しても知らせる処理を設けておらず、永遠に目的地に移動しようとしてしまいます。
本来であれば到着したらその場でIdleのアニメーションになりただ立っている状態にしたいところです。

敵キャラが目的地についたかどうかの情報を持たせる

それでは目的地に到着した場合、到着した!というフラグ(スイッチ)を立て、その後は移動しないようにします。

private var arrived : boolean;

到着したかどうかの判定に使うboolean型の変数arrivedを宣言します。
boolean型は真か偽かの判定に使うものです。
Start関数内で初期値としてfalseを入れておきます。最初はもちろん到着してませんからね。

arrived = false;

さて次は到着したかどうかの判定です。到着したかどうかの判定は目的地と現在地の距離がかなり狭まった時に到着と判定する事にします。
2地点の距離はVector3.Distanceで求められるので、

距離が0.3未満になったら到着判定変数にtrueを入れ、アニメーターのSpeed値に0を入れます。

これで到着した場合の処理が完成しました。

到着していた場合はキャラクターの移動はさせないように、到着していない場合のみ移動処理を実行させます。

ここまでの処理を追加したソースコードは以下の通りです。

これで指定した位置に敵キャラが移動するようになりました。

ですがこれではいちいち目的地を設定し直さないといけなくなってしまい使い勝手がよくありません。

なのでVector3(25, 0, 25)としている場所をランダムに設定出来るようにしてみます。

目的地をランダムに設定出来るようにする

最初のスタートポイント(敵キャラを最初に置いた場所)から一定の範囲内にランダムに目的地を設定する事が出来るようにします。

その為スタートポイントを記憶しておきます。
スタートポイント変数はcurrentととし、Start関数内で代入します。

current = transform.position;

Start関数内はオブジェクトが現れた時に1回実行されるので、その時のオブジェクトの位置、つまりシーンに配置した位置(transform.position)を入れておきます。

次にランダム値を作ります。
Random.insideUnitCircleで半径1以内のランダムな点を作成する事が出来るので、半径8以内であれば、

Random.insideUnitCircle * 8

となります。スタートポイントにランダムな点を足せば決められた範囲内で目的地を作る事が出来ます。

var randDestination = Random.insideUnitCircle * 8;
destination = current + Vector3(randDestination.x, 0, randDestination.y);

ランダムな点はVector2の点で得られるので、それをVector3に適用します。
Unityではyは上下、zは前後の動きなので、Vector2のy値をzに指定します。

Random.insideUnitCircleを使わずともランダム値を得るRandom.range(0.0f, 8.0f)を使って二つの値を作り、それを設定する事でも同じような結果が得られます。

以下全文です。

出来上がったので、Unityの実行ボタンを押して確認してみましょう。
ゾンビが目的地に移動し、目的地近くになったらその場でただ立っている状態になります。

敵7

終わりに

今回はこのへんで、終わります。

敵キャラが登場したら登場した位置を基点にランダムな目的地に移動するキャラクターが出来ました。
次回は目的地に移動したら数秒待機し別の目的地を設定し移動を開始するスクリプトを組みます。
諸事情によりスクリプトを分ける作業が入ります。