Unityで敵キャラが主人公に近づいた時に攻撃をしてくる機能

今回は主人公との距離が縮まったら敵キャラが主人公を攻撃してくる機能を作ってみます。

前回までで主人公が敵キャラの検知エリアに入った時に追いかけてくるところまで作りました。

Unityで主人公キャラが近づいてきたら追いかける敵キャラを作る
Unityで主人公キャラクターが敵キャラクターに近づいたら敵が主人公に近づいてくるようにしたいと思います。

まずは前回まで敵の移動スクリプトをMoveEnemyとしていましたが、攻撃機能も取り付けるのでファイル名とクラス名をEnemyに変更します(面倒な方は特に変えなくてもいいです)。

それに伴いSetPositionスクリプトのMoveEnemyとしていた箇所をEnemyに変更します。

Asset Storeでアニメーション用のデータをインポートしましょう。

カテゴリで3Dキャラクターをチェックし、PricingのFree Assetsにチェックを入れ、Cartoon Zombieをインポートしました。

キャラクターに付いているattackとdamageいうアニメーションを使います。

スポンサーリンク
スポンサーリンク

敵キャラクターが攻撃をしてくる機能の概要

敵キャラクターが主人公に近づいた時に攻撃する為には前回作成した機能のように敵キャラクターが主人公を追いかける必要があります。

主人公を追いかけている状態で、一定の距離内に主人公がきたら敵は攻撃を繰り出します。

一回攻撃をした後も主人公がその場に留まっている場合は攻撃の範囲内にいるのですぐにまた攻撃をしてしまいます。

そこで一回攻撃をしたら次に攻撃するまでのフリーズ状態を作ることにします。

フリーズ状態が終わったら見回りに戻ります。

この時敵のSearchAreaのコライダ内にまだ主人公がいた時は

見回り状態(Walk)→追いかける状態(Chase)

と遷移し、さらに攻撃範囲内にいれば

追いかける状態(Chase)→攻撃状態(Attack)

と遷移させるようにします。

敵キャラが攻撃をする為の処理を作成

敵キャラクターの攻撃処理を作成していきます。

敵キャラの状態を追記

Enemyスクリプトに処理を加えます。

敵キャラクターの状態を増やします。

EnemyStateに攻撃状態と攻撃後のフリーズ状態を作ります。

Updateメソッドに敵キャラの攻撃遷移処理を追加

次は敵キャラが攻撃に遷移するスクリプト部分をEnemyスクリプトのUpdateメソッド内に追加します。

攻撃後のフリーズ状態を続ける時間をインスペクタで設定出来るようにします。

状態がEnemyState.Chaseの時に主人公と1mの距離内に入ったらEnemyState.Attack状態にします。

距離の計算はVector3.Distanceで計算出来ます。

SetStateメソッドに攻撃状態と攻撃後状態を追加

EnemyスクリプトのSetStateメソッド内にEnemyState.Attack状態と攻撃後のEnemyState.Freeze状態へ変更する処理を追加します。

アタック状態、フリーズ状態ともにvelocityの値をVector3.zeroで0にして、移動処理を行わないようにします。

またアニメーションパラメータのSpeedも0にしておきます。

SetStateでEnemyState.Freeze状態に変更されるのは攻撃アニメーションが終わった時にイベントとして受け取ったメソッドで行われます。

アニメーターコントローラーで攻撃の状態と遷移を作成

次に敵のAnimatorControllerの設定をしていきます。

まずはアニメーションパラメータAttackをBoolとして作成します。

敵のAnimatorに攻撃状態を作成

新しいアニメーションのatack01をアニメーターウインドウ上にドラッグ&ドロップし、Walk、Idleを選択し右クリックしてatack01にMake Transitionします。

アニメーション遷移条件を設定する

ConditionsにAttackを追加し、WalkとIdleからatack01への遷移条件に『Attackがtrue』の時を入れます。

遷移のインスペクタでHas Exit Timeのチェックを外しておきます。
このチェックが入っていると条件がOnになっても元のアニメーションの再生が終わらないと次のアニメーションに行きません。

atack01からIdleへMake Transitionを繋げます。インスペクタ上のHas Exit Timeのチェックを外し、条件にAttackがfalseになった時を入れておきます。
攻撃が終了したら何もしていない状態に遷移させる為です。

