今回はUnityのゲームで敵キャラクターがダメージを受けた場所によってアニメーションを変更してみたいと思います。
あれ?これなら以前の記事でやったじゃん!(ー_ー)!!
というわたくしのブログを念入りに見てくれている方もいるかもしれません。
以前の記事


の記事ですね。
体の部位によってコライダを複数設置し細かく判定出来るようにしました。
しかし今回やるのはコライダを複数設置するのではなくキャラクターの移動に使うCharacterControllerやCapsule Collider1つでどこにダメージを受けたか判定していきます。
細かい当たり判定を必要とせず大ざっぱな当たり判定だけでいいけどダメージを受けた場所だけは把握したい時もありますよね?
そんな時の為にコライダ1つだけでダメージを受けた場所を把握出来るようにしておきます。
ダメージを与える敵キャラクターの準備
まずはダメージを与える敵キャラクターを準備します。
敵キャラクターのモデルにCharacterControllerを取りつけコライダのサイズを調整します。
敵キャラクターをキーボードで動かせるようにキャラクターにはスクリプトを取りつけています。
キャラクター移動のスクリプトに関しては今回はダメージを与える場所を変える為に使用するだけなのでキャラクターを動かせれば問題ありません。
キャラクターの移動に関しては

ここら辺りを見てください。
↑が敵キャラクターを床の上に設置した様子です。
コライダのサイズを大きくして確認する時にわかりやすくしておくのもいいかもしれません。
また敵キャラクターのLayerにはEnemyを設定しておいてください。
敵キャラクターのAnimatorを作成する
敵キャラクターのAnimatorに設定するAnimator Controllerを作成していきます。
Animator Controllerの作り方や設定に関しては

