Unityのゲームでゾンビに足を捕まれそこから脱出する機能

今回はご依頼頂いた機能で、ゲームの主人公キャラクターが移動中に敵に捕まれ、身動きが取れなくなった後に特定のキーを押して主人公が脱出する機能です。

昔のバイオハザードにあるような機能ですね。

敵に抱きつかれる機能については

Unityで敵が主人公に抱きつく機能を作る
Unityのゲームで敵が主人公キャラに抱きつき離れないようにする機能を作成します

で作成していますが、

今回は敵に掴まれている間は主人公が身動き出来ない状態にし、特定のキーを数回押してまた移動出来る状態にします。

スポンサーリンク

最小限の主人公キャラを準備

まずは最小限の主人公操作スクリプトHoldCharacterMoveを作成します。

移動に関する最小限のスクリプトになっています。

主人公キャラ側の操作スクリプトはこのスクリプトに処理を追加していきます。

主人公キャラに移動機能であるCharacterControllerを取りつけたりする方法は

Unityでキャラクターを移動する為の準備と概要(スクリプトを書く前に)
Unityでキャラクターを移動する為に準備しておく事と、移動させる為に必要な事を考える

等を参考にしてください。

主人公キャラに設定するAnimatorControllerの状態は立っている状態のIdle、動いている状態のRunを作成しておきます。

アニメーターコントローラーの作成については

Unityのアニメーションの切り替えシステムとスクリプト
Unityのアニメーションを切り替える為のアニメーターの設定とアニメーションの遷移をさせるスクリプトの作成を行います

を参照してください。

主人公キャラにCharacterControllerの追加とスクリプトHoldCharacterMoveの取り付けが終わったらキーボード操作でキャラクターを動かし問題がない事を確認してください。

敵キャラのゾンビの準備

次に主人公キャラを捕まえるゾンビを準備します。

AssetStoreの検索窓でpxltiger zombieで検索し出てきたゾンビをインポートします。

pxltigerは作者名になります。

インポートが終了したらシーンに設置します。

ゾンビのアニメーションは別途ご用意して頂く必要があります。

自分自身でUnityで使用するアニメーションを作成するには

blender2.76を使ってUnityで使うアニメーションを作成する
Unityで使用するキャラクター用のアニメーションをBlenderを使って作成していきます。作成方法を学べば自分のゲームに自前のアニメーションを使うことが出来ます。

を参考にしてください。

ゾンビが主人公に向かっていくアニメーションは

ゾンビが徘徊するアニメーション

↑のように地面にうつぶせで歩くアニメーションを使います。

ちょっと動きが速くてゴキブリのようですね・・・・(^_^;)

また主人公を捕まえた時のアニメーションには

ゾンビが主人公を捕まえたアニメーション

↑のように地面に伏せたまま動かないアニメーションを使用します。

アニメーションの土台自体を傾けておく

どちらのアニメーションも↑のようにキャラクターの土台を傾けて作成しておきます。

立った状態でアニメーションを作成しUnity側で傾けるという方法でも出来ますが、Unity側の管理が簡単になる為blender等でも傾けてアニメーションを作成しておきます。

ゾンビ用のアニメーターコントローラーを作成しこれらのアニメーションを状態として設定します。

ゾンビのアニメーターコントローラー

Idleには地面に伏せたまま動かないアニメーションを設定し、Runには先ほどの地面に伏せて歩くアニメーション、Holdには地面に伏せたまま動かないアニメーションを設定します。

アニメーションパラメータにFloatのSpeed、BoolのHoldを作成します。

Idle→RunはSpeedがGreaterで0.1
Run→IdleはSpeedがLessで0.1
Run→HoldはHoldがtrue
Idle→HoldはHoldがtrue
Hold→IdleはHoldがfalse

という条件で遷移するようにします。

ゾンビにはHoldZombieMoveスクリプトを作成し取りつけます。

このスクリプトはゾンビが登場したら主人公を追いかけ、主人公と一定の距離まで来たらその場で止まるまでの処理です。

主人公キャラクターにはPlayerというタグを作成し設定しておく必要があります。

ゾンビにはCharacterControllerを取りつけてコライダのサイズを調整します。

最初から地面にうつ伏せで動くのではなく、立って歩く状態からうつ伏せ状態へと遷移させる場合はスクリプトでコライダのサイズの調整が必要になりますが、

今回は寝そべったまま始まり立ちあがる事がないものとしておきます。

設定したコライダのサイズ