敵キャラの攻撃の当たり判定部分(コライダ)を作る

アニメーターコントローラーの設定が終わったので、次は敵キャラの攻撃の当たり判定部分を作成していきます。

プレハブを修正するので、一旦、敵キャラのプレハブをヒエラルキー上にドラッグ&ドロップし、修正します。

攻撃の辺り判定部分にコライダを設定します。

右手に攻撃用のコライダを設定

↑のように敵キャラの右手の部分にSphereColliderを追加します。

SphereColliderはスクリプトでOn・Offするので最初はチェックを外しておきます。

敵の手に攻撃の当たり判定をつける

AttackZombieという名前で新しいスクリプトを作成し、SphereColliderの設定してある右手の手の部分に設定します。

AttackZombieスクリプトは後で作成します。

実際の手の攻撃の当たり判定

SphereColliderを上のような感じで設定し当たり部分を大きめに取ります。
この部分が主人公のCharacterControllerのコライダに当たったら攻撃が当たったという処理をします。

修正が終わったら敵キャラのインスペクタ上にあるPrefabのApplyボタンをクリックしプレハブに反映させておきます。

反映させたらヒエラルキー上の敵キャラはいらないのでチェックをはずすか削除しておきます。

アニメーションクリップの設定をする

Assets→Toon Zombie S→Model→zombie anime.fbxを選択し、インスペクタでRigがHumanoidになるようにし、次にAnimationタブをクリックします。

Attack用のアニメーションRigをHumanoidにする

atack01を選択したら赤く印をつけた場所にチェックを入れます。

アニメーションの動きによってオブジェクトに回転や移動を加えないようにし、スクリプトで制御する為です。

アニメーションのPoseにチェックを入れる

アニメーションイベントを作成する

次はアニメーションの再生位置でイベントを発生させるようにしてみます。

アニメーションイベントを追加する

上のようにEventsの所をクリックし、下のマークをクリックします。

アニメーションイベントの設定をする

新しいウインドウが出てくるのでAttackStartとAttackEnd、StateEndというイベントを作り、赤でマークをした位置に移動させます。

これは攻撃判定開始時と判定終了時、攻撃状態の終了を設定する為にこの位置にしています。(微調整してください)
FloatやIntはそのイベント発生時に渡せる引数の値です。特に渡さない場合は変更しなくて大丈夫です。

これでこのアニメーションが再生され、イベントの個所にくるとAnimatorが設定されているオブジェクトにAttackStart、AttackEnd、StateEndというイベントが送られます。

このイベントを受け取るスクリプトはあとで作成します。

これでアニメーションの途中でイベントを投げる事が出来るようになりました。

アニメーションイベントを受け取り、当たり判定のオン・オフをするスクリプト

敵のゲームオブジェクトにProcessEnemyAnimEventスクリプトを新しく作成し取り付けます。

ProcessEnemyAnimEventスクリプトはアニメーションのイベントを受け取るスクリプトになります。

インスペクタでSphereCollider(手に取り付けた攻撃の当たり判定用コライダ)を設定出来るようにします。

Startメソッド内でEnemyスクリプトを取得しています。

AttackStartとAttackEndはさきほど設定したアニメーションから送られてくるイベントを受け取りますので、AttackStartを受け取ったらコライダをOnにし、当たり判定を有効にします。

AttackEndを受け取ったら、コライダをOffにし、当たり判定を無効にします。

攻撃後(StateEnd)はEnemyState.Freezeに設定します。

アニメーションイベントの問題?

このアニメーションイベントで全てうまくいきそうですが、場合によっては呼ばれない事があります。

それは例えば敵が攻撃開始後すぐに主人公に攻撃を受けて攻撃アニメーションのアニメーションイベント部分までの再生がされず、ダメージアニメーションへ遷移してしまう時です。

AttackEndが呼ばれないと敵の手のコライダがオフにならないままになってしまいます。

そんな時はビヘイビアを使用するとうまくいきます。

Unityでアニメーションイベントが実行されない時の為にビヘイビアを設定しておく
Unityでアニメーションを即時遷移させているとアニメーションイベントが実行されない時があります。そんな時にビヘイビアを設定しておくと確実に処理を実行する事が出来ます。

