前回、敵がダメージを受けた際の処理について実装しました。
今の仕様なら、敵が1体だと問題ありませんが、2体以上が同時に出現した場合に困ります。
スライダーとテキストは、ヒエラルキーウィンドウ上にそれぞれ1つずつしか用意していませんので、2体以上の敵が出てくるとお互いが干渉し合います。仮に2体敵が配置されている状況でどちらか1体に攻撃を与えると、もう1体にもダメージが反映されるわけですね。
と言う事で、今回は2体以上出現してもそれぞれのオブジェクトが独自のステータスを持つように修正していきたいと思います。
複数の敵が出現した際にステータスを個別に持たせる方法【Unity】
Canvasの中にPanelを作成する
現状敵のステータスは1個分しか用意していませんでしたが、今回の実装でフィールドに同時に最大敵を4体出現させるようにします。
出来栄えはこんな感じです。別に4体に拘る必要はありません。もっと出現させることも可能ですが、この作り方だとスペースを利用するので、出現させる敵が増えれば増える程、1体のステータスを表示する範囲が狭くなります。
パネルを作成したら、横に綺麗にオブジェクトを並べたいので「Horizontal Layout Group」コンポーネントを追加します。パネルのImageはチェックを外しておきます。
次に、EnemyNameをSliderを1つのグループにまとめるために、パネル内に空のオブジェクトを作ってその中にまとめます。空のオブジェクトの名前は「EnemyStatus0」として3つ複製します。
複製したEnemyStatusは、上図のように通し番号を付けておきます。ステータスを表示させたい位置は、基本的に自由なので好きな場所に調整してください。
敵オブジェクトをプレハブ化する
これまでは敵が1体だったのでそのままシーン上に配置しているだけでしたが、ジェネレータースクリプトを作って、敵を出現させるので、敵オブジェクトをプレハブ化しておきましょう。
ジェネレータスクリプトを作成する
using System.Collections; | |
using System.Collections.Generic; | |
using UnityEngine; | |
public class EnemyGenerator : MonoBehaviour | |
{ | |
public int count = 0; | |
float span = 5f; | |
public float time = 0f; | |
public GameObject EnemyPrefab; | |
// Start is called before the first frame update | |
void Start() | |
{ | |
} | |
// Update is called once per frame | |
void Update() | |
{ | |
time += Time.deltaTime; | |
if(time > span && count < 4) | |
{ | |
GameObject enemy = (GameObject)Instantiate(EnemyPrefab, new Vector3(Random.Range(10,30), 5, Random.Range(10,30)), Quaternion.identity); | |
count++; | |
time = 0; | |
} | |
} | |
} |
5秒毎に、指定した範囲内にてランダムに敵を出現させます。
最大で配置できる敵の数が4体なので、カウンターを用意して4体配置されている場合はそれ以上インスタンスを生成しないようにします。
空のオブジェクトを作って、このスクリプトをアタッチします。名前は「EnemyGenerator」にします。
EnemyLifeManagerスクリプトの修正
using System.Collections; | |
using System.Collections.Generic; | |
using UnityEngine; | |
using UnityEngine.UI; | |
public class EnemyLifeManager : MonoBehaviour | |
{ | |
private GameObject EnemyCanvas; | |
private Slider slider; | |
private Text EnemyNameText; | |
public GameObject ExpPrefab; | |
// public GameObject EnemyStatusPrefab; | |
private GameObject Panel; | |
private GameObject game; | |
GameObject enemyGenerator; | |
EnemyGenerator eg; | |
int maxHP = 100; | |
int HP; | |
// Start is called before the first frame update | |
void Awake() | |
{ | |
EnemyCanvas = GameObject.FindGameObjectWithTag("EnemyCanvas"); | |
Panel = EnemyCanvas.transform.Find("Panel").gameObject; | |
enemyGenerator = GameObject.Find("EnemyGenerator"); | |
eg = enemyGenerator.GetComponent<EnemyGenerator>(); | |
//GameObject damage = (GameObject)Instantiate(EnemyStatusPrefab, Panel.transform); | |
for(int i = 0; i < 4; i++) | |
{ | |
game = Panel.transform.Find("EnemyStatus"+i).gameObject; | |
EnemyNameText = game.transform.Find("EnemyName").GetComponent<Text>(); | |
if(EnemyNameText.text == "") | |
{ | |
slider = game.transform.Find("Slider").GetComponent<Slider>(); | |
EnemyNameText.text = "a"; | |
break; | |
} | |
} | |
slider.value = 1; | |
HP = maxHP; | |
} | |
// Update is called once per frame | |
void Update() | |
{ | |
} | |
private void OnTriggerStay(Collider col) | |
{ | |
if (col.gameObject.tag == "Player") | |
{ | |
EnemyNameText.gameObject.SetActive(true); | |
EnemyNameText.text = "ボール"; | |
slider.gameObject.SetActive(true); | |
} | |
} | |
private void OnTriggerExit(Collider other) | |
{ | |
EnemyNameText.gameObject.SetActive(false); | |
slider.gameObject.SetActive(false); | |
} | |
private void OnCollisionEnter(Collision col) | |
{ | |
if (this.gameObject.tag == "Ball") | |
{ | |
Damage(10); | |
} | |
} | |
void Damage(int damage) | |
{ | |
HP -= damage; | |
slider.value = (float)HP / (float)maxHP; | |
if (slider.value == 0) | |
{ | |
eg.count--; | |
eg.time = 0; | |
EnemyNameText.text = ""; | |
Destroy(this.gameObject); | |
EnemyNameText.gameObject.SetActive(false); | |
slider.gameObject.SetActive(false); | |
GameObject Explode = (GameObject)Instantiate(ExpPrefab, new Vector3(transform.position.x, transform.position.y, transform.position.z), Quaternion.identity); | |
Destroy(Explode, 0.5f); | |
} | |
} | |
} |
先ほど通し番号を付けて作成しておいたEnemyStatusをfor文で取得していき、子要素のテキストが空だった場合、敵が存在しないという事で、スライダーコンポーネントを取得してfor文から抜けます。
スライダー取得と同時に、EnemyNameテキストには何でも良いので適当な文字を入れておきます。この処理をしておかないと、同じEnemyStatusを複数の敵が干渉する場合があるからです。
敵の範囲内に入らない限りテキストはずっと空のままですし、非アクティブの状態なので何かしら文字を入れておくわけです。
後は、敵のHPが0になった際に、EnemyGeneratorスクリプトのカウンターを減らし、タイマーをリセットします。そして、EnemyNameテキストを再び空にします。
スクリプトを保存したら、各EnemyStatusのEnemyNameとSliderオブジェクトを全て非表示にしておきます。また、EnemyNameのテキストは全て空にしておきましょう。
ここまで出来たらゲームを実行してみます。
どの敵を倒しても、ステータスが干渉することはないはずです。いずれかの敵が倒されても、for文で探索を行い敵が補充されると思います。