おっさんプログラマのUnity奮闘記

元WEB系プログラマのUnity奮闘記

おっさんのUnity入門 2Dオブジェクトでスコアを実装する(プレファブとUIの連携)

敵オブジェクトが量産できるようになりましたので、撃破ポイントをスコアとして表示するUIを実装します。

【追加・修正するオブジェクト】
  • EnemyStatus.cs(敵オブジェクトの状態管理用スクリプト)[修正]
  • UIControllder.cs(UIオブジェクトの管理用スクリプト)[修正]
  • CanvasWrapper/Canvas/ScoreText(スコア表示用テキストオブジェクト)[追加]
【スコア用UIオブジェクトの追加】

Hierachy→Create→UI→TextでテキストオブジェクトをCanvas直下に追加します。
UI text additional image

任意の位置・フォントの大きさ・フォントの色を指定します。
UI text adjustment image

UI text adjustment image by Inspector

【実装する機能(敵オブジェクト側(プレファブ))】
  • 敵自身が保持している撃破ポイントをUIオブジェクト側に渡す機能
  • UI表示更新ロック制御機能
【実装する機能(UIオブジェクト側)】
  • 撃破ポイントを格納するリスト
  • 撃破ポイントリストへ敵オブジェクト側から受け取った値を追加する機能
  • 撃破ポイントスコア表示機能
  • 撃破ポイントスコア表示更新機能
  • 撃破ポイントリストの外部呼出しをロックする機能(ほぼ同時に撃破した際に処理が衝突することを防止するため)

【EnemyStatus.cs(敵オブジェクトステータス管理スクリプト)】

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EnemyStatus : MonoBehaviour {

	// 敵の体力の入れ物
	// Inspectorで設定
	[SerializeField]
	private int enemyArmorPoint;

	// 撃破したときに受け取れるポイント
	// Inspectorで設定
	[SerializeField]
	private int enemyDestroyPoint;

	// Canvasの入れ物
	Canvas canvas;
	// UIControllerの入れ物
	UIController uicontroller;

	// 撃破時にUIControllerの撃破ポイントリスト呼び出し判定フラグ
	// falseなら呼び出し可
	private bool isActiveScore;

	void Start () {
		// 敵の体力を初期化
		//enemyArmorPoint = 3;

		// UIControllerを取得する
		// canvasを取得してからそのコンポーネントであるスクリプトを
		// 取得する
		canvas = GameObject.Find ("CanvasWrapper")
			.GetComponentInChildren<Canvas>();
		uicontroller = canvas.GetComponent<UIController> ();
		// UIControllerから撃破ポイントリスト呼び出し可能状態を取得
		isActiveScore = uicontroller.isActiveScoreUpdate;
	}


	// 弾オブジェクトと接触したときに呼び出される関数
	void OnCollisionEnter2D (Collision2D collision) {
		// もしもtagがbulletであるオブジェクトと接触したら
		if (collision.gameObject.tag == "bullet") {
			// 敵の体力が0以上だったら
			if (enemyArmorPoint > 0) {
				// 敵の体力を1削る
				enemyArmorPoint -= 1;
			} else {
				// 敵の撃破スコア更新が可能な状態の場合
				if (isActiveScore == false) {
					// 処理の衝突を防ぐためのisActiveScoreをロック
					uicontroller.isActiveScoreUpdate = true;
					// UIController側から上書きしたisActiveScoreの値を取得
					isActiveScore = uicontroller.isActiveScoreUpdate;
					// ロックが有効になっているか確認
					Debug.Log ("isActiveScoreUpdate : " + isActiveScore);
					// 反映されたisActiveScoreを取得
					isActiveScore = uicontroller.isActiveScoreUpdate;
					// 撃破ポイントリストに撃破ポイントを追加
					// 敵オブジェクト側では整数を渡すだけ
					uicontroller.EnemyDestroyPointListUpdate = enemyDestroyPoint;
					// ロックの解除
					uicontroller.isActiveScoreUpdate = false;
					// UIController側から上書きしたisActiveScoreの値を取得
					isActiveScore = uicontroller.isActiveScoreUpdate;
					// ロックが解除されているか確認
					Debug.Log ("isActiveScoreUpdate : " + isActiveScore);
					// 敵の体力が0になったら敵オブジェクトを消滅させる
					Destroy (gameObject);
				}
			}
		}
	}


}

【UIController.cs(UIオブジェクト管理スクリプト)】

/* UIControllder.cs */

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Linq; // リストの合計処理のために追加

public class UIController : MonoBehaviour {

	// gameoverTextオブジェクトの入れ物
	Text gameover_text;

	// PLAYERとENEMYの接触状態を表すフラグ
	// get/setメソッドで他のスクリプトから呼び出すためprivateにしておく
	private bool enemyTouch;

	// armorTextオブジェクトの入れ物
	Text armorText;
	// PlayerStatus.csの入れ物
	PlayerStatus player_status;
	// PLAYERの体力の入れ物
	private int armorPoint;

	// 撃破ポイントの入れ物
	private int DestroyPoint;
	// scoreTextオブジェクトの入れ物
	Text scoreText;
	// 敵の撃破ポイントの入れ物
	//private int EnemyDestroyPoint;
	// 敵の撃破ポイントの一時保管用リスト
	private List<int> EnemyDestroyPointList = new List<int> ();
	// 敵の撃破ポイント呼び出し状態フラグ
	// trueの時は撃破ポイント制御関数の呼び出しを待機状態にする
	private bool isActiveScore;
	// 敵の撃破ポイント制御関数内で使用するカウンタ
	private float timeOut = 0.5f;
	private float timeCount;