↑がCharacterControllerのコライダのサイズです。

CharacterControllerは基本縦に長くは出来ますが、横に長く出来なかったので↑のような感じにしました。

ゾンビのインスペクタ

↑がゾンビのインスペクタになります。

Speedに1.5、stopDistanceに1を設定します。

次にゾンビに主人公を検知するエリアとスクリプトを作成します。

サーチエリアを作成

ゾンビの子要素に右クリック→Create Emptyで空オブジェクトを作成し名前をSearchAreaとします。

サーチエリアのインスペクタ

↑がSearchAreaのインスペクタでAdd ComponentからPhysics→SphereColliderを選択します。
SphereColliderのサイズを調整し、主人公を検知する範囲を設定します。

SearchCharacterスクリプトを作成し取りつけます。

このスクリプトはSearchAreaの範囲にコライダが入ったり出ていったりした時にゾンビの状態を変更する処理です。

主人公のインスペクタを表示しタグにPlayerを設定します。

ここまでの設定が終わったらUnityを実行し、ゾンビが主人公の近くに来たら止まる事を確認します。

ここまではまだ準備段階です。(^_^;)

以前の記事で敵が主人公を追いかける機能などを簡易的に説明した結果になりますね。

なので、詳細についてはそちらの記事を参照して頂くと言う事で・・・・。

ゾンビが主人公に追いついたら主人公が身動き出来ないようにする

では、ゾンビが主人公に追いついた時に主人公が移動キーを押しても動けないようにしてみましょう。

主人公操作スクリプトHoldCharacterMoveに処理を追加する

まずは主人公にも列挙型で状態変数を作成し、その状態によっては移動キーで移動が出来ないようにしていきます。

主人公操作スクリプトHoldCharacterMoveスクリプトに処理を追記していきます。

↑のように主人公キャラクターの状態を作ります。

また主人公がゾンビに捕まれた場合SPACEキーを何回押したら脱出出来るかの回数を指定するreleaseCountをpublicで宣言します。

キーを押した回数はpushCount変数に入れていきます。

Start関数内で変数の初期化を行っています。

Update関数内では主人公の状態に応じて処理を分け、今までの移動処理をしていた箇所は主人公が通常の状態(MyState.normal)の時だけ行う事にします。

主人公が捕まっている状態(MyState.hold)だった時はSPACEキーが押された回数を計測し、指定回数を超えたら自身の状態を変更し、捕まえているゾンビの状態も
変更しています。

捕まえているゾンビのゲームオブジェクトholdEnemyはSetState関数内で引数として受け取ったゲームオブジェクトを入れています。

主人公のSetState関数はゾンビが主人公を捕まえた時に呼び出しその時にゾンビ自身のゲームオブジェクトを引数として渡しています。

ゾンビが主人公を捕まえた時にSetState関数が呼ばれた時は主人公のアニメーションパラメータであるSpeedを0にしてIdle状態へと遷移させています。

これは捕まった時に主人公のアニメーションを立っている状態にさせる為です。

ゾンビ操作スクリプトHoldZombieMoveに処理を追加する

次はゾンビ側のスクリプトに処理を追加していきます。

まずは宣言部、Start関数、Update関数内です。

一部変数宣言は省略しています。

ゾンビが離れてから再び動き出すまでの時間をwaitTimeとします。

Update関数ではゾンビの状態がZombieState.waitHoldの時の処理を追加しています。

ZombieState.waitHoldはゾンビが主人公を捕まえた後、SPACEキーを特定の回数押して脱出した後のゾンビの状態です。

なぜこの状態が必要なのかと言うと離した後に一定の沈黙状態を作らないとすぐさまゾンビが主人公を捕まえてしまうからです(^_^;)

ゾンビの沈黙状態の時に実行する関数WaitHoldでは待ち時間を経過したらゾンビの状態をZombieState.waitに変更しています。

SetState関数のZombieState.hold状態の時に主人公側のSetState関数を呼び出して主人公が捕まっている状態に変更する処理を追加しています。

ZombieState.waitHold状態の時は捕まえていた主人公の情報を消したり自身のアニメーション状態をIdleへと遷移させています。

これでゾンビ側の操作スクリプトの修正は完了しました。

ゾンビが主人公をサーチするスクリプトSearchCharacterスクリプトの修正

主人公が捕まっている状態から脱出する事が出来るようになったので、SearchCharacterスクリプトで主人公を検知する処理も修正する必要があります。

なぜならこのままの状態だと主人公が離れた後すぐに捕まってしまうからです。

