Unityのダメージ判定・処理
前回までの記事で剣で斬りつける動作を行えるようにし、ステータス管理も可能となった。.jpg)
前回までの記事↓
TPSのキャラ移動
ジャンプ
ダッシュ実装
自作キャラを動き回らせる
剣を振る方法
剣コンボ
足が浮く対策
剣の軌跡の作り方
燃える剣の必殺技
ステータス管理
ステータスができたのでそれを利用してダメージ判定・処理を行っていこうと思う。
今回の記事では、剣で敵を斬りつけた際にダメージを与えられるようにしていく。
ダメージ判定・処理目次
剣をトリガーにするスクリプト記述
目次にもどる
剣をトリガーにする
今までの記事により、Playerの子要素として持っている剣をトリガーにしていく。Playerの子要素となっている剣を選択しCapsule Colliderのコンポーネントを追加する。インスペクター欄でCapsule Colliderの位置と大きさを調整し剣の刀身を覆う程度に合わせよう。
.jpg)
続いてCapsule ColliderコンポーネントのIs Triggerの項目にチェックを入れよう。
.jpg)
Is TriggerとはCollider系コンポーネントの項目の一つ。チェックを入れることで「当たり判定」を「トリガー」に変更することができる
Is Triggerにチェックを入れるとゲームオブジェクトは物理的挙動を行わなくなる。例えばCube(Colliderがついているゲームオブジェクトなら何でもいい)を適当に作成し配置しよう。
Capsule ColliderをつけIs Triggerなしの剣で斬りつけると、剣とCubeが物理的に衝突することでPlayerが後ろに後退する(親であるPlayerにはRightbodyがついているため)。これが通常の当たり判定だ
Capsule ColliderをつけIs Triggerにチェックをつけた剣で斬りつけると剣がCubeをすり抜けて物理的衝突は起きない。これがトリガーだ。
最低でもぶつかるゲームオブジェクトのどちらかにIs Triggerがついていればトリガーにすることができる。
今回は剣を振った範囲に敵が入る→処理を行うという仕様にしたい。斬りつけて自身が後退するのは硬い敵に対してはありかもだが、基本的には後退では不自然だ。よってトリガーとするためにIs Triggerにチェックを入れている。
トリガーはこの他、ゴール地点まで移動した場合、指定位置までくるとワナが発動するといった場合など様々な場面で使える。
当たり判定かトリガーかで使える関数も変わってくる。トリガーの場合はOnTriggerの関数を使用可能だ(ちなみに当たり判定の場合はOnCollision)。
OnTriggerの関数を使うにはトリガーとなっているゲームオブジェクトまたはもう一方のゲームオブジェクトのどちらかにRigidbodyがついている必要がある。加えてどちらのゲームオブジェクトにもコライダー系コンポーネントは必要だ。
よって今回は剣のほうにRigidbodyのコンポーネントも追加する。位置と回転について重力の影響を受けないように以下の部分にチェックを入れておく。