	void Start () {
		// PlayerStatusの取得
		player_status = GameObject.Find ("PLAYER_GREEN")
			.GetComponentInChildren<PlayerStatus>();
		// PLAYERの体力をPlayerStatus.csのArmorPointから取得して初期化
		armorPoint = player_status.ArmorPoint;
		// armorPointを初期化する
		armorText = GameObject.Find ("armorText").GetComponent<Text> ();
		armorText.text = "armorPoint : " + armorPoint.ToString ();

		// UIのテキストを取得する(gameoverTextへ代入する)
		gameover_text = GameObject.Find ("gameoverText").GetComponent<Text> ();

		// ゲームスタート時はgameoverTextを非表示にしておく
		gameover_text.enabled = false;

		// PLAYERとENEMYの接触状態を表すフラグをfalseに初期化
		enemyTouch = false;

		// UIのテキストを取得する(scoreTextへ代入する)
		scoreText = GameObject.Find("scoreText").GetComponent<Text>();
		// 敵の撃破ポイントを初期化
		DestroyPoint = 0;
		// 撃破ポイントリスト呼び出し判定フラグを初期化
		isActiveScore = false;
		// 撃破ポイントの初期化
		DestroyPoint = 0;
		// 撃破ポイントリストを空にする
		EnemyDestroyPointList.Clear();
		// 敵の撃破ポイント制御関数内で使用するカウンタの初期化
		timeCount = 0;

	}
	

	void Update () {
		// PLAYERとENEMYが接触したら
		if (enemyTouch == true) {
			// PLAYERの体力を更新する
			armorPoint = player_status.ArmorPoint;
			// PLAYERの体力表示を更新する
			armorText.text = "armorPoint : " + armorPoint.ToString ();
		}

		if (armorPoint == 0) {
			// ゲームオーバー表示をするための関数を呼び出す。
			GameOverMessage ();
		}

		// 敵の撃破ポイントスコア表示更新用カウンタ
		// 0.5秒になったら0.0秒にリセットされる
		timeCount += Time.deltaTime;
		if (isActiveScore == false) {
			// 0.5秒毎に更新する
			// 0.5秒以内ならば敵の撃破ポイントリストの追加のみ処理する
			// 敵の撃破ポイントリストの追加は敵オブジェクト側から制御する
			if (timeCount >= timeOut) {
				// 敵の撃破スコアを更新する関数の呼び出し
				ScoreMessage ();
				// timeCountが0.5秒になったら0.0秒にリセットされる
				timeCount = 0.0f;
			}
		}

		// 撃破ポイントリスト格納状態確認関数呼び出し
		//DestroyPointValue (EnemyDestroyPointList);
	}


	// 他のスクリプトから読み書きするための関数
	// 他のスクリプトから呼び出せるようにpublic属性を付ける
	// true/falseを呼び出し元のスクリプトへ渡すためbool型を付ける
	public bool EnemyTouch {
		get { return enemyTouch; }
		set { enemyTouch = value; }
	}


	// ゲームオーバー表示のためのフラグコントロール関数
	void GameOverMessage () {
		gameover_text.enabled = true;
	}


	// 敵の撃破ポイントリスト呼び出し状態判定フラグ更新関数
	public bool isActiveScoreUpdate {
		get { return isActiveScore; }
		set { isActiveScore = value; }
	}

	// 敵を撃破したポイントを制御する関数
	void ScoreMessage () {
		// 敵の撃破ポイントリストの合計値を取得
		int totalDestroyPoint = EnemyDestoryPointSum ();
		// 敵の撃破ポイントリストを空にする。
		EnemyDestroyPointListClear ();
		// 敵の撃破ポイントを加算する
		DestroyPoint = DestroyPoint + totalDestroyPoint;
		// UIの撃破スコアを更新する
		scoreText.text = "Score : " + DestroyPoint.ToString ();
	}

	// 敵の撃破ポイントリストの合計値を返す関数
	public int EnemyDestoryPointSum () {
		return EnemyDestroyPointList.Sum ();
	}

	// 敵の撃破ポイントリストを初期化する関数
	void EnemyDestroyPointListClear () {
		EnemyDestroyPointList.Clear ();
	}

	// 敵の撃破ポイントリスト更新関数
	// 加算のみ行うため整数型を受け取り関数無いで加算する
	public int EnemyDestroyPointListUpdate {
		set { EnemyDestroyPointList.Add (value); }
	}


	// 敵の撃破ポイントリスト確認関数
	void DestroyPointValue (List<int> EnemyDestroyPointList) {
		int i;
		for (i = 0; i < EnemyDestroyPointList.Count; i++) {
			Debug.Log ("[" + i + "] = " + EnemyDestroyPointList [i]);
		}
	}

}

【敵オブジェクトの耐久力と撃破ポイントを指定】

プレファブ内の敵オブジェクトRED_ENEMYを選択してInspectorを表示します。
Enemy Armor Point と Enemy Destroy Point に任意の整数値を入力します。
Image changing durability and destroy points installed in the prefab in the Inspector

【動作確認】

Image of operation confirmation and debugging
Inspectorで指定した値がScoreに加算されるようになりました。
Consoleではロックが実行されている様子が確認できました。



次回は、敵プレファブのバリエーションを増やしてスクリプトの使いまわしを試します。


【参考にさせていただいたサイト】

qiita.com

おっさんのUnity入門 2Dオブジェクトで敵を量産する(プレファブの使い方)

敵オブジェクトを量産する仕組みを実装していきます。

【追加するオブジェクト】

  • GameController
  • EnemyGenerater.cs