その為、ゾンビの状態によっては主人公を追いかける状態にしないようにします。

ゾンビがZombieState.waitHold状態の時も追いかけないようにしています。

これで主人公がゾンビに捕まった時にSPACEキーを指定回数押したら脱出し、ゾンビの状態も待ち状態(ZombieState.waitHold)になりました。

ゾンビは指定の秒数を経過し、サーチエリアに主人公がいる場合は主人公を追いかける状態へと遷移します。

ではここまでの機能を確認してみましょう。

HoldCharacterMoveの設定

HoldCharacterMoveでreleaseCountの数値を10にします。

HoldZombieMoveの設定

HoldZombieMoveでwaitTimeを3に設定します。

それではUnityの実行ボタンを押して確認してみましょう。

捕まって脱出する一連の流れ

↑のようにゾンビに捕まると主人公は動けなくなり、SPACEキーを10回押すとゾンビから離れます。

ゾンビの方は3秒間の待ち時間の後、サーチエリア内に主人公がいる為再び主人公を追いかけ始めています。

これで主人公がゾンビに捕まって一定のキーを押した時に脱出する一連の流れが出来上がりました。

ゾンビに捕まった時の主人公のアニメーションとゾンビの手が足を掴むようにする

ここまでで主人公が捕まってから脱出するまでの機能は出来上がりましたが、ゾンビに捕まっているのかどうかわかりません。

そこで主人公が捕まった時は主人公のアニメーションを捕まった時の物に変更し、ゾンビの方は主人公の足を手で捕まえるようにしてみます。

主人公のアニメーションを用意し両足にゾンビがつかむ場所を作成する

主人公のアニメーションの用意とアニメーターコントローラーの設定

まずは主人公が捕まった時のアニメーションは

主人公が捕まった時のアニメーション

↑のようなアニメーションを用意しました。

うーむ・・・なぜかモデルをゾンビにしている・・・・(+_+)

主人公がゾンビに捕まった時は右足と左足、どちらを捕まれたかによってアニメーションを反対にします。

主人公のアニメーターコントローラーを開き、右足を捕まれた状態と左足を捕まれた状態を作成します。

主人公のアニメーターに捕まれた状態を作成

↑のようにHoldRightとHoldLeftの状態を作り、先ほどのアニメーションを設定します。

アニメーションパラメータを追加する

アニメーションパラメータにBoolのHoldRightとHoldLeftを追加しておきます。

HoldRightとHoldLeftへの遷移条件は

HasExitTimeのチェックを外し

Idle→HoldRightはHoldRightがtrue
HoldRight→IdleはHoldRightがfalse
Idle→HoldLeftはHoldLeftがtrue
HoldLeft→IdleはHoldLeftがfalse

Run→HoldRightはHoldRightがtrue
Run→HoldLeftはHoldLeftがtrue

という条件を入れます。

アニメーションのブレンドは条件が成立したらすぐに捕まったアニメーションに遷移させるようにブレンドしておきます。

アニメーションを逆にする

HoldLeftの状態を選択しインスペクタを表示したらMirrorの項目にチェックを入れます。

Mirror項目にチェックを入れると本来のアニメーションとは左右逆になるので、先ほどのアニメーションでは左足が後ろに来るようになります。

これで主人公のアニメーターコントローラーの設定は終了です。

主人公の足にゾンビが掴む場所を作成する

次にゾンビが主人公を捕まえた時に手がくる位置を主人公の足に作成します。

手がくる位置を作成

Hierarchy上で右クリック→3D Object→Sphereを選択し、右足と左足の子要素に設定します。

3DオブジェクトのSphereを作成

それぞれのインスペクタでScaleを調整し、足の大きさより少し大きめになるぐらいにしておきます。

このSphereColliderのサイズを使って手を置く位置を計算します。

SphereColliderのIs Triggerのチェックを入れて物理的に当たらないようにします。

手がくる位置を視覚的に見えるようにした絵

↑が実際に設置したRightFootIKとLeftFootIKの位置と大きさになります。

わかりやすいように表示していますが、実際には見えないようにしたいのでRightFootIKとLeftFootIKのMeshRendererのチェックを外してください。

主人公操作スクリプトHoldCharacterMoveにアニメーション操作処理を追加

主人公の状態MyStateのholdを消して、holdRightとholdLeftという状態を作成します。

これに関連してUpdate関数内で主人公の状態がMyState.holdの時にしていた処理をMyState.holdRightとMyState.holdLeftの時に変更します。

