Unityの敵の攻撃の実装
前回までの記事でPlayerが敵に攻撃するとHPゲージが減るようにした。前回までの記事↓
TPSのキャラ移動
ジャンプ
ダッシュ実装
自作キャラを動き回らせる
剣を振る方法
剣コンボ
足が浮く対策
剣の軌跡の作り方
燃える剣の必殺技
ステータス管理
ダメージ判定・処理
HPゲージ
今回は、敵の攻撃を作成し、Playerが喰らったら自身のHPゲージが減るようにしていく。
敵の攻撃の実装目次
敵の攻撃アニメーション設定敵の当たり&トリガー判定・軌跡
ステータス設定
敵の攻撃を実装
目次にもどる
敵の攻撃アニメーション設定
まずは敵の攻撃の作成を行うが、Playerを作成した時と基本的な作業は同じだ。最初に敵の3Dモデルが必要となる。自分が好きなものを使えばいいが、以下のように3Dモデルを選択し、RigタブのAnimation TypeがHumanoidとなっている3Dモデルを選択しよう(2足歩行のモデルなら、だいたいHumanoidなはず。Humanoidが適用できない骨構造の異なるモデルの場合は攻撃のアニメーションなどの実装が難しくなる)
自分は今回は以下のフリー素材を使うことにした(2023年3月現在はフリー)。
インポートしたスケルトンをプロジェクトビューからヒエラルキー欄にドラッグ&ドロップ。
その後、プロジェクトビューのわかるフォルダにドラッグ&ドロップしてプレハブ化する。
このスケルトンは最初から剣と盾を持っている。よって、Player作成の時は剣素材を別に用意したが今回は不要。
一方で盾は今回は使うつもりはない。プレハブ化したスケルトンを右クリックすることでスケルトンの子を全て確認できる。子のうちSHIELDについて今回は右クリック→Deleteで削除した(お好みで、削除しなくてもOK)。
攻撃のアニメーションは剣を振るアニメーションなら何でも問題ない(Humanoid対応のもの。まぁそうなっていると思うが)。
今回はスケルトンのフリー素材についているAnimationClipであるskeleton@Attack1h1とskeleton@Idle1を使うことにする(フリー素材のanimationフォルダにある)。
プロジェクトビュー→右クリック→Create→Animatorcontrollerを作成する。名前は今回はEnemy1とした。
以下のようにステートと遷移を作成しよう。
IdleステートのAnimationClipとしてskeleton@Idle1、AttackステートのAnimationClipとしてskeleton@Attack1h1を選択する。
今回はintパラメータのAttackを作成し、Attackパラメータが1になるとAttackステート、Attackパラメータが0になるとIdleステートになるように遷移を設定する。
遷移のHas Exit Timeのチェックは外しておこう(AnimationClipの全再生を待たずに遷移が行われる)。
続いてskeleton@Attack1h1の元となるFBXファイルを選択してインスペクター欄を見よう。
以前と同様に剣の振り開始時にAttackStart、振り終わりにHit、アニメーション終了時にAttackEndを呼び出すイベントを作成する。
また、Loop timeにチェックを入れておこう。
イベントでパラメータを変えても、Attackステート→Idleステートの遷移が上手くいかずAttackステートの最後でアニメーションが停止してしまうことがあり、それを防ぐためだ。
スケルトンのヒエラルキー欄を選択しインスペクター欄を見る。AnimatorのControllerの項目で作成したEnemy1を選択する。
目次にもどる
敵の当たり&トリガー判定・軌跡
スケルトンを選択し、ScaleをPlayerと同程度に調整。身体全体を覆うようにCapsule Colliderをつける。Rigidbodyもつけ、今回はMASSを55に設定しFreeze RotationのX,Y,Zにチェックをつけて向きによる重力が働かないようにする(Playerの攻撃を受けるために使用するので、敵の攻撃とは関係ない)。スケルトンの剣(子要素となっているSWORD)を選択しよう。Capsule Colliderをつけ、Is Triggerにチェックをつけてトリガー判定にして剣を覆うように配置する。Rigidbodyもつけ、Freeze RotationとFreeze RotationのX,Y,Zにチェックをつけて位置や向きによる重力が働かないようにする。
スケルトンの剣にTrailをつける。以前記事でPlayerの剣を振った際にTrailで軌跡が出るようにしている。Trailを選択し右クリック→Duplicateして複製した上で、ドラッグアンドドロップで持ってきてスケルトンの剣の子とすると速い。前の記事を見ていない場合やTrailがわからない場合は以下記事を確認してほしい(軌跡がいらないならこの作業は飛ばしてOK)
剣の軌跡の作り方
Playerか敵かを判別するため、PlayerにはPlayerのタグを設定。
同様にスケルトンにはEnemyのタグを作成し設定する。
Playerにもスケルトンと同様にCapsule ColliderとRigidbodyをつける(これまでの記事を見ているなら既についていると思うが)。
目次にもどる
ステータス設定
続いてスクリプトで敵が攻撃を行いPlayerにダメージが入るようにしていく。今回は敵から主人公までの距離が2m以内であれば敵が攻撃を繰り出すようにする。
以前ステータス管理でやったようにScriptableObjectの項目を増やす。わからない&忘れた人は以下の記事へ。
ステータス管理
今回は「Short Attack Range」と「Enemytime」という項目を新たに作成した。
「Short Attack Range」は近接攻撃を行える距離であり今回は2とした(2m以内にPlayerが入ったら攻撃するようにするため)。
「Enemytime」は敵が攻撃などの行動を判断する処理を行う間隔であり、今回は0.5とした。
目次にもどる
敵の攻撃を実装
今回はインターフェースとしてIEnemyとICharaAttackを作成して定義。IEnemy
ICharaAttack
IEnemyのEnemyAIkoudou()では敵の攻撃判断(後々は移動も)の処理を行っていく。
ICharaAttackのHitCount();やHitCountdown()では攻撃ヒット数について処理を行う(剣一振りで1ヒットとするようにしていく)。Attacktimekanshiは今回は使用しないが後々必要になる。
敵のスケルトンのゲームオブジェクトには5つのスクリプトをアタッチする。シリアル化しているので指定を行う。
・Enemystate1(ステートを決定)
・Enemykoudou(攻撃するかを判断)
・Enemykinsetu1(攻撃処理)
・CharaDamage(以前記事作成のスケルトン側のダメ処理)
・EnemyGaugemuki(以前記事作成のスケルトン側のHPゲージ)
そしてスケルトンの剣(子のSWORD)にはCharaAttack(攻撃か敵に当たったかどうかの処理)のスクリプトをアタッチする。
Playerにもスケルトンにアタッチしたのと同じCharaDamageをアタッチする。
まずはEnemystate1(ステートを決定)のスクリプトから見ていく。
Enemytimeの間だけコルーチンで待機後に処理を行う。
Attackパラメータの値が1以上で攻撃中ならreturn;で処理を終わらせる。
Nullチェックし、State = Enemykoudou.EnemyAIkoudou();としてIEnemyを定義したスクリプトのEnemyAIkoudouのメソッドを呼び出している。
IEnemyを定義しているのがEnemykoudouのスクリプト(攻撃するかを判断)であり、記述は以下の通り。
public int EnemyAIkoudou()メソッドの記述が処理されることになる。
Playerとスケルトンの位置から距離を出し、スケルトンのShortAttackRange以下かどうかを調べる。条件に当てはまればState=1、そうでなければState=0となり、それを戻り値(返り値)として返す。
再度Enemystate1(ステートを決定)のスクリプトを見よう。
State = Enemykoudou.EnemyAIkoudou();なので戻り値はStateに代入される。
switch文でStateが0の時にAttackパラメータを0にして停止、Stateが1の時Attackパラメータを1にして攻撃を行う。
攻撃が行われるとAnimationClipに設定したイベントがメソットの呼び出しが行われる。そのメソッドを記述しているのがEnemykinsetu1(攻撃処理)のスクリプトだ。
このスクリプトはICharaAttackのインターフェースを宣言しており、public int HitCount()とpublic void HitCountdown()のメソッドがあるが、この2つについては後述する。
まずvoid Start()メソッドだが、ここでTrailの無効化を行っている。
AnimationClipが再生されると、剣の振り始めにvoid AttackStart()が呼び出される。ここでTrailの有効化を行い軌跡が出るようにしている。そしてHcount = 1としており、これは残りのヒット数を示す。
剣の振り終わりにvoid Hit()が呼び出されTrailの無効化を行いHcount = 0としている。
アニメーション終了時にvoid AttackEnd()が呼び出されAttackパラメータが0となる。これにより、ステートの遷移が行われ停止状態となる。
スケルトンが剣を振った際にPlayerに当たったかどうか判定するのが、スケルトンの剣にアタッチしたCharaAttackのスクリプトである。
以前の記事でプレイヤーが敵を攻撃する処理を作った時と基本的な構造は同じだ。
大きく異なるのが、ヒット数確認の処理を入れていれている点。以前の記事の攻撃処理では剣の一振りで複数回ダメージが入ることがある問題があったが、それを解消している。
Hcount = AttackChara.GetComponent
HHcount = Hcount.HitCount();としており、Enemykinsetu1のHitCount()のメソッドを呼び出せる。メソッドの内容は以下の通り。
Hcountは前述したようにAttackStart()で1が代入され、Hit()では0が代入される。その値が返り値として返され、HHcountに代入される。
その後にif (HHcount <= 0)と条件分岐を行っており、HHcountが0以下であればreturn;で処理が終了し、ダメージ処理が行われない。
以前の記事と同様にvoid OnTriggerEnter(Collider other)で剣がコライダーに侵入した瞬間に処理が行われる。
ダメージ処理が行われることが確定したら、ダメージ処理の前にHcount.HitCountdown();としてEnemykinsetu1のHitCountdown()メソッドを呼び出している。
--Hcount;として値を-1することで、値は0となる(値には1が代入されているため)。剣が振り終わる前に敵が剣の当たり判定から出て再度入るようなことがあったとしてもHcountは0なので2回目のダメージは入らずにすむ。
damageable.Damage(ATK);としてPlayerのCharadamageスクリプトを呼び出す。スクリプトは以下の通り。
以前の記事でのPlayer→敵のダメージ処理スクリプトとほとんど変わってはいない。変更点はvoid Start()でScriptableObjectのdataを変数に代入しているので、public void Damage(int value)でのnullチェックがいらなくなったくらい。
計算式がATK-DEFでダメージ決定というかなり適当なものなので、後々変更の必要があるが、ひとまずこれで敵が攻撃し当たればPlayerにダメージが入り、PlayerのHPが0になれば死亡処理が行われる。
残りはPlayerのHPゲージの処理だ。敵のHPゲージは敵の真上にあったほうが都合がよかったが、Playerの真上にHPゲージがあると敵と重なり見づらくなる。
そこで今回はHPゲージを画面左下に固定していこうと思う。
HPゲージの作り方は以前の記事と変わらない。作っていない場合は以下の記事を参考に作ってほしい。
HPゲージの作り方
以上の記事を見て既に作っているなら、DuplicateしてドラッグアンドドロップしてPlayerの子にしよう。今回は名前をPlayerGaugeとした。
PlayerGaugeを選択し、CanvasコンポーネントのRender Mode(Canvasをカメラにどのようにうつすかの設定)の項目を変更する。選択肢は以下の3つである。
World Space
ワールド空間上の決められた位置にCanvasを配置する。カメラの描写範囲から外れれば見えなくなる。以前記事の敵に追従するHPゲージ作成で使用。
Screen-Space Overlay
カメラの描写範囲の最前面にCanvasを配置する。カメラとCanvasの間に他のゲームオブジェクトが入ってCanvasが見えなくなるといったことが起こらない。
Screen-Space Camera
画面描写範囲にCanvasを配置する。最前面という指定はなく、カメラとCanvasの距離をPlane Distanceの項目で決められる。
PlayerのHPゲージはUIと言えるもので重要な情報だ。見えなくなることを避けたいので、「Screen-Space Overlay」を選択する。
続いて、子であるSliderのインスペクター欄を開きRect Transformの以下の部分を選択しよう。
すると以下のような画面が出る。これは原点にする位置を決める画面であり、今回は左下の以下の部分を選択する。
こうすることで画面の一番左下がSliderにとっての原点となる。
例えば、Rect TransformのPos Xが130、Pos Yが80のように値を入力すれば、それは画面左下の原点からX方向に130ピクセル、Y方向に80ピクセルの位置となる。今回はこの値にしてみた。
長くなったが、以上のような作業を行えば、以下のようにPlayerが2m以内に入ったら敵の攻撃が行われPlayerに当たったらダメージが入って画面左下のHPゲージが減るようになった。
続きの記事は以下。
メッシュ変形での範囲攻撃