【RED_ENEMYのプレファブ化】
Prefabricating RED_ENEMY

RED_ENEMYを任意の場所(この場合Prefabs)へドロップします。

【GameControllerの追加】
ゲーム全体をコントロールするためのオブジェクトを追加します。
Add GameController

GameControllerに敵を自動的に生成するためのスクリプトEnemyGenerator.csを追加します。


【EnemyGenerator.cs】

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EnemyGenerator : MonoBehaviour {

	// 敵オブジェクト(プレファブ)の入れ物
	// 敵オブジェクトはInspectorで指定する
	[SerializeField]
	private GameObject enemyObj;

	// プレイヤーオブジェクトの入れ物
	private GameObject playerObj;

	void Start () {
		// プレイヤーオブジェクトを取得
		playerObj = GameObject.Find ("PLAYER_GREEN");

		// プレイヤーからx軸方向に5、y軸方向に2移動した位置に
		// 敵を出現させる
		Instantiate (enemyObj, 
			playerObj.transform.position
			+ new Vector3 (5, 2, 0), transform.rotation);
	}

}


【量産に使用するプレファブを指定】
先ほどプレファブ化したRED_ENEMYを指定します。
Designate prefab for mass production


Automatically generated image
Sceneに配置していない敵オブジェクトが登場しました。

【不要となった敵オブジェクトをSceneから削除する】
Delete unnecessary enemy objects from Scene

Delete enemy object


【動作確認】
Operation check

プレファブから生成された敵オブジェクトのみが出現するようになりました。

今度は一定時間毎に敵が生成されるように修正します。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EnemyGenerator : MonoBehaviour {

	// 敵オブジェクト(プレファブ)の入れ物
	// 敵オブジェクトはInspectorで指定する
	[SerializeField]
	private GameObject enemyObj;

	// プレイヤーオブジェクトの入れ物
	private GameObject playerObj;

	// 敵を生成するまでの時間
	float timer;
	// 敵を生成するまでの閾値
	float instantiateInterval;
	// 敵の最大生成数
	int maxInstanceValue;


	void Start () {

		timer = 3;
		instantiateInterval = 3;
		maxInstanceValue = 10;

		// プレイヤーオブジェクトを取得
		playerObj = GameObject.Find ("PLAYER_GREEN");

	}


	void Update () {
		// 敵オブジェクトの生成関数を呼び出す
		GenerateEnemy ();
	}


	// 敵オブジェクトの生成関数
	void GenerateEnemy () {
		// カウンタ
		timer -= Time.deltaTime;

		if (timer < 0) {
			if (maxInstanceValue > 0) {
				// プレイヤーからx軸方向に10、y軸方向に1移動した位置に
				// 敵を出現させる
				Instantiate (enemyObj, 
					playerObj.transform.position
					+ new Vector3 (10, 1, 0), transform.rotation);
				// 最大出現数を減らす
				maxInstanceValue--;
				// タイマーのリセット
				timer = 3;
			}
		}
	}
}


【動作確認】
Operation check

一定間隔かつ一定距離で敵が量産されるようになりました。

おっさんのUnity入門 2Dオブジェクトで敵接触時のダメージアクションを加える

f:id:gomunpass:20180311144119j:plain

敵と接触したときに後方へ弾き飛ばされるアクションを実装してみます。

プレイヤーの移動スクリプトを修正します。

【PlayerMoveScript.cs】

using System.Collections;
using System.Collections.Generic;
using UnityEngine;


public class PlayerMoveScript: MonoBehaviour {

	public Rigidbody2D rb2d;
	private bool isGrounded;
	private bool enemyTouch;
	GameObject Enemy;

	// Canvasの入れ物
	Canvas canvas;
	// UIControllerの入れ物
	UIController uicontroller;

	void Start () {
		// オブジェクトのRigidbody2Dを取得
		rb2d = GetComponent<Rigidbody2D> ();

		// UIControllerを取得する
		// canvasを取得してからそのコンポーネントであるスクリプトを
		// 取得する
		canvas = GameObject.Find ("CanvasWrapper")
			.GetComponentInChildren<Canvas>();
		uicontroller = canvas.GetComponent<UIController> ();

		// ENEMYとの接触状態を非接触であるfalseへ設定する
		// UIControllder内にあるEnemyTouch関数から接触状態を取得して代入する
		enemyTouch = uicontroller.EnemyTouch;

	}

	void Update () {
		// 地面に接触しているときだけ操作可能にする
		if (isGrounded == true) {		
			// 左右のキー入力を取得
			float moveparam = Input.GetAxis ("Horizontal");
			// 左右方向移動のためオブジェクトに力を加える
			rb2d.AddForce (Vector2.right * moveparam * 10f);

			// スペースキー入力でオブジェクトをジャンプさせる
			if (Input.GetKeyDown (KeyCode.Space)) {
				rb2d.AddForce (new Vector2 (0, 9.8f), ForceMode2D.Impulse);
				isGrounded = false;
			}
		}
	}

	void FixedUpdate () {

		// ENEMYとの接触状態を非接触であるfalseへ設定する
		// UIControllder内にあるEnemyTouch関数から接触状態を取得して代入する
		enemyTouch = uicontroller.EnemyTouch;

		Debug.Log (enemyTouch);

		if (enemyTouch == true) {
			BlowOff (Enemy);
			uicontroller.EnemyTouch = false;
		}
	}