もしくは攻撃アニメーションが最後まで再生されてから次のアニメーションを再生するように遷移条件のHas Exit Timeにチェックをして攻撃アニメーションを最後まで再生させます。

攻撃の当たり判定をするスクリプト

次はAttackZombieスクリプトです。

Playerタグを持つインスタンスに当たったらキャラクター操作スクリプトCharacterScriptのTakeDamageメソッド(ダメージ処理メソッド)を実行させます。

引数としてこのスクリプトが設定されているオブジェクトのルート(敵キャラ)のTransform情報(位置情報)を渡しています。

つまり主人公キャラのCharacterScriptスクリプトのTakeDamageメソッドに敵キャラのTransform情報を渡すという事になります。

これで敵キャラの攻撃の処理が出来ました。

ただ、攻撃された主人公キャラ側の処理をまだ作っていないので、攻撃されても主人公は棒立ち状態のままです。

主人公を検知するSearchCharacterスクリプトの修正

敵キャラクターの子要素のSearchAreaに取り付けた主人公検知スクリプトのSearchCharacterスクリプト内を修正します。

以前は敵キャラクターがEnemyState.Chase状態以外の時のチェイス状態にしましたが、EnemyState.WaitもしくはEnemyState.Walk状態の時だけチェイス状態にするようにします。

主人公側の攻撃を受けた時の処理

主人公側の攻撃を受けた時の処理を作成していきます。

まずは攻撃を受けた時の処理を主人公操作スクリプトに追加します。

さきほど攻撃を受けた時にTakeDamage関数を実行する処理を記述したので、主人公側で攻撃を受けた時の処理を書いていきます。

まずはフィールド部分です。

主人公側でも今何をしている状態か?を列挙体と記憶しておくフィールドstateを作ります。

TakeDamageメソッドが呼ばれたら、状態stateをダメージを受けている状態MyState.Damageに変更し、主人公キャラのアニメーションパラメータのDamageトリガーをOnにします。

また攻撃を受けた際に敵の向いている方向に動かしたい時は、コメントアウトしている部分を有効にします(今回は攻撃を受けても動かしません)。

主人公のアニメーターコントローラにダメージ状態と遷移を作成する

主人公キャラクターのAnimatorControllerを修正します。

主人公にDamage状態を追加したAnimatorController

敵キャラのアニメーターコントローラーとほぼ同じで、アニメーションパラメータにTrigger型のDamageのパラメータを作成し、
Damage状態を作り、DamageトリガーがOnになったらAny StateからDamageに遷移するように作ります。

(今回の記事とは関係ないですが、Walk→IdleのSettingsのInterruption SourceをNext Stateに変更し、何もしていない状態から歩き状態への遷移をスムーズにします)。

Any State→Damageの遷移条件はHas Exit Timeのチェックを外し、Damageがトリガーされた時にすぐに遷移するようにします。

Damage→Idleの遷移の遷移条件はHas Exit Timeにチェックをします。

Damage→Idleへの遷移条件

Damage→Walkの遷移条件はHas Exit Timeにチェックをし、SpeedがGreaterで0.1を設定します。

Damage→Walkの遷移条件

Damage→他の状態への遷移はHas Exit Timeにチェックをいれているのでダメージアニメーションが終わるまでは次の状態へ遷移しません。

なので主人公のダメージアニメーションに設定したアニメーションイベントのEndDamageが発生することになります。

主人公用のダメージアニメーションにアニメーションイベントを追加

主人公のダメージアニメーションでイベントを発生させておきます。

ダメージアニメーションは最初にインポートしたCartoon Zombieのdamageアニメーションを使用します。

DamageEndアニメーションイベントを追加する

アニメーションが終了した時にEndDamageイベントを発生させています。

主人公キャラクターのアニメーションイベントを受け取るスクリプトの作成

主人公キャラクターのダメージアニメーションにアニメーションイベントを設定したので、このイベントを受け取るスクリプトを作成します。

ProcessCharaAnimEventスクリプトを新しく作り主人公のゲームオブジェクトに取り付けます。

主人公はダメージを受けた後はMyState.Normalに設定します。

キャラクター操作処理部分を修正する

次に主人公操作スクリプトのUpdateメソッド内を修正します。

ダメージを受けている時に方向キー等を押した場合、ダメージを受けながら移動してしまいますので、そうならない為の条件を加えます。

