本講座では、UnityとC#を用いて3DFPSゲームを作成します。今回は第6回です。
第五回ではプレイヤーのエイム照準器による当たり判定処理や攻撃処理を作成し、敵にヒットさせることができるようになりました。
前回の記事 :
今回は弓矢が当たった敵の攻撃処理を作り込んでいきます。「敵のHPを減らして倒す」という基本的な処理を作成していきましょう。
また、敵の種類を増やせるように新しく「継承」というC#の機能を活用しながらゲームを開発していきましょう。
では始めます。
敵キャラクターの実装 攻撃が当たったらHPを減らして消滅
まずは前回作成した、簡易的な敵の処理をちゃんと作成し攻撃を当てたらダメージを受け、倒せるようにしていきましょう。
EnemyBase.csに敵キャラクターの基本となる処理を書いていこう
まずは「Assets/AppMain/Script」に「EnemyBase」という名前で新しくスクリプトを作成します。
また、この処理の追加に伴って「Arrow.cs」の処理を変更していきます。まずは「EnemyBase」の処理を見ていきましょう。
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 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
using System.Collections; using System.Collections.Generic; using UnityEngine; public class EnemyBase : MonoBehaviour { // 最大HP. [SerializeField] int maxHp = 3; // 現在のHP. int hp = 0; // 攻撃を受けるフラグ. bool canHit = true; void Start() { Init(); } void Update() { } // --------------------------------------------------------------- /// <summary> /// 初期化処理. /// </summary> // --------------------------------------------------------------- public void Init() { hp = maxHp; } // --------------------------------------------------------------- /// <summary> /// コライダーエンター処理. /// </summary> /// <param name="col"></param> // --------------------------------------------------------------- public void OnEnemyColliderEnter( Collision col ) { if( col.gameObject.tag == "Arrow" && canHit == true ) { // Arrowを取得して「Arrow」の敵にヒットした時の処理を実行. var arrow = col.gameObject.GetComponent<Arrow>(); arrow.OnEnemyHit(); // HPを矢の攻撃力分マイナス. hp -= arrow.Attack; if( hp <= 0 ) { // 死亡時処理. OnDead(); } else { Debug.Log( gameObject.name + " に攻撃がヒット。残りHP " + hp ); // 次回ヒットまでの待機時間. StartCoroutine( HitWait() ); } } } // --------------------------------------------------------------- /// <summary> /// 死亡時処理. /// </summary> // --------------------------------------------------------------- void OnDead() { Debug.Log( gameObject.name + "を倒しました" ); Destroy( gameObject ); } // --------------------------------------------------------------- /// <summary> /// 攻撃ヒット後次の攻撃が当たるまでの待機処理. /// </summary> // --------------------------------------------------------------- IEnumerator HitWait() { // 指定時間待機してフラグを戻す. canHit = false; yield return new WaitForSeconds( 0.5f ); canHit = true; } } |
上から順に見ていきます。
まず追加している変数です。「maxHp」はHPの最大値を「SerializeField」を付けてInspectorから設定できるようにしています。
次に「hp」は現在のHPを表します(int型)。「canHit」は攻撃によるダメージを受けられるかどうかの状態を表すフラグです。
続いて「Start」関数内は「Init()」という処理を実行しているのみです。「Update」は何も追加していません。
では「Init」関数です。処理は「hp = maxHp」のみで初期化処理として現在のHPに最大HPを代入しています。
次の関数を抜き出します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public void OnEnemyColliderEnter( Collision col ) { if( col.gameObject.tag == "Arrow" && canHit == true ) { // Arrowを取得して「Arrow」の敵にヒットした時の処理を実行. var arrow = col.gameObject.GetComponent<Arrow>(); arrow.OnEnemyHit(); // HPを矢の攻撃力分マイナス. hp -= arrow.Attack; if( hp <= 0 ) { // 死亡時処理. OnDead(); } else { Debug.Log( gameObject.name + " に攻撃がヒット。残りHP " + hp ); // 次回ヒットまでの待機時間. StartCoroutine( HitWait() ); } } } |
これは今までも出てきました、コライダーのエンター処理にInspectorから設定するための関数です。そのため引数は「Collision」型の「col」を用意します。
処理を見ていきましょう。最初はif文「if( col.gameObject.tag == “Arrow” && canHit == true )」で最初の条件はこれもここまでで見たように「コライダーのタグが”Arrow”」の時です、かつ「canHit」のフラグが「true」の時になります。
最初に「arrow」という変数にコライダーのゲームオブジェクトから「Arrow」を取得して保管します。
そして取得した「arrow」の「OnEnemyHit()」関数を実行します。この関数はこれから作成していくので、この段階ではエラーになります。関数に「public」を付けておくとこのように他のクラスから関数の実行ができるようになります。
次の処理「hp -= arrow.Attack」は現在のHPの「hp」から「arrow.Attack」を引いています。この「arrow.Attack」は「arrow」の「Attack」という「public」の変数です。これも後ほど「Arrow」クラスに追加します。この段階ではエラーになります。
続いて「if( hp <= 0 )」つまり現在のHPが「0」以下の場合に「OnDead()」関数を実行します。そうでない時は(ログは飛ばして)「HitWait()」というコルーチンを実行します。
「OnDead()」関数は「Destroy( gameObject )」で自身のゲームオブジェクトを破棄してます。死亡時に自分で自分を破棄しているということです。
そしてコルーチンも抜き出して解説しておきましょう。
1 2 3 4 5 6 7 |
IEnumerator HitWait() { // 指定時間待機してフラグを戻す. canHit = false; yield return new WaitForSeconds( 0.5f ); canHit = true; } |
以前も出てきたコルーチンは返り値が「IEnumerator」になり、最初に「canHit」を「false」にして攻撃が当たらないようにします。
「yield return new WaitForSeconds( 0.5f )」でこの場合「0.5秒」待機し、最後にまた「canHit」を「true」に戻します。
実行する際は「StartCoroutine」で実行することを忘れないようにしましょう。
Arrow.csの変更
では続いて「Arrow.cs」を変更していきます。
【学歴不問・高卒、元ニートでも挑戦できる】
Unity C#の継承を活用して別の敵を作成しよう
次に別の種類の敵を作成してみましょう。
敵キャラクターの配置
まずはHierarchyに新しく空オブジェクトを作成し「EnemyBat」という名前にします。
次にインポートしたアセットを追加していきます。「Assets/Level 1 Monster Pack/Prefabs/Bat」フォルダにある「Bat_Red」をドラック&ドロップして「EnemyBat」の子に追加します。
追加した子の「Bat_Red」のTransformを下記のように設定します。
1 2 3 4 |
Bat_RedのTransform Position( 0, 0, 0 ) Rotation( 0, 0, 0 ) Scale( 40, 40, 40 ) |
ここから少し先ほどとは違うところにコンポーネントを付与していきます。
少し分かりにくいので注意してください、「SphereCollider」「ColliderCallReceiver」を「EnemyBat/Bas_Red/RIG/Bat1Control」に付与します。
なぜここにつけるのかというのは理由があり、今後第8回で詳しく解説しますが後に設定するアニメーションに関係しています。
今はまだアニメーションの設定をしていないので、「コライダーをつける位置が重要になる場合もある」ということを覚えておきましょう。
「Rigidbody」は一旦付与しないで大丈夫です。必要な時にはまた設定します。「SphereCollider」は体よりひと回り大きいくらいに設定しましょう。
新しい敵キャラクタースクリプトの作成
次にこの敵用のスクリプトを作成します。
「Assets/AppMain/Script」に新しくC#スクリプトを作成し「EnemyBat」という名前にします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
using System.Collections; using System.Collections.Generic; using UnityEngine; public class EnemyBat : EnemyBase { protected override void Start() { base.Start(); } protected override void Update() { base.Update(); } } |
内容は「EnemyCrocodile」と全く同じなので解説は飛ばしますが、「EnemyBase」を継承しています。
ではこの「EnemyBat.cs」をHierarchyの「EnemyBat」に付与しましょう。(Colliderなどが付与されている「BatControl」ではなく親のオブジェクトです)
できたら、わかりやすいカメラの前あたりに「EnemyBat」を動かしておきましょう。コウモリなのでとりあえず空中のお好きなところに置いておきましょう。
最後に「BatControl」に付与していた「ColliderCallReceiver」の「Collision Enter Event」に「On Enemy Collider Enter」を登録します。やり方は「Enemy1」の時と同じです。
では、再生して「EnemyBat」にも矢を当ててみましょう。HPは初期値の「3」のままなので3発当てれば倒せる(消える)ようになりました。
新しい敵を作成するときは同じように、「EnemyBase」を継承したスクリプトを作成して設定していけば簡単に作成ができます。
その上で、敵それぞれの独自の処理はサブクラスの方に記載していきます。
では今回はここまでにして、次回は敵ごとに行動パターンなどを作成していきましょう。
次の記事 :
コメント