	// 何らかのオブジェクト同士が接触したときの処理
	void OnCollisionEnter2D (Collision2D collision) {
		// 空中での連続ジャンプを抑制するため地面との接触を感知するフラグの操作
		// 地面とするオブジェクトにタグ(ground)をInspector上から追加しておく
		if (collision.gameObject.tag == "ground") {
			isGrounded = true;
		}

		// ENEMYに接触したときに動作する関数
		//(Inspectorで作成したenemyタグを事前にRED_ENEMYに適応しておく)
		// 接触したオブジェクトがenemyのtagが付いたオブジェクトだったら
		if (collision.gameObject.tag == "enemy") {
			// 接触した敵オブジェクトをEnemyに渡す
			Enemy = collision.gameObject;
			enemyTouch = true;
			EnemyTouchSend ();
		}
	}

	// PLAYERが他のオブジェクトと接触し続けているときに呼び出される関数
	void OnCollisionStay2D (Collision2D collision) {
		if (collision.gameObject.tag == "ground") {
			isGrounded = true;
		}
	}

	// PLAYERが他のオブジェクトと接触しなくなったときに呼び出される関数
	void OnCollisionExit2D (Collision2D collision) {
		if (collision.gameObject.tag == "ground") {
			isGrounded = false;
		}

		if (collision.gameObject.tag == "enemy") {
			enemyTouch = false;
			EnemyTouchSend ();
		}
	}

	// ENEMYとの接触時の動作を制御する関数
	void EnemyTouchSend () {
		// UIController.csのEnemyTouchを介してenemyTouchの値を書き換える
		uicontroller.EnemyTouch = enemyTouch;
	}


	// ENEMYと接触後に弾かれる動作を制御する関数
	void BlowOff (GameObject Enemy) {
		// 敵の位置を取得
		Vector3 enemyTFP = Enemy.transform.position;
		// プレイヤー自身の位置を取得
		Vector3 playerTFP = transform.position;

		if (enemyTFP.x - playerTFP.x > 0) {
			// 敵がプレイヤーより右側にいる時の処理
			playerTFP = enemyTFP + new Vector3 (-2, 1, 0);
			transform.position = playerTFP;
		} else if (enemyTFP.x - playerTFP.x <= 0) {
			// 敵がプレイヤーより左側にいる時の処理
			playerTFP = enemyTFP + new Vector3 (2, 1, 0);
			transform.position = playerTFP;
		}
	}

}


f:id:gomunpass:20180311144130j:plain

プレイヤーから見て後方へ弾き飛ばされるアクションを実装できました。

おっさんのUnity入門 2Dオブジェクトで弾を飛ばして敵を倒す(オブジェクトの削除とレイヤーの使い方)

f:id:gomunpass:20180224103812j:plain

弾となるオブジェクトを追加して敵オブジェクトを撃退する動作を実装します。

【追加するオブジェクト】

  • Bullet (スプライトをSceneへ追加して作成)

Bulletの配置位置は敵オブジェクトに当たるくらいの高さに設定します。


【BulletのInspector設定】
f:id:gomunpass:20180224103824j:plain

【追加するComponent】

  • Box Collider 2D(Add Component > Physics 2D > Box Collider 2D)
  • Rigidbody 2D(Add Component > Physics 2D > Rigidbody 2D)

Hierachyにある弾オブジェクトBulletは、後で量産して発射できるようにPrefab(プレファブ)化しますが、編集の都合でひとまずSceneへ配置した状態で編集します。
Rigidbody 2DのGravity Scaleに微小な数値を指定して徐々に弾が下降するようにします。


【Bullet.cs】

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Bullet : MonoBehaviour {

	// 弾オブジェクト(Inspectorでオブジェクトを指定)
	[SerializeField] // Inspectorで操作できるように属性を追加します
	private GameObject bullet;
	// 弾オブジェクトのRigidbody2Dの入れ物
	private Rigidbody2D rb2d;
	// 弾オブジェクトの移動係数(速度調整用)
	float bulletSpeed;

	void Start () {
		// オブジェクトのRigidbody2Dを取得
		rb2d = GetComponent<Rigidbody2D> ();
		// 弾オブジェクトの移動係数を初期化
		bulletSpeed = 10.0f;
	}

	void Update () {
		// 弾オブジェクトの移動関数
		BulletMove ();	
	}

	// 弾オブジェクトの移動関数
	void BulletMove () {
		// 弾オブジェクトの移動量ベクトルを作成(数値情報)
		Vector2 bulletMovement = new Vector2 (1, 0).normalized;
		// Rigidbody2D に移動量を加算する
		rb2d.velocity = bulletMovement * bulletSpeed;
	}

	// ENEMYと接触したときの関数
	void OnCollisionEnter2D (Collision2D collision) {
		// ENEMYに弾が接触したら弾は消滅する
		if (collision.gameObject.tag == "enemy") {
			Destroy (gameObject);
		}
	}
}

弾オブジェクトが敵オブジェクトと接触すると敵オブジェクトを画面の外まで押し出してしまうので、敵オブジェクトに接触したら消滅するようにDestory関数を使います。



【Bullet.csのBulletオブジェクトにHierachyのBulletを指定する】
f:id:gomunpass:20180224103832j:plain

Bulletフォームの右横にある〇をクリックしてBulletを指定します。



f:id:gomunpass:20180224103838j:plain
弾オブジェクトが配置位置から直進します。

f:id:gomunpass:20180224103847j:plain
敵オブジェクトと接触するとSceneから削除されます。


【弾オブジェクトの量産化】

プレイヤーオブジェクトの位置から弾オブジェクトを発射できるように改良しています。


【弾オブジェクトのプレファブ化】

スクリプトから動的にオブジェクトを生成する場合は、プレファブと呼ばれるオブジェクトのテンプレートを呼び出すことで実現できます。

f:id:gomunpass:20180224115347j:plain