を参照してください。
新しくAnimator Controllerを作成しアニメーションパラメータに
↑のようにFloat型のSpeedとTrigger型のDamageForwardRight(前の右側)、DamageForwardLeft(前の左側)、DamageBackRight(後ろの右側)、DamageBackLeft(後ろの左側)のパラメータを作成します。
それぞれの状態を作成し、その場所に攻撃を受けた時にアニメーションが遷移するようにし条件を設定します。
今回はサンプルなのでAny State(どの状態からでも)からMake Transitionをしてそれぞれの条件がトリガーされたら遷移するようにしておきます。
それぞれのダメージ状態からはExitに遷移をさせHas Exit Timeにチェックを入れておきます。
Has Exit Timeにチェックが入っていればダメージアニメーションが終了したらExitにながれEntryへと遷移します。
敵を攻撃するスクリプトを作成する
次に敵を攻撃するスクリプトを作成していきます。
このスクリプトが敵のコライダのどの部分を攻撃したかを判定します。
ヒエラルキー上で右クリック→Create Emptyを選択します。
↑のようにGameObjectという空のゲームオブジェクトを作成し、そこに敵をクリックした時に攻撃(コライダの情報を取得)するスクリプトを作成し取りつけます。
Main Camera等に設定しても構いません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | using UnityEngine; using System.Collections; public class ClickDamage : MonoBehaviour { // 接触した相手のAnimatorを入れる private Animator animator; void Update () { if(Input.GetButtonDown("Fire1")) { RaycastHit hit; // メインカメラからクリックした位置にレイを飛ばす Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); // Enemyレイヤーが設定されているコライダと接触したら if(Physics.Raycast(ray, out hit, 1000f, LayerMask.GetMask("Enemy"))) { animator = hit.collider.gameObject.GetComponent<Animator>(); // 接触した位置を接触したゲームオブジェクトからのローカル座標で計算 var hitPoint = hit.transform.InverseTransformPoint(hit.point); // 接触したコライダの中心位置のローカル座標を取得 var colliderCenter = hit.transform.InverseTransformPoint(hit.collider.bounds.center); var isForward = false; var isRight = false; // コライダの中心より前が接触 if(hitPoint.z > colliderCenter.z) { Debug.Log("前側に攻撃を受けた"); isForward = true; } else { Debug.Log("後ろ側に攻撃を受けた"); } // コライダの中心より右側が接触 if(hitPoint.x > colliderCenter.x) { Debug.Log("右側に攻撃を受けた"); isRight = true; } else { Debug.Log("左側に攻撃を受けた"); } // コライダの中心より上側が接触 if(hitPoint.y > colliderCenter.y) { Debug.Log("上側に攻撃を受けた"); } else { Debug.Log("下側に攻撃を受けた"); } if(isForward && isRight) { animator.SetTrigger("DamageForwardRight"); } else if(isForward && !isRight) { animator.SetTrigger("DamageForwardLeft"); } else if(!isForward && isRight) { animator.SetTrigger("DamageBackRight"); } else if(!isForward && !isRight) { animator.SetTrigger("DamageBackLeft"); } } } } } |
マウスの左ボタンを押したらカメラからマウスを押した場所にレイを飛ばしEnemyレイヤーが設定されたコライダと接触したかどうかを調べています。
1 2 3 4 5 6 7 8 | animator = hit.collider.gameObject.GetComponent<Animator>(); // 接触した位置を接触したゲームオブジェクトからのローカル座標で計算 var hitPoint = hit.transform.InverseTransformPoint(hit.point); // 接触したコライダの中心位置のローカル座標を取得 var colliderCenter = hit.transform.InverseTransformPoint(hit.collider.bounds.center); |
で接触したコライダのゲームオブジェクトからAnimatorコンポーネントを取得し変数に入れています。
実際のゲームの場合は敵キャラクターのAnimatorの操作は敵キャラクターに設定したスクリプトで行うのがいいと思います。
コライダの情報はhitに入っており、hit.pointで接触した位置をワールド座標のVector3の値で得られます。
今回の場合はコライダの中心から見たローカル座標のVector3の値がほしいのでInverseTransformPointを使ってワールド座標からローカル座標に変換しています。
hit.transformでヒットしたコライダのTransformを得られ、そこからhit.point座標分を相対的に移動した位置がInverseTransformPointで得られます。
またコライダの中心座標はhit.collider.bounds.centerで得られるのでそれもローカル座標に変換しています。
コライダの中心位置はInverseTransformPointを使わずにCharacterControllerのCenterをそのまま使っても同じですが、InverseTransformPointの練習の為に計算しました。
コライダの当たった位置は接触した位置(hit.point)とコライダの中心(hit.collider.bounds.center)との位置関係から調べるという事になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | var isForward = false; var isRight = false; // コライダの中心より前が接触 if(hitPoint.z > colliderCenter.z) { Debug.Log("前側に攻撃を受けた"); isForward = true; } else { Debug.Log("後ろ側に攻撃を受けた"); } // コライダの中心より右側が接触 if(hitPoint.x > colliderCenter.x) { Debug.Log("右側に攻撃を受けた"); isRight = true; } else { Debug.Log("左側に攻撃を受けた"); } // コライダの中心より上側が接触 if(hitPoint.y > colliderCenter.y) { Debug.Log("上側に攻撃を受けた"); } else { Debug.Log("下側に攻撃を受けた"); } |
isForward変数はコライダの中心より前が接触したらtrue、後ろならfalse。
isRight変数はコライダの中心より右ならtrue、左ならfalseを入れます。
ローカル座標に変換した接触位置とコライダの中心位置を使ってコライダのどの部位に接触したかをコンソールに出力しています。
今回の場合は前後、上下、左右で判定するのでコライダの接触した位置は6か所で判定出来るようになっています。
1 2 3 4 5 6 7 8 9 10 11 | if(isForward && isRight) { animator.SetTrigger("DamageForwardRight"); } else if(isForward && !isRight) { animator.SetTrigger("DamageForwardLeft"); } else if(!isForward && isRight) { animator.SetTrigger("DamageBackRight"); } else if(!isForward && !isRight) { animator.SetTrigger("DamageBackLeft"); } |
最後にisForwardとisRightを使って前後、左右で敵キャラクターのAnimatorを操作しています。
上下も入れてもよかったんですが、正直なところ分けるだけのアニメーションの用意が出来なくて・・・・(^_^;)
今回設定したアニメーションもダメージアニメーションとは関係ないやつですしね・・・。
敵キャラクターをクリックして確認する
それでは敵キャラクターを動かしたり、クリックする位置を変えたりしてアニメーションやコンソールに表示される文字が変わるかどうか確認しましょう。
ちょっとわかり辛いですが敵キャラクターのクリックした位置によってコライダの前後、上下、左右のどの位置かを表示し、アニメーションが変わっているのが確認出来ます。
これで1つのコライダでダメージを受けた場所を特定する事が出来るようになりました。
本来であれば敵キャラクター側から見て前方左の上を攻撃されたとすれば頭を右に傾けるようなアニメーション。
前方左の下を攻撃されたら前につんのめるようなアニメーション。
後方左の上を攻撃されたら右前に倒れるようなアニメーション。
というように設定すると受けた場所によってアニメーションを変える事が出来ると思います。
今回はCharacterControllerのコライダを使って判定していますが、Coliiderを継承しているCapsule ColliderやSphereCollider等もboundsを持っているので、
同じようにコライダの中心からの接触位置を判定出来ます。
あ・・・YouTubeにチャンネル作ったのに動画をアップロードしなかった・・・(-_-)
レイを使わない場合の判定
ここまではレイを使って接触した位置がコライダの中心からローカルのどの位置にあるかで判断していますが、
主人公が剣を振った時にコライダのどの位置と接触したかを判断したい事もあります。
剣にAttackSwordスクリプトを設定します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | using UnityEngine; using System.Collections; public class AttackSword : MonoBehaviour { [SerializeField] private int damage; void OnTriggerEnter(Collider col) { if(col.tag == "Enemy") { col.GetComponent <Enemy>().TakeDamage (damage, col.ClosestPointOnBounds (transform.position)); } } } |
↑の例ではEnemyスクリプトのTakeDamageメソッドにダメージ数と剣の位置と接触したコライダの一番近い位置を渡しています。
TakeDamageメソッド内でダメージエフェクトをインスタンス化し、接触した位置に表示する事が出来るようになります。
1 2 3 | col.ClosestPointOnBounds (transform.position) |
↑で剣の位置(transform.position)と一番近い接触したコライダ(この場合敵の当たり判定のコライダ)のBounds(コライダを囲む四角形)の一番近いポイントが得られます。
こうすることで銃を撃った時にレイを使った接触位置だけでなく、剣で攻撃した時の接触位置も取得する事が出来ます。