OnTriggerの関数には以下の3種があるので覚えておこう。
OnTriggerEnter
トリガーのゲームオブジェクトに侵入した瞬間に呼び出される。
OnTriggerStay
トリガーのゲームオブジェクトに侵入している間に呼び出される。
OnTriggerExit
トリガーのゲームオブジェクトから脱出した瞬間に呼び出される。
目次にもどる
スクリプト記述
ダメージ処理はとても汎用性が高く頻繁に使うことになる。自分が敵に攻撃した時(今回の記事の目的)はもちろん、敵に攻撃された時、罠に引っかかった時、箱などの障害物を作成する時など様々な場面でダメージ処理を行うことになる。
Player、敵、障害物など様々なゲームオブジェクトのスクリプトに同じダメージ処理を記述するのは非効率だ。
例えばゲームオブジェクトにタグ「Enemy」をつけておき、OnTrigger関数を使ってトリガー侵入を感知し、ゲームオブジェクトがタグ「Enemy」だったらダメージ処理を行う方法もある。
だが、それでは「Enemy」の他に例えば「Boss」や「Wall」を追加したいとなった時に大きな変更が必要になる。
よって、対象を抽象的にした上でダメージ処理を呼び出すのが理想だ。
そんな場合はインターフェースを使うといい。
インターフェースとは関数の宣言を並べたものだ。
新たなスクリプトを作成しIDamageableと名前をつけよう(インターフェースを記述する際は最初にIをつけるという決まりがある)。そして以下のように記述してほしい。
このスクリプトで行っているのはIDamageableというインターフェースの定義だ。
public void Damage(int value);としてDamage関数、public void Death();としてDeath関数を作り宣言しているのみとなっている。
他のスクリプトでIDamageableを継承して使っていくことになる。IDamageableを継承した場合、Damage関数とDeath関数のメソッドを必ず作らなければならなくなる。
IDamageableはインターフェースとなり、他ゲームオブジェクトにアタッチする必要はない(というかできない)。性質的にも通常のスクリプトとは別物なのでinterfaceといった別のフォルダを作って、そこに集めておくのをおすすめする。
続いて剣で敵を斬った際の処理を行う。
以前までの記事でステータス管理をScriptableObjectで可能にした。今回は斬りつけた際の攻撃力が欲しい。
主人公用のAssetファイルを作成し今回はPlayer1と名前をつけた。ステータスは適当でいいが、ATK(攻撃力を示す)の項目を作っておこう。自分はATKを800とした。
.jpg)
また、敵用としてAssetファイルを作成し今回はMazokusoldierと名前をつけた。ステータスは適当でいいが、MAXHP(最大体力)とDEF(防御力)の項目を作っておこう。自分はMAXHPを1000、DEFを300とした。
.jpg)
新たにPlayerAttackのスクリプトを作成し以下のように記述する。今までの記事で作成したPlayerの子の剣のゲームオブジェクトにアタッチしよう。
[SerializeField] private charadata charadata;としてシリアル化している。Player1のAssetファイルを指定しておこう。
次にvoid OnTriggerEnter(Collider other)というメソッドを作っている。ようやくOnTriggerEnterの出番だ。他のゲームオブジェクトに侵入した瞬間に呼び出される。接触したゲームオブジェクトが引数otherとして渡される。
IDamageable damageable = other.GetComponent
if (damageable != null)で条件分岐しdamageableがnullではないことを確認(nullは無効な値を示す)。
条件に当てはまったらdamageable.Damage(charadata.ATK);として、damageableのDamageメソッドを呼び出し、その際、引数としてPlayer1のATKを送る。これでダメージ判定はOKだ。
続いて、ダメージを受ける敵として何でもいいのでゲームオブジェクトを作成しておく。それに合わせてコライダー系コンポーネントとRightBodyコンポーネントをつけておこう。
CharaDamageという名前のスクリプトを作成し以下のように記述し敵ゲームオブジェクトにアタッチする。
public class CharaDamage : MonoBehaviour, IDamageableとしており、IDamageableのインターフェースを継承している。
[SerializeField] private charadata charadata;としてシリアル化している。MazokusoldierのAssetファイルを指定しておこう。
Startメソッドではcharadataのnullチェックをした上で、MazokusoldierのMAXHPを変数HPに代入している。
続いてはDamageメソッドとDeathメソッドの記述である。この2つはIDamageableのインターフェースを継承しているので必ず記述せねばならない。
まずはDamageメソッドから。public void Damage(int value)と記述している。先ほどPlayerAttackのスクリプトでATKを引数にしてこのスクリプトのDamageメソッドを呼び出せるようにしており、valueにATKの値が入ることになる。
後はcharadataのnullチェックをした上でHP -= value - charadata.DEF;としている。このダメージ計算式はPlayerのATKからMazokusoldierのDEFを引いた値をHPから引くというあまりに適当な式である(DEFのほうが高いとHP増える)。今回はひとまずダメージ判定・処理を行ってみることを目的にしているので、この記述としている。
計算後、if (HP <= 0)と条件分岐し、当てはまった場合はDeathメソッドを呼び出す。
Deathメソッド内ではDestroy(gameObject);となっており、敵ゲームオブジェクトが破壊される(つまり消える)。
以上のような作業を行えば、以下のように斬りつけて敵ゲームオブジェクトのHPを0にすると、敵が消えるようになる。
インターフェースで抽象化することにより、敵だろうが障害物だろうがひとまずDamageメソッドを呼んでおけばOKとなるのが大きな利点。
また、インターフェースによりDamageメソッドとDeathメソッドがなければいけないということがメソッドの書き忘れを防いでくれる。
もちろんダメージ計算式が適当な点、敵を倒した時に消えるだけである点、ATKしか送れず魔法攻撃時にはどうするか、剣を振ってなくてもダメージが入るなど追加するべき点は多数ある。だが、ひとまずダメージ判定・処理は完了した。
続きの記事は以下。
HPゲージ作成