プレファブを作るには、HierachyにあるオブジェクトをProject内の任意の場所へドラッグ&ドロップします。管理しやすいように予めProject直下にPrefabsというフォルダを作っておきます(Project > Create > Folder)。
プレファブ化が完了するとHierachyにあるオブジェクト名が青い表示に変化します。

※HierachyにあるBulletは必要なくなったので、右クリック > Delete で削除します。

※発射されたときの弾オブジェクトの生成位置を発射位置の中心になるように初期化しておきます。
 Prefabs > Bullet を選択して Transform > Position「X:0 Y:0 Z:0」へ設定しておきます。
f:id:gomunpass:20180224121426j:plain



【弾オブジェクトを発射するための設定】

弾オブジェクトを発射するためのキーの設定をします。

Edit > ProjectSettings > Input でキー入力ウィンドウを表示します。

f:id:gomunpass:20180224120535j:plain

Axes > Fire1 > Positive Button へ「a」を指定して、「a」キーをタイプするたびに弾オブジェクトが発射されるようにします。


【弾オブジェクトとプレイヤーの衝突回避設定】
弾オブジェクトはBox Collider 2Dが設定されているため、プレイヤーオブジェクトとも衝突することになります。現在の設定では、プレイヤーオブジェクトの中心から発射されるためプレイヤーも発射した弾にはじかれてしまいます。
Unityにはレイヤーというオブジェクトが配置される層の重なりを設定することができる機能があります。別レイヤーに配置されたオブジェクト同士は衝突しなくなるためこれを利用します。

【レイヤーの追加設定】
f:id:gomunpass:20180224121949j:plain

Edit > ProjectSettings > Tags and Layers でレイヤーを設定するウィンドウを表示します。
Layers >「User Layer 8 : Player」「User Layer 9 : Bullet」それぞれ入力します。


【レイヤーと物理動作の設定】
f:id:gomunpass:20180224122008j:plain

Edit > ProjectSettings > Physics2D でレイヤー同士の衝突動作を設定します。
※Physics ではなくPhysics2Dであることに注意

縦軸Bullet 横軸Playerの重なっている部分のチェックボックスを外すと物理的な衝突が発生しなくなります。
※コライダーによる衝突判定(OnCollisionEnter2D)は物理的衝突動作を解除した後も有効です。


【プレイヤーに発射用スクリプトを追加する】

衝突関係の設定ができたので、プレイヤーに弾オブジェクトを発射するためのスクリプトを追加します。

【PLAYER_GREENオブジェクトに追加するComponent】

プレイヤーを基準として弾オブジェクトが右方向へ発射されるようになります。

【PlayerBullet.cs】

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerShot : MonoBehaviour {

	// InspectorでPrefab化したBulletを指定する
	[SerializeField]
	private GameObject bullet;

	void Update () {
		// 弾オブジェクトを生成して飛ばす関数を呼び出す
		ShotAction ();	
	}

	// 弾オブジェクトを生成して飛ばす関数
	void ShotAction () {
		if (Input.GetButtonDown ("Fire1")) {
			Instantiate (bullet, transform.position, transform.rotation);
		}
	}
}


【PLAYER_GREENのInspectorでプレファブ化したBulletを指定する】

f:id:gomunpass:20180224123954j:plain

Hierachy > PLAYER_GREEN > Inspector > Bullet (Script) > Bullet のフォーム右側にある〇をクリックしてPrefabs内のBulletを指定します。


f:id:gomunpass:20180224124301j:plain

キーボードの「a」をタイプすると弾オブジェクトが発射されるようになりました。


f:id:gomunpass:20180224125607j:plain

現時点では敵オブジェクトに当たらない弾オブジェクトはメモリ上にずっと存在していることになります。撃ち続けるとどんどんメモリを消費してしまうため、一定時間後に自動的にメモリ上から削除するように弾オブジェクト移動用スクリプトBullet.csを修正します。

【Bullet.cs】

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Bullet : MonoBehaviour {

	// 弾オブジェクト(Inspectorでオブジェクトを指定)
	[SerializeField] // Inspectorで操作できるように属性を追加します
	private GameObject bullet;
	// 弾オブジェクトのRigidbody2Dの入れ物
	private Rigidbody2D rb2d;
	// 弾オブジェクトの移動係数(速度調整用)
	float bulletSpeed;

	void Start () {
		// オブジェクトのRigidbody2Dを取得
		rb2d = GetComponent<Rigidbody2D> ();
		// 弾オブジェクトの移動係数を初期化
		bulletSpeed = 10.0f;
		// 出現から3秒後に弾オブジェクトを消滅させる(メモリの節約)
		Destroy (gameObject, 3.0f);
	}

	void Update () {
		// 弾オブジェクトの移動関数
		BulletMove ();	
	}

	// 弾オブジェクトの移動関数
	void BulletMove () {
		// 弾オブジェクトの移動量ベクトルを作成(数値情報)
		Vector2 bulletMovement = new Vector2 (1, 0).normalized;
		// Rigidbody2D に移動量を加算する
		rb2d.velocity = bulletMovement * bulletSpeed;
	}

	// ENEMYと接触したときの関数
	void OnCollisionEnter2D (Collision2D collision) {
		// ENEMYに弾が接触したら弾は消滅する
		if (collision.gameObject.tag == "enemy") {
			Destroy (gameObject);
		}
	}
}


f:id:gomunpass:20180224125621j:plain

一定時間(この場合3秒)後に自動的に生成されたBulletオブジェクトが削除されました。


【敵オブジェクトの削除】

続いて敵に体力を設定して体力が0になったら消滅するように改良していきます。