次に状態変更関数SetStateの処理を変更します。

引数で受け取った状態がMyState.normalだった時は主人公が捕まった状態から脱出した時なのでアニメーションパラメータのHoldRightとHoldLeftをfalseにします。

MyState.holdRight、MyState.holdLeftだった時はアニメーションパラメータのHoldRightとHoldLeftをそれぞれtrueにしています。

これで主人公側の修正と追加が終わりました。

ここまででHoldZombieMoveスクリプトで主人公の状態をMyState.holdに変える処理が残っているのでエラーが出ています。

これからHoldZombieMoveに修正を加えていく事でエラーが出なくなるので安心してください。

ゾンビが主人公を捕まえた時に手を主人公の足に付ける

次にゾンビが主人公を捕まえた時にゾンビの手を主人公の足に付ける方法を考えます。

現時点ではゾンビが主人公を捕まえた時のアニメーションはうつぶせになっているだけのもので手は地面に付いています。

そこでIKを使って手の位置を主人公に設置したRightFootIKとLeftFootIKの位置に持っていく事でこれを実現させます。

IKを使うと手や足の位置を本来のアニメーションから独立して位置や回転を設定する事が出来るようになります。

IKを使用する事が出来るようにする

IKに関しては

UnityのIKを使って物を運ぶアニメーション
UnityのIKを使ってキャラクターが物を運んでいるアニメーションを作成していきます

等のIKに関する記事を参照してください。

まずはゾンビのアニメーターコントローラーでIK Passにチェックを入れスクリプトでOnAnimatorIKが呼び出されるようにします。

アニメーターコントローラーのIK Passにチェックを入れる

ゾンビのアニメーターコントローラーのBase Layerの歯車を押してIK Passにチェックを入れます。

これでスクリプトにOnAnimatorIK関数を記述すると呼ばれるようになります。

ゾンビ操作スクリプトHoldZombieMoveにIKの処理を追加する

アニメーターコントローラーでIKの設定をしたので、HoldZombieMoveスクリプトにOnAnimatorIK関数を記述しIKが働くようにします。

まずは変数宣言を追加します。

今までは主人公の位置とゾンビの位置がstopDistanceの距離より短くなった時に捕まえた状態にしてましたが、ゾンビの位置はtransform.positionとしており
ゾンビの足元からの距離になっていました。

そこでheadでゾンビの頭のボーンを指定し頭からの距離で計算するようにします。

またゾンビが掴む右足と左足も指定出来るようにし、headからrightFoot、leftFootまでの距離で捕まえたかどうかを判定するようにします。

catchRight変数は右足を掴んでいる場合にtrue、左足を掴んでいる場合はfalseにする変数で、どちらの足を掴んでいるか判定する時に使用します。

次はUpdate関数内のZombieState.chaseの状態の処理を変更します。

主人公の右足とゾンビの頭の距離がstopDistanceより短い時は右足を掴んだとし状態を変更します。

主人公のSetState関数を呼び出し掴んだ足によって主人公の状態も変更しています。

それ以外の時で主人公の左足とゾンビの頭の距離がstopDistanceより短かった時は左足を掴んだ状態としています。

これでゾンビが主人公を追いかけている時に距離が短くなったら右足か左足かを掴んだという状態に変更出来ます。

次にOnAnimatorIK関数を作成し、ゾンビが足を掴んだ時に手の位置を右足か左足に持っていく処理を記述します。

IKのウエイトの設定や位置、回転の情報を設定する方法はIK関連の記事を見てください。

簡単に説明すると右手、左手を合わす時にどの程度のウエイトをかけるか設定し(ウエイトは0~1)、その後に実際に右手と左手の位置と回転を設定しています。

右足を掴んでいるか左足を掴んでいるかで処理が多少変わりますが、やることがほとんど同じなのでcatchRightの値によってあらかじめオフセット値を計算しておき、

同じ処理で済むようにして記述が少なくなるようにしています。

オフセット値で計算しているのはtransform.right(ゾンビの右側に1m)にrightFoot、leftFootのSphereColliderに設定したradius(半径)を5で割った値をかけたものです。

つまりrightFootIKとleftFootIKの球の周りに手の位置を設定する為の処理です。

rightFoot、leftFootのScaleは0.2にしたのでその影響で5で割る必要があります。

右手と左手のIKを設定した後にゾンビの向きを主人公の足の方向に向けるとよりリアルな感じになると思い向きを変更するようにしました。

