主人公が敵に攻撃を与えた時に
で敵のHPのメーターを減らすという事をしました。
今回は敵のHPのメーターを減らすのではなく敵に与えたダメージを表示するようにしてみます。
また、敵が攻撃を受けた体の部位にダメージのUIを表示するようにし、UIを上の方に動かしながら段々見えなくなるようにし、ダメージUIの表示が邪魔にならないようにします。
今回の機能を作成すると、
↑のような感じでダメージを与えた場所からダメージUIが登場し、ダメージUIが常にカメラの方向を向くようになっています。
攻撃を受ける簡易的な敵キャラクターの作成
まずは簡易的な敵キャラクターを作成していきます。
上のようにヒエラルキーで右クリック→Create Emptyで空のゲームオブジェクトを作成し、名前をKakasiとします。
Kakasiの子要素にヒエラルキーで右クリック→3D Objectで頭、体、右手、左手、右足、左足をSphereとCapsuleで作成します。
それぞれ名前をHead、Body、RightHand、LeftHand、RightFoot、LeftFootとします。
簡易的な敵キャラクターはご自由に作成してください。普通の人型3Dオブジェクトを使用し、コライダを分けて作成してもOKです。
KakasiにはAdd Component→Physics→CharacterControllerを取りつけます。
またTakeDamageスクリプトを作成し取り付けておきます。
かかしの体のコライダのIs Triggerにチェックを入れ物理的に当たらないようにします。
上がKakasiの子要素のBodyのインスペクタです。
Enemyという新しいタグを作成し、Bodyのタグを変更します。
Kakasiの他の子要素も同じようにタグをEnemyに設定しておきます。
上が出来上がった敵キャラクター「かかし君」です。
人型キャラクター全体を覆うCharacterControllerのコライダと体の部位ごとにコライダが設定されています。
ここで作成した「かかし君」はその場に立っているだけにしておきます。
かかしに設定するTakeDamageスクリプトの作成
かかしが主人公の攻撃を受けた時の処理をTakeDamageスクリプトに書きます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | using UnityEngine; using System.Collections; public class TakeDamage : MonoBehaviour { // DamageUIプレハブ [SerializeField] private GameObject damageUI; public void Damage(Collider col) { // DamageUIをインスタンス化。登場位置は接触したコライダの中心からカメラの方向に少し寄せた位置 var obj = Instantiate<GameObject>(damageUI, col.bounds.center - Camera.main.transform.forward * 0.2f, Quaternion.identity); } } |
インスペクタにはダメージ用のUIを設定します(後で作成します)。
主人公から攻撃を受けた時にDamageメソッドを呼び出しますが、その中でダメージ用のUIをインスタンス化し、受け取ったコライダの中心からカメラの向いている方の反対側に0.2移動した地点で登場させます。
ダメージを表示するUIの作成
次にダメージを与えた時に表示するダメージUIを作成していきます。
ヒエラルキーで右クリック→UI→Canvasでキャンバスを作成し、名前をDamageUIとします。
子要素に右クリック→UI→Textとしダメージ用のテキストを作ります。
RectTransformのScaleを調整してUIの大きさを変更します。
DamageUIのインスペクタのCanvasのRender ModeをWorld Spaceに変更し、Add Componentから新しいスクリプトDamageUIを作成し取りつけます。
Render ModeをWorld Spaceに変更するとUIが他のゲームオブジェクトと同じように扱う事が出来ます。
DamageUIスクリプトはUIの移動やカメラの方向を向かせたり、UIの透明度を変更したりします。
スクリプトは後で作成します。
DamageUIの子要素のTextには初期テキストとして100を設定します。
上が出来上がったダメージ用UIです。
「かかし君」の辺りに表示されていますが、これはかかし君のサイズとUIのサイズを合わせる為です。
ダメージUIを表示する位置はスクリプトで行うので位置を厳密に設定する必要はありません。
DamageUIが出来上がったらAssetsフォルダにドラッグ&ドロップをしてプレハブ化しておきます。
DamageUIの作成
DamageUIスクリプトを作成します。
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 | using UnityEngine; using System.Collections; using UnityEngine.UI; public class DamageUI : MonoBehaviour { private Text damageText; // フェードアウトするスピード private float fadeOutSpeed = 1f; // 移動値 [SerializeField] private float moveSpeed = 0.4f; void Start () { damageText = GetComponentInChildren<Text>(); } void LateUpdate () { transform.rotation = Camera.main.transform.rotation; transform.position += Vector3.up * moveSpeed * Time.deltaTime; damageText.color = Color.Lerp(damageText.color, new Color(1f, 0f, 0f, 0f), fadeOutSpeed * Time.deltaTime); if (damageText.color.a <= 0.1f) { Destroy(gameObject); } } } |
ダメージ表示のテキストの不透明度を1から少しづつ下げ透明にしていきます。
Colorは
Color(R, G, B, A)
でRはRed、GはGreen、BはBlue、AはAlphaをfloatで指定します。
つまり白はColor(1, 1, 1, 1)で黒はColor(0, 0, 0, 1)、青はColor(0, 0, 1, 1)となります。
今回はColor.Lerpを使って現在のテキストのカラーのアルファを1から0へとfadeOutSpeedの速さで変化させています。
UIの角度はカメラの角度の方向を向くようにし、位置はmoveSpeedの速さで上に進んでいくようにします。
alpha値が0.1より下になったらDamageUIインスタンスを削除します。
0としていないのはColor.Lerpを使うと0付近の値までは近づきますがなかなか0にならない為、ある程度透明になったら消しています。
Color.Lerpを使うほかにも以下のように個別にalphaを用意しその値を少しずつ減らして、それをdamageText.colorに設定するColorのaに設定する事でも同様の事が行えます。
1 2 3 4 5 6 | // 少しづつ透明にしていく alpha -= fadeOutSpeed * Time.deltaTime; // テキストのcolorを設定 damageText.color = new Color(1f, 0f, 0f, alpha); |
主人公の攻撃が当たったかどうか
次に主人公の武器が「かかし君」に当たったかどうかの処理ですが、この辺りの処理は
を参考に作成してください。
アニメーションイベント、コライダのOn・Offの機能を作成しておいてください。
主人公が装備している武器や攻撃時の手足にコライダを設定しIsTriggerにチェックを入れます。
最初は攻撃判定をさせないので当たり判定のコライダ(SphereCollider等)の名前の横のチェックを外しておきます。
武器や手足などの当たり判定箇所にAttackという新しいスクリプトを取りつけます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | using UnityEngine; using System.Collections; namespace DamagePointUI { public class Attack : MonoBehaviour { void OnTriggerEnter(Collider col) { if(col.tag == "Enemy") { col.transform.root.GetComponent <TakeDamage>().Damage(col); } } } } |
武器や手足のコライダが他のコライダと接触した時にOnTriggerEnterイベントが発生するので、接触した相手にEnemyタグを設定したコライダがあった時に、そのコライダのルート(Kakasi)に設定しているTakeDamageスクリプトのDamageメソッドを呼び出します。
その際に接触したコライダの情報も渡します。
ダメージUIが正しく動作するか確認してみる
これで機能が出来たので主人公で「かかし君」を攻撃しダメージが表示されるかどうか確認してみましょう。
上のようにうまくいきました。
敵キャラクター「かかし君」を動かすとずれる
「かかし君」を少し動かしてみましょう。
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 | using UnityEngine; using System.Collections; public class Kakasi : MonoBehaviour { private CharacterController characterController; private Vector3 velocity = Vector3.zero; // 移動スピード [SerializeField] private float moveSpeed = 1f; // 移動距離 [SerializeField] private float moveDistance = 1f; void Start () { characterController = GetComponent <CharacterController> (); } void Update () { if (characterController.isGrounded) { velocity = Vector3.zero; velocity = new Vector3 (moveDistance * Mathf.Sin (moveSpeed * Time.time), 0f, 0f); } velocity.y += Physics.gravity.y * Time.deltaTime; characterController.Move (velocity * Time.deltaTime); } } |
上のようなスクリプトをKakasiに設定します。
X軸を行ったり来たりするようになります。
「かかし君」が動き出した事で上のようにDamageUIと「かかし君」との位置がずれてしまいます。
こういう仕様でいいという人はいいんですが、DamageUIも「かかし君」とともに動かしたい方もいると思います。
DamageUIを「かかし君」の子要素にする
TakeDamageスクリプトを修正します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | using UnityEngine; using System.Collections; public class TakeDamage : MonoBehaviour { // DamageUIプレハブ [SerializeField] private GameObject damageUI; public void Damage(Collider col) { // DamageUIをインスタンス化。登場位置は接触したコライダの中心からカメラの方向に少し寄せた位置 var obj = Instantiate<GameObject>(damageUI, col.bounds.center - Camera.main.transform.forward * 0.2f, Quaternion.identity); obj.transform.SetParent(col.transform); } } |
DamageUIを作成した時に親要素を引数で受け取ったコライダのゲームオブジェクトにします。
この場合はDamageUIの親要素がかかしのダメージを受けた部位が親要素になります。
インスタンス化したオブジェクトの親は引数で設定することも出来ますが、引数で設定した場合はUIのScaleが親からのスケールで計算されてしまう為、一旦インスタンス化した後に親の設定をしています。
DamageUIは「かかし君」の子要素の部位なので、「かかし君」が移動するとDamageUIも一緒に動くようになります。
それではUnityの実行ボタンを押して確認してみましょう。
上のようにDamageUIがKakasiと共に動くようになりました。
これでUnityのアクションゲームで与えたダメージを与えた場所にUIで表示する機能が完了しました。
本機能とは関係なく当たり判定で気になる事
今回の機能を作成していて武器と敵の接触判定は気をつけなければいけないなと思いました。
剣を振るアニメーションが速いと敵との接触判定(OnTriggerEnter)がスルーされる事が多いです。
アニメーション再生スピードを落とせば問題はないんですが・・・、
見た目的に武器が敵に当たっているのに当たり判定がされない!?という状況になってしまう可能性もあります。
当たり範囲のコライダを大きくしたりアニメーションのスピードを下げたりして回避するのがいいのかな?
ここら辺もうまく出来るようにしなきゃいかんですね(-.-)
そこら辺の対処は
を参照してください。