【Unityの練習ゲーム作り 第二弾!】
大人気アプリ モンスト風のゲームを、Unityの練習で作ってみます。
その1 大まかな設計を考えました
その2 自キャラの動きを実装しました
今回は 敵キャラを追加し、自ターン,敵ターンを作ってみます。
敵キャラの追加
自キャラが攻撃を加える敵キャラを、画面に追加します。
当たり判定を追加して、完成。
攻撃、HP管理
- 自キャラが敵にあたったときに、敵の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 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 |
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; void Start () { this.rigid2d = GetComponent<Rigidbody2D>(); gameManager = GameObject.Find("GameManager"); enemyController = GameObject.Find("Enemy-Yellow"); } void Update () { // マウスを押した地点の座標を記録 if (Input.GetMouseButtonDown(0)){ this.startPos = Input.mousePosition; shotGaugeSet = true; } // マウスを離した地点の座標から、発射方向を計算 if (Input.GetMouseButtonUp(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(Input.GetKeyDown(KeyCode.Space)) { // this.rigid2d.velocity *= 0; //} } 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(other.gameObject.tag == "Enemy"){ enemyController.GetComponent<EnemyController>().enemyHP(-this.attack); } } // HP管理 public void characterHP(float hp_){ this.hp += hp_; Debug.Log("Character HP:" + this.hp); } } |
セクションごとに見ていきます。
1 2 |
float hp = 100f; float attack = 10f; |
自キャラのHP(hp
)と攻撃力(attack
)を新変数として追加しました。
1 2 3 4 5 6 |
// ぶつかって敵に攻撃 void OnCollisionEnter2D(Collision2D other){ if(other.gameObject.tag == "Enemy"){ enemyController.GetComponent<EnemyController>().enemyHP(-this.attack); } } |
敵に衝突したときに、敵のHPを減少させます。
自キャラと敵キャラの衝突判定は、void OnCollisionEnter2D(Collision2D other)
で実現されます。
衝突したときの、オブジェクトが「敵」である判定は タブ(="Enemy"
)で実施しています。
敵キャラのHPを管理する関数に、自キャラの攻撃力(attack
)を与えています。
1 2 3 4 5 |
// HP管理 public void characterHP(float hp_){ this.hp += hp_; Debug.Log("Character HP:" + this.hp); } |
HP管理です。
外部関数からアクセス可能にするため、publicです。与えられた値によって、自身のHPが変化します。
敵からの攻撃、回復アイテムによる回復を想定しています。
●敵キャラ:
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 |
using System.Collections; using System.Collections.Generic; using UnityEngine; public class EnemyController : MonoBehaviour { GameObject gameManager; GameObject characterController; Rigidbody2D rigid2d; float hp = 100f; float attack = 10f; void Start () { this.rigid2d = GetComponent<Rigidbody2D>(); gameManager = GameObject.Find("GameManager"); characterController = GameObject.Find("Character-Red"); } void Update () { } // 攻撃 void enemyAttack(){ characterController.GetComponent<CharacterController>().characterHP(-this.attack); } // HP管理 public void enemyHP(float hp_){ this.hp += hp_; Debug.Log("Enemy HP:" + this.hp); } } |
中身は、自キャラと同じですので説明は割愛します。
ターン制
モンストでは 自キャラと敵キャラのターンが、交互に繰り返されます。
「自分のターンでしか、自キャラが動けない」をコードに書いていきます。
●自キャラ
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 |
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"); enemyController = GameObject.Find("Enemy-Yellow"); } 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; //} } 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(other.gameObject.tag == "Enemy"){ enemyController.GetComponent<EnemyController>().enemyHP(-this.attack); } } // 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("ターンが返ってきた"); } } |
随分長くなってしまいました…。
追加部分を見ていきます。
1 |
bool characterTurn = true; // 自分のターンか |
ターン管理用に、新しくbool変数を導入。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// マウスを押した地点の座標を記録 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); } |
自キャラの発射関数の呼び出し条件を変更しています。
characterTurn
が Trueかつ、移動していないときのみ呼び出せるようになっています。
1 2 3 4 5 6 7 8 9 |
// 速さが一定以下になったことを検知し、キャラを停止しターンを終了 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"); } |
新しく追加したターン終了を管理するセクションです。
1 2 3 |
void FixedUpdate(){ this.rigid2d.velocity *= 0.995f; } |
↑元からある、自キャラの速度を徐々に落としていく処理です。
自キャラの速度は限りなく0に近づくものの、決して0にはなりません。(※正確には、コンピュータ処理では表現できる桁数に限りがあるので、時間をかければ見かけ上0にすることは可能です。)
そこで、自キャラの移動速度が一定値以下になったとき、強制的にスピードを0にしています(4行目)。
その後、自身の characterTurn
を falseにし、GameManager にターンが終了したことを伝えています。
1 2 3 4 5 |
// ターン管理 public void setTurn(bool turn){ this.characterTurn = turn; Debug.Log("ターンが返ってきた"); } |
外部から、characterTurn
を変更させるセッターです。
●敵キャラ
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 |
using System.Collections; using System.Collections.Generic; using UnityEngine; public class EnemyController : MonoBehaviour { GameObject gameManager; GameObject characterController; Rigidbody2D rigid2d; float hp = 100f; float attack = 10f; bool enemyTurn = false; //敵のターンか void Start () { this.rigid2d = GetComponent<Rigidbody2D>(); gameManager = GameObject.Find("GameManager"); characterController = GameObject.Find("Character-Red"); } void Update () { if(this.enemyTurn == true){ enemyAttack(); this.enemyTurn = false; // ターン管理をゲームマネージャーにわたす gameManager.GetComponent<GameManager>().turnControl("Character"); } } // 攻撃 void enemyAttack(){ characterController.GetComponent<CharacterController>().characterHP(-this.attack); } // HP管理 public void enemyHP(float hp_){ this.hp += hp_; Debug.Log("Enemy HP:" + this.hp); } // ターン管理 public void setTurn(bool turn){ this.enemyTurn = turn; } } |
自キャラと構成は同じです。
●ゲームマネージャー
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 |
using System.Collections; using System.Collections.Generic; using UnityEngine; public class GameManager : MonoBehaviour { GameObject characterController; GameObject enemyController; void Start () { characterController = GameObject.Find("Character-Red"); enemyController = GameObject.Find("Enemy-Yellow"); } void Update () { } public void turnControl(string turn_){ switch(turn_){ case "Enemy": //敵にターンをわたす enemyController.GetComponent<EnemyController>().setTurn(true); break; case "Character": //味方にターンをわたす characterController.GetComponent<CharacterController>().setTurn(true); break; } } } |
ターンを切り替える turnControl
を持っています。
GameManager
を介さずとも ターン切替は可能です。
しかしキャラが増えたときや、HPがゼロになった場合にオブジェクトそのものがなくなることが想定されるため、GameManager
を使用しています。
試しに動かしてみます。
自キャラが敵に衝突するたびに、敵のHPが減っています。
また自キャラが動いている最中は、操作を受け付けません。
今回はここまで!
ターン処理は結構面倒ですね…。ターン処理用のアセット等 あるのかな?
次回はキャラ数を増やしてみます。