【敵オブジェクトに敵の体力を管理するスクリプトを追加します。】
f:id:gomunpass:20180224135057j:plain

【EnemyStatus.cs】

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EnemyStatus : MonoBehaviour {

	// 敵の体力の入れ物
	[SerializeField]
	private int enemyArmorPoint;


	void Start () {
		// 敵の体力を初期化
		enemyArmorPoint = 3;
	}
	

	// 弾オブジェクトと接触したときに呼び出される関数
	void OnCollisionEnter2D (Collision2D collision) {
		// もしもtagがbulletであるオブジェクトと接触したら
		if (collision.gameObject.tag == "bullet") {
			// 敵の体力が0以上だったら
			if (enemyArmorPoint > 0) {
				// 敵の体力を1削る
				enemyArmorPoint -= 1;
			} else {
				// 敵の体力が0になったら敵オブジェクトを消滅させる
				Destroy (gameObject);
			}
		}
	}
}


【弾オブジェクトへタグを追加する】

敵オブジェクトが弾オブジェクトを認識できるようにするためbulletというタグを追加します。

f:id:gomunpass:20180224135109j:plain

Prefabs > Bullet > Inspector > Tag > Add Tag ... を選択してタグ追加ウィンドウを表示します。

f:id:gomunpass:20180224135120j:plain

「bullet」と入力して「save」をクリックします。

f:id:gomunpass:20180224135134j:plain

bulletが追加されて使用可能になりました。

f:id:gomunpass:20180224135146j:plain

Prefabs > Bullet > Inspector > Tag > bullet を選択します。


【動作確認】
f:id:gomunpass:20180224135157j:plain

f:id:gomunpass:20180224135215j:plain

敵の体力が0になってシーンから削除された。

おっさんのUnity入門 2Dオブジェクトでプレイヤーに体力を設定する(UIテキストとプレイヤーオブジェクトの連携)

f:id:gomunpass:20180221210306j:plain

前回のシーンにPLAYERの体力を設定して体力が0になったらゲームオーバーの表示が出るように改良します。

【追加するオブジェクト】

  • armorText(Hierarchy > UI > Text)


f:id:gomunpass:20180221210317j:plain

Font Size や Color などを適当に指定します。

【PlayerStaus.cs】
Playerの状態を管理するスクリプトを作成します。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerStatus : MonoBehaviour {

	// PLAYERの体力
	[SerializeField] // Inspectorで確認できるように属性を付ける
	private int armorPoint;

	void Start () {
		// PLAYERの体力を初期化
		armorPoint = 10;
	}

	// PLAYERの体力を更新する関数
	public int ArmorPoint {
		get { return armorPoint; }
		set { armorPoint = value; }
	}

	// ENEMYとの接触時に動作する関数
	void OnCollisionEnter2D (Collision2D collision) {
		// 接触したオブジェクトがenemyのtagが付いたオブジェクトだったら
		if (collision.gameObject.tag == "enemy") {
			int tmpPoint = ArmorPoint;
			tmpPoint = tmpPoint - 2;
			ArmorPoint = tmpPoint;
		}
	}
}


f:id:gomunpass:20180221215409j:plain


【UIController.cs】
PlayerStatus.csからPLAYERの体力を受け取って表示を変更するように修正します。
ゲームオーバーの表示タイミングを変更して体力が0になったら表示するように修正します。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class UIController : MonoBehaviour {

	// gameoverTextオブジェクトの入れ物
	Text gameover_text;

	// PLAYERとENEMYの接触状態を表すフラグ
	// get/setメソッドで他のスクリプトから呼び出すためprivateにしておく
	private bool enemyTouch;

	// armorTextオブジェクトの入れ物
	Text armorText;
	// PlayerStatus.csの入れ物
	PlayerStatus player_status;
	// PLAYERの体力の入れ物
	private int armorPoint;


	void Start () {
		// PlayerStatusの取得
		player_status = GameObject.Find ("PLAYER_GREEN")
			.GetComponentInChildren<PlayerStatus>();
		// PLAYERの体力をPlayerStatus.csのArmorPointから取得して初期化
		armorPoint = player_status.ArmorPoint;
		// armorPointを初期化する
		armorText = GameObject.Find ("armorText").GetComponent<Text> ();
		armorText.text = "armorPoint : " + armorPoint.ToString ();

		// UIのテキストを取得する(gameoverTextへ代入する)
		gameover_text = GameObject.Find ("gameoverText").GetComponent<Text> ();

		// ゲームスタート時はgameoverTextを非表示にしておく
		gameover_text.enabled = false;

		// PLAYERとENEMYの接触状態を表すフラグをfalseに初期化
		enemyTouch = false;
	}
	

	void Update () {
		// PLAYERとENEMYが接触したら
		if (enemyTouch == true) {
			// PLAYERの体力を更新する
			armorPoint = player_status.ArmorPoint;
			// PLAYERの体力表示を更新する
			armorText.text = "armorPoint : " + armorPoint.ToString ();
		}

		if (armorPoint == 0) {
			// ゲームオーバー表示をするための関数を呼び出す。
			GameOverMessage ();
		}
	}

	// 他のスクリプトから読み書きするための関数
	// 他のスクリプトから呼び出せるようにpublic属性を付ける
	// true/falseを呼び出し元のスクリプトへ渡すためbool型を付ける
	public bool EnemyTouch {
		get { return enemyTouch; }
		set { enemyTouch = value; }
	}

	void GameOverMessage () {
		gameover_text.enabled = true;
	}
}

f:id:gomunpass:20180221210325j:plain