これで処理が完成しました。

Unityを実行し主人公が捕まった時の状態を確認してみましょう。

HoldZombieMoveの頭、右足、左足の設定をする

↑のようにHoldZombieMoveスクリプトのheadにゾンビの頭のボーンをドラッグ&ドロップ、rightFootに主人公のrightFootIK、leftFootに主人公のleftFootIKをドラッグ&ドロップします。

また距離を計算する時の位置をゾンビの頭にしたのでstopDistanceは0.5に設定し直しました。

それでは実行してみましょう。

ゾンビが足を掴むサンプル

サンプルではすごくよく出来ているように見えますが・・・・(^_^;)

ゾンビが主人公を捕まえる条件の距離は走っている時の足の位置ですが、捕まえた状態になると主人公は捕まったアニメーションに変更されます。

(主人公の足の位置が走っている状態の場所から捕まっている状態の位置に移動します)

つまりゾンビの捕まえ方によっては手が足に届かないこともあります。

またゾンビのCharacterControllerのサイズを小さくしたことで主人公のCharacterControllerの設定によってはゾンビの上に乗る事が出来てしまいます。

そうなると主人公がゾンビの上にいる状態でゾンビが足を掴むことになり手があらぬ方向に曲がってしまいます。

こうならない為にはゾンビのCharacterControllerのサイズを少し高めに設定するか、主人公のCharacterControllerの設定で乗れる段差Step Offsetの値を小さくする事でゾンビの上には乗らないように出来ると思います。

ゾンビの上に乗るようにはしたいけれど足を掴ませない為にはまた別の処理が必要ですね・・・・これに関してはがんばって作成してみてください。(^_^;)

これですべての機能が完成!!

といきたい所ではありますが、別の所で問題がまだ残っています。

ゾンビのアニメーションが立って歩くもので立ったまま主人公を捕まえる場合は考慮しなくてもいい問題なんですが・・・・。

ゾンビが地面に合わせて角度を変えて歩くようにする

ゾンビが主人公を掴む機能と主人公が脱出する機能は出来ましたが、実はゾンビが主人公を追いかける時に坂やデコボコ道を動く時に同じように水平のまま移動してしまいます。

これは今回の機能とは関係ないですが・・・・デコボコ道をずっと水平のまま歩くのは残念な感じがします。

ゾンビが坂で水平になる

↑のように坂を上る時も水平なままです。

これはtransform.LookAtで自身のY座標と同じ高さを見ているからですね。

さらに水平なままなので坂の上で主人公を捕まえると手が変な方向に曲がってしまいます。

HoldZombieMoveに処理を追加しゾンビが地面に合わせて角度を変えるようにしてみます。

ゾンビの下にある地面の向きをrotに入れるようにします。

またゾンビが主人公の足を掴む時に毎回GetComponentでHoldCharacterMoveスクリプトを取得してましたが、これをやめてStart関数内で取得しておきます。

↑のようにholdCharacterMove変数を介してHoldCharacterMoveスクリプトの関数を呼び出すことが出来るようにします。

次にUpdate関数内でゾンビから下方向にレイを飛ばし地面であったらその反射角度を求める処理を最初に入れます。

Physics.Linecastでゾンビから下方向に1mレイを飛ばし、ぶつかったゲームオブジェクトにBlockレイヤーが設定されていたらその情報をhitに入れます。

地面にはBlockレイヤー(名前が変な名前になっていますが地面だとわかりやすいGroundとか名前を自由に設定してください)を設定しておく必要があります。

hit.normalで衝突した面の向いている方向を取得できます。

次に今回のメインの処理であるゾンビの角度を地面の角度にする処理を追加します。

ゾンビが歩いていたり、主人公を追いかけている時、主人公を捕まえた時にゾンビの角度を変更します。

ゾンビの向いている方向はtransform.LookAtで歩いている時、主人公を追いかけている時に目的地を設定し、

主人公を掴んでいる時は足を指定します。

その後

transform.rotation = Quaternion.FromToRotation(transform.up, rot) * transform.rotation;

で上向きと地面の反射向きから角度を計算し、それに自身の角度をかけた後それを自身の角度にしています。

この処理はちょっと頭が混乱しますね・・・。

詳しくは

UnityのRaycastHit.normalとQuaternion.FromToRotationを使いこなす
UnityのQuaternion.FromToRotationで2つの方向から角度を求める事が出来ますが、その時の方向にPhysics.Raycastで得られたRaycastHit.normalの値を使う事が多々あります。このRaycastHit.normalとはなんなのか?を調べました。