キャラクターの状態がMyState.Normalの時だけ移動処理を行います。

終わりに

これで敵の攻撃と主人公が攻撃を受けた時の処理が出来ました。

敵が主人公を攻撃するサンプル

当たり判定の大きさと主人公キャラとの距離の調整をすればそれなりに見えるんではないかと・・・・・。

今回の作業はいろいろな個所の設定が必要なので、慣れてない人はどれがどれだか解らないという状態になるかもしれません。

説明下手なもので、飛び飛びの解説になってしまいました・・・・。(;_;)

次回は攻撃されてばかりではストレスがたまるので主人公も攻撃を出来るようにしてみます。

コメント

  1. kokoiti より:

    こんばんは、いつもお世話になってます。
    敵が攻撃したあとウエイトに移行せず攻撃のモーションのまま固まります。それとAttackZombi・ProcessEnemyAnimEvent・ProcessCharaAnimEventのスクリプトを原文そのままunityにいれてもエラーが起きます。どうすればいいですか。

  2. 匿名 より:

     こんばんは。いつもお世話になっております。
     少し質問しても宜しいでしょうか。この記事の通りにやったのはいいのですが、色々と問題が起こっています。

    ①敵が近づいて攻撃した後、ウエイトに移行せず攻撃のポーズのまま動かなくなる(その後は攻撃のポーズのまま近づいてくる)

    ②ProcessEnemyAnimEvent・ProcessCharaAnimEvent・AttackZombiのスクリプトをUNITYに入れるとエラーが起きる(スクリプトはこの記事の原文そのまま)

     色々試してみましたがどうしてもできませ出来ません、よろしくお願いします。

  3. カーマイン より:

    はじめまして、Unityを始めたばかりの超初心者です。
    とてもわかりやすく、参考にさせてもらっているのですが・・・
    記事通りにスクリプトを記入しても、「敵キャラが出現した瞬間にAttackを行う」「主人公キャラが近づいてもAttackをしてこない」状態になってしまいます。
    何が悪いか見当付くでしょうか・・・?
    教えていただけるとありがたいです

  4. 春の水 より:

    いつも楽しく閲覧させてもらっています。

    3点ほど気になる箇所がございましたのでコメントさせていただきます。

    1点目は

    「主人公用のダメージアニメーションにアニメーションイベントを追加」章の
    “”アニメーションが終了した時にDamageEndイベントを発生させています。””の箇所です。

    解説スクリプトの都合上、
    ここの名前をEndDamageにしなくては永遠にstateがNormalにならない問題がありました。

    2点目は

    「アニメーターコントローラーで攻撃の状態と遷移を作成」章で
    ParametersでAttackを追加した際に横にある□にチェックを入れないと主人公キャラが攻撃された際、当たり判定周りが動かず、躓きました。

    3点目は

    「アニメーションイベントを受け取り、当たり判定のオン・オフをするスクリプト」章上で

    上記2つは現時点では特に必要がないように感じました。

    以上になります。
    2点目に関しては過去の記事で解説されている可能性があるかもしれないので
    見逃していたら申し訳ございません。

    今後も頑張ってください。楽しみにしています。

    • まずは1点目

      EndDamageとDamageEndはいつのまにやら逆に書いていたようです・・・(^_^;)

      これは修正しました。

      次に2点目

      「アニメーターコントローラーで攻撃の状態と遷移を作成」の部分はアニメーションパラメータのAttackに最初からチェックを入れておくということでしょうか?

      主人公が攻撃された際ということなので、ゾンビの手の攻撃用のコライダのオン・オフがされていないということで当たり判定がされていないとしたらアニメーションイベントの受け取りがうまくいっていないのかもしれません。

      アニメーションイベントの受け取りがされているかどうかをAttackStartメソッド内等でDebug.Logを使って処理が行われているかどうかを判定してみるといいかもしれません。

      次に3点目

      スクリプトはC#に書き換えた時に前後の記事の機能を一緒に作っていたりしたので、使っていないものが載っていたりすることもあります。(-_-)

      またブログの当初は機能を積み上げていく形で記事を作っていたのですが、現在は個別にしているのでその影響もあってその記事内で使っていないものが載っていたりもします。(^_^;)

タイトルとURLをコピーしました