左上にPLAYERの体力が表示されるようになりました。
ENEMYに1回接触すると2ポイント減るように設定しています。

f:id:gomunpass:20180221210334j:plain

体力が0になるとゲームオーバーが表示されます。

おっさんのUnity入門 2Dオブジェクトの敵キャラクターを動かす(オブジェクトの自動追尾)

f:id:gomunpass:20180218120150j:plain
前回のシーンに敵キャラクターENEMYを追加してPLAYERを追尾する動作を実装してみます。

【追加するオブジェクト】
  • RED_ENEMY(スプライトをSceneへドラッグして作成)
【RED_ENEMYの設定】

f:id:gomunpass:20180218155739j:plain
このあと実装する「ゲームオーバー表示動作」で必要となる各オブジェクト識別のためのtagを追加します
Tag のプルダウンから<Add Tag ...>をクリック
f:id:gomunpass:20180218155748j:plain
「+」をクリックして「enemy」と入力します。
f:id:gomunpass:20180218155654j:plain
Box Collider 2D と Rigidbody 2D を追加して物理動作をできるようにします。

【EnemyMoveScript.cs(ENEMYへ追加するスクリプト)】
/* EnemyMoveScript.cs */

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EnemyMoveScript : MonoBehaviour {

	GameObject player_green;

	private Rigidbody2D rb2d;

	void Start () {
		// オブジェクトのRigidbody2Dを取得
		rb2d = GetComponent<Rigidbody2D> ();
		// PLAYERオブジェクトを取得
		player_green = GameObject.Find ("PLAYER_GREEN");
	}
	
	void Update () {
		// 移動関数の呼び出し
		EnemyMove ();
	}

	// ENEMYの移動関数1フレーム毎にUpdate関数から呼び出される
	void EnemyMove () {
		// PLAYERの位置を取得
		Vector2 targetPos = player_green.transform.position;
		// PLAYERのx座標
		float x = targetPos.x;
		// ENEMYは、地面を移動させるので座標は常に0とする
		float y = 0;
		// 移動を計算させるための2次元のベクトルを作る
		Vector2 direction = new Vector2 (
			x - transform.position.x, y).normalized;
		// ENEMYのRigidbody2Dに移動速度を指定する
		rb2d.velocity = direction * 2;
	}

}


f:id:gomunpass:20180218120205j:plain

地面を水平移動しながらPLAYERへ近づいてくる動作ができました。

【参考にさせていただいた記事】

qiita.com



f:id:gomunpass:20180218151058j:plain

続いてENEMYと接触したときにゲームオーバー表示するように改良してみます。

【追加するオブジェクト】
  • CanvasWrapper(空のオブジェクト Hierarchy > Create > Create Empty)
  • gameoverText(Textオブジェクト Hierarchy > Create > UI > Text)
【CanvasWrapperの設定】

f:id:gomunpass:20180218151116j:plain


Canvasの設定】

f:id:gomunpass:20180218151131j:plain
Canvas / Render Mode<Screne Space - Camera>
Canvas / Render Camera<Main Camera(Camera)> 右端の〇ボタンから選択する
Canvas / Order in Layer <1>

【gameoverTextの設定】

f:id:gomunpass:20180218151148j:plain
Text(Script) / Font Size <120>
Text(Script) / Alignment それぞれ真ん中のボタンをクリックする
Text(Script) / Color 四角い部分をクリックして好みの色を選択する