の記事や

UnityのIKを使って足の位置と角度を地面に合わせる機能
Unityのゲームでキャラクターの両足が別の高さの地面の上にある時にそれぞれの足をそれぞれの地面につけるようにする機能を作成します

を参照してください。

これで処理が出来ました。

ゾンビに坂を登らせて確認してみましょう。

ゾンビの角度が変わるか坂を登らせて検証

↑のように坂に合わせてゾンビの角度が変わるようになりました。

平らな地面や坂だけのステージとは限りませんので、Terrainで作ったデコボコな地面で確認してみましょう。

テラインの地面を動かしてゾンビの角度を確認

↑のようにデコボコに合わせてゾンビの角度が変わりよりリアルな感じになりました!(^_^)v

これですべての機能が完成です!(^-^)

と言いたいところなんですが・・・、今回のゾンビを動かすスクリプトでは障害物があると引っかかって動けなくなってしまいます。

そこでゾンビを動かすスクリプトをナビゲーション機能を使った移動方法に変えて見ましょう。

ゾンビの移動をナビゲーション機能を使った移動に変更する

ナビゲーション機能を使うとナビメッシュした地面を効率よく移動する敵キャラを作成する事が出来ます。

ナビゲーション機能については

敵キャラをUnityのナビゲーション機能を使って移動させる
Unityのナビゲーション機能を使って敵キャラクターを移動させます。ナビゲーション機能はあらかじめBakeされたフィールドを移動出来る機能です。

等のナビゲーションに関する記事を参照して頂くと詳しく記述しています。

ゾンビにNavMeshAgentコンポーネントを設定する

まずはCharacterControllerを使用して動かしていたゾンビをNavMeshAgentで動くようにコンポーネントを変更します。

ゾンビのインスペクタからAdd Component→Navigation→NavMeshAgentを追加します。

ゾンビに設定したNavMeshAgentの設定

↑のようにゾンビのNavMeshAgentを設定します。

細かい設定をしてエージェントの動きを変更してみるといいと思います。

NavMeshAgentのサイズ

↑のようなNavMeshAgentのサイズになりました。

今まで使っていたCharacterControllerコンポーネントは使用しないので削除します。

ゾンビ操作スクリプトHoldZombieMoveをナビゲーション機能で移動するように変更

それではHoldZombieMoveスクリプトを変更していきます。

ゾンビの移動スピードに使用していたspeed変数は使用しなくなるので変数を削除し、NavMeshAgent変数を宣言してナビゲーション機能で移動するようにします。

またCharacterControllerのeCon変数も使用しなくなるので削除します。

Update関数内のCharacterControllerを使った移動処理の部分は使わなくなるので削除します。

velocity.y += Physics.gravity.y * Time.deltaTime;
eCon.Move(velocity * Time.deltaTime);

またspeedを使っていた箇所であるWalkChase関数内の処理も使用しないので削除します。

direction = (destination – transform.position).normalized;
velocity += direction * speed;

WalkChaseの↑の記述があった部分にはエージェントの目的地を設定するようにします。

ゾンビの状態変更関数SetStateではエージェントの再開と停止の記述を追加します。

ゾンビが主人公を追いかける状態になった時は目的地を主人公に設定しエージェントの再開をします。

ゾンビが主人公を掴んでいる時はエージェントの動きを停止します。

これでスクリプトの修正が終わりました。

地面をNavMeshしてエージェントが移動出来るようにする

最後にNavMeshAgentはNavMeshされた地面しか移動できないので地面をNavMeshしましょう。

地面のゲームオブジェクトをすべてCtrlキーを押しながら選択し、インスペクタの右上にあるstaticのチェックを入れます。

staticにチェックを入れる

staticは静的という意味で動かないゲームオブジェクトであることを意味します。

staticにはいくつか種類がありますが今回は大元のstaticにチェックを入れ全てチェックされるようにしています。

staticにチェックを入れたら

Navigationメニューを表示する

↑のようにUnityのメニューからNavigationを選択しNavigationタブを表示します。

Bakeの設定をする

NavMeshの設定をしてBakeボタンを押します。

NavMeshされた地面

↑がNavMeshされた地面です。

青くなっている部分がNavMeshAgentが動く場所になり白い部分は移動出来ません。

エージェントがうまく地面を動かない場合はその動けない部分がNavMeshされていない可能性があります。

さきほどのNavigationタブで設定を調整し再度Bakeする必要があります。

