【Unityの練習ゲーム作り 第二弾!】
大人気アプリ モンスト風のゲームを、Unityの練習で作ってみます。
その1 大まかな設計を考えました
その2 自キャラの動きを実装しました
その3 敵キャラを追加し、ターン制を実装しました
今回は さらに敵キャラを追加したときの管理を考えてみます。
ゲーム内容の変更
モンスト風のゲームを作ってきましたが、モンストの内容そのままを実装するのは さすがに難しいです。
そこで、以下のようにゲーム内容を単純化することにしました。
- 敵の出現:場所,敵種,数をランダム
- ゲーム開始時と、全滅時に新たに敵が出現
- 敵は3種:通常タイプ,攻撃特化タイプ,回復タイプ
敵キャラのコントロール
敵キャラの追加
早速 新たに敵キャラを作っていきます。
複数の敵キャラに共通部分があるため、オブジェクト指向的に作るのが良いかと思いましたが、慣れないことを最初からやるより 自分のわかり易さを優先することにしました。
それぞれの敵キャラをコントロールするスクリプトです。
●通常タイプ
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 |
using System.Collections; using System.Collections.Generic; using UnityEngine; public class EnemyController_Y : MonoBehaviour { private GameObject characterController; private GameObject enemyManager; private Rigidbody2D rigid2d; private float hp = 100f; private float attack = 10f; void Start () { rigid2d = GetComponent<Rigidbody2D>(); characterController = GameObject.Find("Character-Red"); enemyManager = GameObject.Find("EnemyManager"); } void Update () { // 破壊処理 if (this.hp == 0) { Destroy(gameObject); enemyManager.GetComponent<EnemyManager>().enemyCount_(0); } } // 攻撃 public void enemyAttack(){ characterController.GetComponent<CharacterController>().characterHP(-this.attack); } // HP管理 public void enemyHP(float hp_){ this.hp += hp_; if (this.hp > 100f){ this.hp = 100f; } Debug.Log("Y-Enemy HP:" + this.hp); } } |
●攻撃特化タイプ(通常攻撃タイプと異なる部分のみ表示)
1 2 3 4 5 6 7 8 9 10 |
private float hp = 50f; private float attack = 30f; void Update () { // 破壊処理 if (this.hp == 0) { Destroy(gameObject); enemyManager.GetComponent<EnemyManager>().enemyCount_(2); } } |
●回復タイプ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
private float hp = 100f; private float attack = 10f; void Update () { // 破壊処理 if (this.hp == 0) { Destroy(gameObject); enemyManager.GetComponent<EnemyManager>().enemyCount_(1); } } // 攻撃 public void enemyAttack(int enemyY, int enemyG){ if(enemyY>0){ characterController_y = GameObject.Find("EnemyYellow_Prefab(Clone)"); characterController_y.GetComponent<EnemyController_Y>().enemyHP(this.attack); Debug.Log("Yellowを回復した"); } if(enemyG>0){ characterController_g = GameObject.Find("EnemyGreen_Prefab(Clone)"); characterController_g.GetComponent<EnemyController_G>().enemyHP(this.attack); Debug.Log("Greenを回復した"); } } |
セクションごとに見ていきます。
ここは、体力がゼロになったときにオブジェクトを開始し、敵キャラの数を減らしています。
1 2 3 4 |
if (this.hp == 0) { Destroy(gameObject); enemyManager.GetComponent<EnemyManager>().enemyCount_(0); } |
回復タイプは、他タイプキャラが存在するときに その体力を回復します。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// 攻撃 public void enemyAttack(int enemyY, int enemyG){ if(enemyY>0){ characterController_y = GameObject.Find("EnemyYellow_Prefab(Clone)"); characterController_y.GetComponent<EnemyController_Y>().enemyHP(this.attack); Debug.Log("Yellowを回復した"); } if(enemyG>0){ characterController_g = GameObject.Find("EnemyGreen_Prefab(Clone)"); characterController_g.GetComponent<EnemyController_G>().enemyHP(this.attack); Debug.Log("Greenを回復した"); } } |
敵キャラのコントロール
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 89 |
using System.Collections; using System.Collections.Generic; using UnityEngine; public class EnemyManager : MonoBehaviour { public GameObject EnemyYellow_Prefab; public GameObject EnemyPink_Prefab; public GameObject EnemyGreen_Prefab; public GameObject gameManager; public GameObject enemyController; int generateCount = 3; int[] enemyCount = new int[]{0,0,0}; bool enemyTurn = false; void Start () { gameManager = GameObject.Find("GameManager"); //generateEnemy(); } void Update () { // 敵の生成 if((enemyCount[0]+enemyCount[1]+enemyCount[2]) == 0){ generateCount = Random.Range(2,6); // 敵の生成数 for(int i = 0; i < generateCount; i++){ generateEnemy(); } this.enemyTurn = false; } if(enemyTurn == true){ enemyTurn_(); enemyTurn = false; gameManager.GetComponent<GameManager>().turnControl("Character"); } } // ターン管理 public void setTurn(bool turn){ this.enemyTurn = turn; } // 敵キャラの生成 void generateEnemy(){ int enemyKind = Random.Range(0,3); // 敵の種類 float px = Random.Range(-2f,2f); // 生成場所 x座標 float py = Random.Range(-3f,3f); // 生成場所 y座標 switch(enemyKind){ case 0: GameObject enemyYellow = Instantiate(EnemyYellow_Prefab) as GameObject; enemyYellow.transform.position = new Vector3(px, py, 0); enemyCount[enemyKind] ++; break; case 1: GameObject enemyPink = Instantiate(EnemyPink_Prefab) as GameObject; enemyPink.transform.position = new Vector3(px, py, 0); enemyCount[enemyKind] ++; break; case 2: GameObject enemyGreen = Instantiate(EnemyGreen_Prefab) as GameObject; enemyGreen.transform.position = new Vector3(px, py, 0); enemyCount[enemyKind] ++; break; } } // 敵ターン制御 void enemyTurn_(){ for(int i = 0; i < enemyCount[0]; i++){ enemyController = GameObject.Find("EnemyYellow_Prefab(Clone)"); enemyController.GetComponent<EnemyController_Y>().enemyAttack(); } for(int i = 0; i < enemyCount[2]; i++){ enemyController = GameObject.Find("EnemyGreen_Prefab(Clone)"); enemyController.GetComponent<EnemyController_G>().enemyAttack(); } for(int i = 0; i < enemyCount[1]; i++){ enemyController = GameObject.Find("EnemyPink_Prefab(Clone)"); enemyController.GetComponent<EnemyController_P>().enemyAttack(enemyCount[0],enemyCount[2]); } } // 敵数管理 public void enemyCount_(int i){ this.enemyCount[i] --; } } |
ここでは、敵の生成と敵キャラターンの行動をコントロールしています。
はじめに敵の生成です。
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 |
if((enemyCount[0]+enemyCount[1]+enemyCount[2]) == 0){ generateCount = Random.Range(2,6); // 敵の生成数 for(int i = 0; i < generateCount; i++){ generateEnemy(); } this.enemyTurn = false; } // 敵キャラの生成 void generateEnemy(){ int enemyKind = Random.Range(0,3); // 敵の種類 float px = Random.Range(-2f,2f); // 生成場所 x座標 float py = Random.Range(-3f,3f); // 生成場所 y座標 switch(enemyKind){ case 0: GameObject enemyYellow = Instantiate(EnemyYellow_Prefab) as GameObject; enemyYellow.transform.position = new Vector3(px, py, 0); enemyCount[enemyKind] ++; break; case 1: GameObject enemyPink = Instantiate(EnemyPink_Prefab) as GameObject; enemyPink.transform.position = new Vector3(px, py, 0); enemyCount[enemyKind] ++; break; case 2: GameObject enemyGreen = Instantiate(EnemyGreen_Prefab) as GameObject; enemyGreen.transform.position = new Vector3(px, py, 0); enemyCount[enemyKind] ++; break; } } |
enemyCount[]
には3種の敵キャラの数を保管しており、敵の数が0になったときに 敵を新たに生成します。
続いて敵のターン制御。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
if(enemyTurn == true){ enemyTurn_(); enemyTurn = false; gameManager.GetComponent<GameManager>().turnControl("Character"); } // 敵ターン制御 void enemyTurn_(){ for(int i = 0; i < enemyCount[0]; i++){ enemyController = GameObject.Find("EnemyYellow_Prefab(Clone)"); enemyController.GetComponent<EnemyController_Y>().enemyAttack(); } for(int i = 0; i < enemyCount[2]; i++){ enemyController = GameObject.Find("EnemyGreen_Prefab(Clone)"); enemyController.GetComponent<EnemyController_G>().enemyAttack(); } for(int i = 0; i < enemyCount[1]; i++){ enemyController = GameObject.Find("EnemyPink_Prefab(Clone)"); enemyController.GetComponent<EnemyController_P>().enemyAttack(enemyCount[0],enemyCount[2]); } } |
敵の数だけ、敵の攻撃関数を呼び出します。
自キャラコントロール
前回からの変更点は、敵キャラにぶつかったときのダメージの制御です。
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 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class CharacterController : MonoBehaviour { Rigidbody2D rigid2d; Vector2 startPos; GameObject gameManager; GameObject enemyController; public Slider shotGauge; float speed = 0; float gaugeLength = 0; float hp = 100f; float attack = 10f; bool shotGaugeSet = false; bool characterTurn = true; // 自分のターンか void Start () { this.rigid2d = GetComponent<Rigidbody2D>(); gameManager = GameObject.Find("GameManager"); } void Update () { // マウスを押した地点の座標を記録 if (Input.GetMouseButtonDown(0) && characterTurn && this.rigid2d.velocity.magnitude==0){ this.startPos = Input.mousePosition; shotGaugeSet = true; } // マウスを離した地点の座標から、発射方向を計算 if (Input.GetMouseButtonUp(0) && characterTurn && this.rigid2d.velocity.magnitude==0){ Vector2 endPos = Input.mousePosition; Vector2 startDirection = -1*(endPos - startPos).normalized; this.rigid2d.AddForce(startDirection * speed); shotGaugeSet = false; Debug.Log(speed); } // マウスが押されている間 ショットゲージを呼ぶ if (shotGaugeSet){ shotGaugeValue(); } // 速さが一定以下になったことを検知し、キャラを停止しターンを終了 if (this.rigid2d.velocity.magnitude < 2.0f && this.rigid2d.velocity.magnitude!=0){ //Debug.Log(this.rigid2d.velocity.magnitude); this.GetComponent<Rigidbody2D>().velocity = Vector2.zero; characterTurn = false; gameManager.GetComponent<GameManager>().turnControl("Enemy"); } // テスト用:スペースキー押下で停止 if(Input.GetKeyDown(KeyCode.Space)) { this.rigid2d.velocity *= 0; gameManager.GetComponent<GameManager>().turnControl("Enemy"); } } void FixedUpdate(){ this.rigid2d.velocity *= 0.995f; } // ショットゲージ関数 void shotGaugeValue(){ gaugeLength += 0.025f; //ゲージがMaxでゼロに戻る if(gaugeLength> 1.025f){ gaugeLength = 0; } shotGauge.value = gaugeLength; //ゲージ長さをlengthに代入 speed = gaugeLength * 1000f + 500f; // スピードをゲージ値から計算 } // ぶつかって敵に攻撃 void OnCollisionEnter2D(Collision2D other){ if(this.rigid2d.velocity.magnitude!=0){ string enemytag = other.gameObject.tag; switch(enemytag){ case "EnemyYellow": enemyController = GameObject.Find("EnemyYellow_Prefab(Clone)"); enemyController.GetComponent<EnemyController_Y>().enemyHP(-this.attack); break; case "EnemyPink": enemyController = GameObject.Find("EnemyPink_Prefab(Clone)"); enemyController.GetComponent<EnemyController_P>().enemyHP(-this.attack); break; case "EnemyGreen": enemyController = GameObject.Find("EnemyGreen_Prefab(Clone)"); enemyController.GetComponent<EnemyController_G>().enemyHP(-this.attack); break; } } } // HP管理 public void characterHP(float hp_){ this.hp += hp_; Debug.Log("Character HP:" + this.hp); } // ターン管理 public void setTurn(bool turn){ this.characterTurn = turn; Debug.Log("ターンが返ってきた"); } } |
動作
敵を全滅させると、敵がランダムに新生成されます。
(これってモンストじゃなくて、ブロック崩しでは…)
反省
今回の方法だと、例えば 通常攻撃タイプ同士を識別できません。
また敵種が増えるたびに、使用タグと関数が増えます。
うーん、こういう時どうするんだろう…?配列で管理するのかな?
「敵キャラの生成」を調べて、次回のゲームでは活かしたいです。
次回は、HPのバー表示やSEを入れていきます。