【PlayerMoveScript.cs】PLAYERの制御
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerMoveScript: MonoBehaviour {

	public Rigidbody2D rb2d;
	public bool isGrounded;
	private bool enemyTouch;
	// Canvasの入れ物
	Canvas canvas;
	// UIControllerの入れ物
	UIController uicontroller;

	void Start () {
		// オブジェクトのRigidbody2Dを取得
		rb2d = GetComponent<Rigidbody2D> ();

		// UIControllerを取得する
		// canvasを取得してからそのコンポーネントであるスクリプトを
		// 取得する
		canvas = GameObject.Find ("CanvasWrapper")
			.GetComponentInChildren<Canvas>();
		uicontroller = canvas.GetComponent<UIController> ();

		// ENEMYとの接触状態を非接触であるfalseへ設定する
		// UIControllder内にあるEnemyTouch関数から接触状態を取得して代入する
		enemyTouch = uicontroller.EnemyTouch;
	}

	void FixedUpdate () {
		// 地面に接触しているときだけ操作可能にする
		if (isGrounded == true) {		
			// 左右のキー入力を取得
			float moveparam = Input.GetAxis ("Horizontal");
			// 左右方向移動のためオブジェクトに力を加える
			rb2d.AddForce (Vector2.right * moveparam * 10f);

			// スペースキー入力でオブジェクトをジャンプさせる
			if (Input.GetKeyDown (KeyCode.Space)) {
				rb2d.AddForce (new Vector2 (0, 9.8f), ForceMode2D.Impulse);
				isGrounded = false;
			}
		}
	}

	// 何らかのオブジェクト同士が接触したときの処理
	void OnCollisionEnter2D (Collision2D collision) {
		// 空中での連続ジャンプを抑制するため地面との接触を感知するフラグの操作
		// 地面とするオブジェクトにタグ(ground)をInspector上から追加しておく
		if (collision.gameObject.tag == "ground") {
			isGrounded = true;
		}

		// ENEMYに接触したときに動作する関数
		//(Inspectorで作成したenemyタグを事前にRED_ENEMYに適応しておく)
		// 接触したオブジェクトがenemyのtagが付いたオブジェクトだったら
		if (collision.gameObject.tag == "enemy") {
			// enemyTouchが非表示を表すfalse状態だったらEnemyTouch関数を呼び出す
			if (enemyTouch == false) {
				EnemyTouchSend ();
			}
		}
	}

	// ENEMYとの接触時の動作を制御する関数
	void EnemyTouchSend () {
		// UIController.csのEnemyTouchを介してenemyTouchの値をtrueに書き換える
		uicontroller.EnemyTouch = true;

	}

}
【EnemyMoveScript.cs】ENEMYの制御
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EnemyMoveScript : MonoBehaviour {

	GameObject player_green;

	private Rigidbody2D rb2d;

	void Start () {
		// オブジェクトのRigidbody2Dを取得
		rb2d = GetComponent<Rigidbody2D> ();
		// PLAYERオブジェクトを取得
		player_green = GameObject.Find ("PLAYER_GREEN");
	}
	
	void Update () {
		// 移動関数の呼び出し
		EnemyMove ();
	}

	// ENEMYの移動関数1フレーム毎にUpdate関数から呼び出される
	void EnemyMove () {
		// PLAYERの位置を取得
		Vector2 targetPos = player_green.transform.position;
		// PLAYERのx座標
		float x = targetPos.x;
		// ENEMYは、地面を移動させるので座標は常に0とする
		float y = 0;
		// 移動を計算させるための2次元のベクトルを作る
		Vector2 direction = new Vector2 (
			x - transform.position.x, y).normalized;
		// ENEMYのRigidbody2Dに移動速度を指定する
		rb2d.velocity = direction * 2;
	}

}
【UIController.cs】テキストオブジェクトなどの制御
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class UIController : MonoBehaviour {

	// gameoverTextオブジェクトの入れ物
	Text gameover_text;

	// PLAYERとENEMYの接触状態を表すフラグ
	// get/setメソッドで他のスクリプトから呼び出すためprivateにしておく
	private bool enemyTouch;


	void Start () {
		// UIのテキストを取得する(gameoverTextへ代入する)
		gameover_text = GameObject.Find ("gameoverText").GetComponent<Text> ();
		//Debug.Log (gameover_text);
		// ゲームスタート時はgameoverTextを非表示にしておく
		gameover_text.enabled = false;

		// PLAYERとENEMYの接触状態を表すフラグをfalseに初期化
		enemyTouch = false;
	}
	

	void Update () {
		// PLAYERとENEMYが接触したら
		if (enemyTouch == true) {
			// ゲームオーバー表示をするための関数を呼び出す。
			GameOverMessage ();
		}
	}

	// 他のスクリプトから読み書きするための関数
	// 他のスクリプトから呼び出せるようにpublic属性を付ける
	// true/falseを呼び出し元のスクリプトへ渡すためbool型を付ける
	public bool EnemyTouch {
		get { return enemyTouch; }
		set { enemyTouch = value; }
	}

	void GameOverMessage () {
		gameover_text.enabled = true;
	}
}


f:id:gomunpass:20180218151201j:plain
ゲームスタート時は、ゲームーオーバー表示は非表示になっている


f:id:gomunpass:20180218151212j:plain
PLAYERとENEMYが接触すると非表示になっているゲームオーバーテキストが表示される

おっさんのUnity入門 2Dオブジェクトを動かす(プレイヤー操作)

f:id:gomunpass:20180216202848j:plain

スクウェアオブジェクトをPCのキーボードで動くしくみを実装してみます。

【用意するオブジェクト】
  • PLAYER_GREEN
  • Ground
【実現したい動作】
  • PCの左右矢印キーを使って左右の移動が出来るようにする
  • スペースキーをタイプするとジャンプするようにする
  • 空中で連続ジャンプしないように地面接触判定を加える
【PLAYERを動かすスクリプト
/* PlayerMoveScript */

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerMoveScript: MonoBehaviour {

	public Rigidbody2D rb2d;
	public bool isGrounded;

	void Start () {
		// オブジェクトのRigidbody2Dを取得
		rb2d = GetComponent<Rigidbody2D> ();
	}

	void FixedUpdate () {
		// 左右のキー入力を取得
		float moveparam = Input.GetAxis ("Horizontal");
		// 左右方向移動のためオブジェクトに力を加える
		rb2d.AddForce (Vector2.right * moveparam * 10f);

		// スペースキー入力でオブジェクトをジャンプさせる
		if (Input.GetKeyDown (KeyCode.Space)) {
			if (isGrounded == true) {
				rb2d.AddForce (new Vector2 (0, 9.8f), ForceMode2D.Impulse);
				isGrounded = false;
			}
		}
	}

	// 空中での連続ジャンプを抑制するため地面との接触を感知するフラグの操作
	// 地面とするオブジェクトにタグ(ground)をInspector上から追加しておく
	void OnCollisionEnter2D (Collision2D collision) {
		if (collision.gameObject.tag == "ground") {
			isGrounded = true;
		}
	}
}
【PLAYERの設定】

f:id:gomunpass:20180216203749j:plain
Box Collider 2DとRigidbody 2Dを追加して物理動作できるようにする。

【地面の設定】

f:id:gomunpass:20180216203810j:plain
Box Collider 2Dを追加して地面をすり抜けないようにする。

【動作イメージ(移動しながらジャンプ)】

f:id:gomunpass:20180216203824j:plain


次回は敵オブジェクトを追加して死亡動作などを追加してみます。


この記事のもっと詳しい内容を知りたい方はこちらのページををご覧ください。
【Unity 始め方 入門】サンプルコード付き 2Dオブジェクトを動かす(プレイヤー操作:Input.GetAxis)解説yokubari.online




独自ブログ始めました
よくばりオンラインyokubari.online