これでエージェントが動く地面の作成も終わりました。

ナビゲーション機能で動くゾンビを確認する

それでは最後にCharacterControllerで動くゾンビとナビゲーション機能で動くゾンビを比較して確認してみましょう。

まずはCharacterControllerを使ったゾンビの移動を見てみます。

CharacterControllerゾンビの移動

↑のように障害物があっても主人公の方向に進もうとする為障害物にぶつかったまま動けなくなります。

次にNavMeshAgentを使ったゾンビの移動を見てみます。

NavMeshAgentゾンビの移動

↑のように障害物があってもそれを避けて主人公を追いかけていきます。

敵のAIを作成する上でナビゲーション機能を使うと非常に簡単に作成する事が出来るのがわかります。(^-^)

ただ、障害物があってもゾンビが主人公の方向を向いてしまうのは少し問題ですね。

ゾンビと主人公の間に障害物があった時は主人公の方向を見ないで次の移動先を見るようにさせるといいかもしれません。

WalkChase関数内でゾンビから主人公にレイを飛ばし間にBlockレイヤーに設定したゲームオブジェクトがない場合だけキャラクターの方向を主人公に向けます。

ゾンビの位置や主人公の位置(transform.position)は足元になっているのでそこから少し上向きに調整した位置でレイを飛ばしています。

これは地面自体にレイが接触する間違いが発生する可能性もあった為、位置を調整しました(主人公とゾンビの段差が違いすぎる時は地面に接触する可能性あり)。

これで障害物が間にあっても主人公は追いかけるが主人公の方を見なくなります。

障害物がある時は主人公の方を向かせない

↑のように障害物がある時は主人公の方を向かなくなりました。

捕まった後に主人公が脱出した場合に違う方向を向いてしまいますが・・・・これはWaitHold関数内に処理を変える必要がありそうですね・・・。

--以降2017/01/06日にWaitHold関数に処理を追加--

WaitHold関数内で離した時にゾンビの傾きだけ更新していましたが、ここの処理を削除します。

そしてtransform.LookAtを使ってゾンビがキャラクターを掴んだ位置(agent.destinationは掴んだ時の最後の目的地)を指定します。

しかしここで今まで使っていなかったLookAtの第2引数が出てきます。

第1引数は向く方向を指定しますが、第2引数はゲームオブジェクトの上向きの方向を指定します。

その為rot(地面の向いている方向)を指定して体の向きを地面と合わせるようにしています。

--WaitHold関数内の処理追加ここまで--

同じような処理を使って障害物で主人公が見えなくなった時に追いかけるのをやめさせる事も出来ます。

Unityで主人公が敵から隠れた時は追いかけないようにする
Unityのゲームで主人公キャラが敵キャラから逃げ、壁等で見えなくなったら敵キャラクターは主人公を追いかけないようにします

↑の記事で障害物があった時に追いかけないようにする機能を作成しているので参考にしてください。

複数の敵がいた場合の対処

--2017/01/06に複数の敵がいた場合の処理を追加--

ここまでで機能としては出来ましたが、複数の敵がいる場合の対応をしていません。

そこで複数の敵がいた場合の対処をしていきます。

主人公が他のゾンビに掴まれていた時は主人公は追いかけるけど掴まないようにしていきましょう。

主人公操作スクリプトHoldCharacterMoveに処理を追加します。

主人公の状態を返す関数を作成しておきます。

次にゾンビ操作スクリプトHoldZombieMoveを修正していきます。

Update関数内の追いかける処理を記述している部分で主人公の状態を取得し他のゾンビに掴まれていなければ今までどおりの処理をします。

他のゾンビに掴まれている状態であり、指定距離より短くなったら、自分自身をZombieState.waitHoldに変更するようにします。

こうすることで主人公の近くにくるまでは追いかけるが足を掴まずその場で待機するようになります。

SetState関数内でZombieState.waitHoldの時の処理にagent.Stop()を追加します。

これは他のゾンビが主人公を掴んでいる時に自身はwaitHoldの状態にするので主人公に追いついた時に主人公は掴まないけど移動は止める必要があるからです。

複数の敵を出現させるとゾンビ同士が重なってしまう事が起こりますが、NavMeshAgentのサイズを少し大きくして対処するといいと思います。

今回の対処は一人の敵が掴んでいる時は他の敵は近くで一定時間待機という方法にしていますが、複数の敵に掴まれるような対処も面白そうですね!

実現できるかどうかはさておいてですが・・・・(^_^;)

最後に複数のゾンビをコピーして試してみましょう。

複数のゾンビがいる時の対処の結果

↑のようにゾンビに掴まれている時は他のゾンビは近くまでくるだけになりました。

--2017/01/06に追加した複数の敵がいる場合の対処終わり--

ユーザーが一定時間SPACEキーを押さなかったら押した回数をリセットする

--2017/01/11に一定時間キーを押さないと押した回数を初期化する処理を追加始め

今までの処理だとreleaseCountの回数分SPACEキーを押したらゾンビから逃げられましたが、連続で押さなくても指定回数押しただけで脱出が出来ました。

そこで前にSPACEキーを押してから一定時間キーが押されなければキーを押した回数をリセットし最初からやり直しさせるように処理を変えてみましょう。

主人公操作スクリプトHoldCharacterMoveを変更していきます。

最後にキーを押してから次のキーを押さなければいけない時間freezeTimeと最後にキーを押した時の時間lastTypeTimeを追加します。

Update関数内の主人公が捕まっている時の処理でゲーム開始からの時間(Time.time)から最後にキーを押した時間を引いてfreezeTimeを超えたら押した回数(pushCount)をリセットしています。

状態変更関数SetStateで主人公が捕まった時の処理にlastTypeTimeに初期時間を入れておきます。

これで機能が完成したので、インスペクタのfreezeTimeにキーを押さなければいけない時間(float値)を設定し確認してみてください。

--2017/01/11に一定時間キーを押さないと押した回数を初期化する処理追加終了--

これで本当にすべての機能が完成しました!

終わりに

敵が主人公の足を掴んで脱出するという機能の作成をする内に違う問題点(ゾンビが水平なまま移動してしまう)が発生して記事のボリュームが多くなりました・・・(^_^;)

キャラクターが立った状態で移動する場合は地面の角度を考慮しなくてもそんなに気にはなりませんが、

地面にうつ伏せになって歩く状態や人型キャラクター以外のモンスターなどを移動させる際には地面の角度とキャラクターの傾きを合わせる処理があるとリアルな感じが出ていいですね。

今回作成した機能のまま使用しても結構使えるとは思いますが、もっとこだわって作成するならゾンビの手がちゃんと主人公の足を掴むようにしたり、

主人公との位置関係によってゾンビの手があらぬ方向に曲がらないようにしていくといいですね。

また、今回のスクリプトだと一人の敵が主人公を掴むだけならいいんですが、複数の敵がいた場合に問題が出ると思います。

主人公操作スクリプトであるHoldCharacterMoveで主人公を掴んでいる敵情報であるholdEnemyではひとつの敵しか把握していないからです。

複数の敵を登場させるとおそらくこのholdEnemyがどんどん切り替わるようになって問題が発生すると思います。

こういった場合は主人公の状態が掴まれている状態だった時は敵キャラの方で掴まないようにしたり、複数の敵に掴まれる仕様にする場合は、

また色々面倒な処理が必要になりそうですね・・・・(^_^;)

ここら辺はがんばってみてください・・・・、わたくしは力尽きました(+o+)

2017/01/06に一つの対処法を追記しました。

今回の記事では他の記事へのリンクが多くあります。

それだけ色々な機能を把握しておく必要があるという事ですね。

キャラクターの移動、キャラクターのアニメーションの作成、アニメーションの制御、IK、角度の計算、ナビゲーション機能。

でも、これだけの機能を使いこなせれば他の複雑な機能を作ろうと思った時に応用が利くようになると思います。

それらの機能について当ブログで記事を書いているのでそちらも参考にしつつ、今回の機能の作成に取り組んでみてください(^^)/

スポンサーリンク

記事をシェアして頂ける方はこちら

フォローして頂くとやる気が出ます

コメント

  1. Mika より:

    おおおすごい!ずっと悩んでいた部分が見事に解消されました。( ;∀;)
    しかし、unityのアニメーション関連はまだまだトリッキー過ぎて難易度が高いですね。
    もっと簡単に実装できたら良いのになーと思います。ありがとうございました、早速試してみます、此処は正に神ブログです。m(__)m

    • 最大級の褒め言葉ありがとうございます(^^)/

      記事内でも触れていますが、ゾンビが複数いた時の対応等が必要なのでもう少し踏み込んで作ってみるべきでした・・・(+_+)

      そこら辺の対応はまた難しくなりそうですね・・・。

      2017/01/06に複数の敵がいる時の対処を追記しました。