Unity(C#)初心者・入門者向けチュートリアル ひよこのたまご

AndroidやiOS向けアプリを簡単に作れるゲーム開発環境Unity(ユニティ)の使い方を、チュートリアル方式で一緒に学びましょう!

【Unity7】uGUIでUIの表示とレベル機能を実装したい!【2Dローグライク12】

Unity5.0.0f4 Personal(2015年6月)

f:id:hiyotama:20150519220636p:plain

前回の続きです〜


今回は、ゲーム画面にUIを表示させます〜ゲームが始まった時に表示されるレベル(「Day 1」とか)と、プレイヤーの体力を表すFoodを画面に表示させます〜

また、それらUIに表示されている数値を、スクリプトから操作して変更します〜

【目標】UIを表示させ、UIの数値を変更する



2D Roguelike 12 of 14 : UI & Levels - YouTube



①UIを作成する
まずはDay1やDay2などと表示してレベルを示すUIを作成します〜始めに黒い画像を背景として表示し、その上に白いテキストを載せる形で進めていきます〜
HierarchyからCreate>UI>Imageを選択して下さい〜Imageの名前はLevelImageにして下さい〜


f:id:hiyotama:20150602185728p:plain
Imageを作成 名前はLevelImage


LevelImageのInspectorビューに移って下さい〜次に画像の赤枠部分を選択し、altボタンを押しながら右下のパネルを選択して下さい〜


f:id:hiyotama:20150602190237p:plain
altを押しながら右下を選択


この状態でSceneビューを見てみると、画面全体をLevelImageが覆った状態となっていることが分かります〜


f:id:hiyotama:20150602190618p:plain
画面全体が白く覆われる


ImageコンポーネントのColorを黒にして、背景は完成です〜


f:id:hiyotama:20150602190931p:plain
Colorを黒にする


続いてレベルを表示するテキストを作成していきます〜HierarchyからCreate>UI>Textを選択し、名前をLevelTextに変更して下さい〜
また、LevelTextはLevelImageの子オブジェクトにします〜


f:id:hiyotama:20150602192719p:plain
LevelTextをLevelImageの子オブジェクトにする


それではInspectorの設定を行います〜まずはテキストを画面の中央へ持っていきたいので、下画像の赤枠をクリックし、中央のパネルをaltを押しながらクリックして下さい〜


f:id:hiyotama:20150602192958p:plain


テキストを「Day 1」に、フォントをPressStart2P-Regularに、テキストサイズを32に、Alignment(文字位置)を中央に、テキストカラーを白に変更します〜


f:id:hiyotama:20150602193604p:plain
こんな感じ


これで画面中央に文字が表示されるはずなのですが、うまく表示されません〜これは、文字サイズが文字の枠をはみ出してしまっているからです〜
こういった場合、Overflowの設定を行うことで文字が文字枠からはみ出していても表示させることができます〜Horizontal OverflowとVertical OverflowをOverflowに変更して下さい〜


f:id:hiyotama:20150602193758p:plain
Overflow設定

f:id:hiyotama:20150602193827p:plain
文字が表示されるようになった


最後にプレイヤーの体力を表すFoodのUIを作成します〜HierarchyからCreate>UI>Textを選択し、名前をFoodTextに変更して下さい〜


続いて基点を画面下側に設定します〜赤枠クリック後、altを押しながら基点が下にくるパネルをクリックして下さい〜


f:id:hiyotama:20150602195348p:plain
AnchorをBottomに設定


続いてテキストを「Food: 100」、フォントをPressStart2P-Regular、文字サイズを24、Alignmentを中央、文字色を白に変更して下さい〜また、先ほどと同じくOverflowの設定も行います〜


f:id:hiyotama:20150602195000p:plain
このように設定すると


f:id:hiyotama:20150602195037p:plain
いい感じに表示される


ちょっと画面の下ギリギリすぎるので、Anchorの位置を少しだけ上にします〜Rect Transform内のAnchorsをクリックして下さい〜


f:id:hiyotama:20150602200120p:plain


MinとMaxという項目があり数値が書いてありますが、これらはAnchorの位置を表しています〜
X:0, Y:0が左下、X:1, Y:1が右上を表しています〜


f:id:hiyotama:20150602200026p:plain
X:0, Y:0が左下、X:1, Y:1が右上


それではMinのYとMaxのYを0.05に変更し、Anchorを少し上にあげましょう〜
また、Anchorを変更しただけではテキストの位置は変わりません〜今の状態でPosYを0に変更することで、テキスト位置がBottomより少し上に変更されます〜


f:id:hiyotama:20150602200615p:plain
Min YとMax Yを0.05に、PosYを0に変更


f:id:hiyotama:20150602200718p:plain
わかりにくいけど、ちょっと上に上がった


続いてこのFoodTextをLevelImageの下に表示して、プレイヤー操作画面でのみ表示されるようにします〜HierarchyビューのFoodTextをドラッグし、CanvasにドロップするとCanvas内の子オブジェクトの中で一番上の位置に変更されます〜


f:id:hiyotama:20150602201155p:plain
ドラッグ&ドロップ!


f:id:hiyotama:20150602201217p:plain
子オブジェクト内で一番上となる


UIはCanvas内で下にある程前面に表示されるため、これでFoodTextは隠れた状態となりました〜


f:id:hiyotama:20150602201329p:plain
隠れた


②GameManagerスクリプトの修正
それではGameManagerスクリプトを修正し、先ほど作成したUIを表示させたり値を変えたりしていきましょう〜以下ソースです〜

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
//********** 開始 **********//
using UnityEngine.UI; //UI用に宣言
//********** 終了 **********//

public class GameManager : MonoBehaviour {
//********** 開始 **********//
	public float levelStartDelay = 2f; //レベル表示画面で2秒待つ
//********** 終了 **********//
	public float turnDelay = .1f;
	public static GameManager instance = null;
	public BoardManager boardScript;

	public int playerFoodPoints = 100;
	[HideInInspector] public bool playersTurn = true;
//********** 開始 **********//
	private Text levelText; //レベルテキスト
	private GameObject levelImage; //レベルイメージ
	private int level = 1; //レベルは1にしておく
	private bool doingSetup; //levelImageの表示等で活用
//********** 終了 **********//
	private List<Enemy> enemies;
	private bool enemiesMoving;
	
	void Awake ()
	{
		if (instance == null) {
			instance = this;
		} else if (instance != this) {
			Destroy(gameObject);
		}
		
		DontDestroyOnLoad(gameObject);
		enemies = new List <Enemy>();
		boardScript = GetComponent<BoardManager>();
		InitGame();
	}
//********** 開始 **********//
	//UnityのAPIで、Sceneが呼ばれる度に実行されるメソッド
	private void OnLevelWasLoaded (int index)
	{
		level++; //レベルを1プラスする
		InitGame();
	}
//********** 終了 **********//
	
	void InitGame () {
//********** 開始 **********//
		//trueの間、プレイヤーは身動きを取れない
		doingSetup = true;
		//LevelImageオブジェクト・LevelTextオブジェクトの取得
		levelImage = GameObject.Find("LevelImage");
		levelText = GameObject.Find("LevelText").GetComponent<Text>();
		levelText.text = "Day " + level; //最新のレベルに更新
		levelImage.SetActive(true); //LebelImageをアクティブにし表示
		Invoke ("HideLevelImage", levelStartDelay); //2秒後にメソッド呼び出し
//********** 終了 **********//
		enemies.Clear();
		boardScript.SetupScene(level);
	}
	
//********** 開始 **********//
	private void HideLevelImage ()
	{
		levelImage.SetActive(false); //LevelImage非アクティブ化
		doingSetup = false; //プレイヤーが動けるようになる
	}
//********** 終了 **********//

	public void GameOver ()
	{
//********** 開始 **********//
		//ゲームオーバーメッセージを表示
		levelText.text = "After " + level + " days, you starved.";
		levelImage.SetActive(true);
//********** 終了 **********//
		enabled = false;
	}
	
	void Update ()
	{
//********** 開始 **********//
		//doingSetup=trueの時はEnemyを動かさない
		if (playersTurn || enemiesMoving || doingSetup) {
//********** 終了 **********//
			return;
		}
		
		StartCoroutine(MoveEnemies());
	}
	
	public void AddEnemyToList (Enemy script)
	{
		enemies.Add (script);
	}
	
	IEnumerator MoveEnemies ()
	{
		enemiesMoving = true;
		yield return new WaitForSeconds (turnDelay);
		if (enemies.Count == 0) {
			yield return new WaitForSeconds (turnDelay);
		}
		for (int i = 0; i < enemies.Count; i++) {
			enemies[i].MoveEnemy();
			yield return new WaitForSeconds(enemies[i].moveTime);
		}
		
		playersTurn = true;
		enemiesMoving = false;
	}
}

GameManager.cs


OnLevelWasLoadedメソッド
まずはOnLevelWasLoadedメソッドです〜このメソッドは、Unityに標準で備わっているメソッドで、Sceneが呼ばれる度に実行されるメソッドです〜引数のindexはSceneのindex番号で、Build SettingにてSceneをAdd Currentした時に与えられる番号です〜今回は使いません〜


f:id:hiyotama:20150602211512p:plain
Build Settingsのこの赤枠の数字が渡される


続いてレベルをプラス1して、InitGameメソッドを読んでいます〜


InitGameメソッド
InitGameメソッドではまず、doingSetupをtrueにし、Enemyが動けないようにしています〜(後述)


続いてLevelImageオブジェクトとLevelTextオブジェクトを取得し、LevelTextに関しては最新のレベルに更新しています〜
また、LevelImageをSetActiveによってアクティブ化し、画面に表示しています〜


最後にInvokeでHideLevelImageを呼び出しています〜Invokeは第2引数の分だけ遅れてメソッドを呼び出します〜デフォルトだと2秒遅れになっています〜


HideLevelImageメソッド
2秒経過後、まずはLevelImageを非アクティブ化し、LevelImageを非表示にします〜


続いてdoingSetupをfalseにし、Enemyが動けるようにしています〜


Updateメソッド
Updateメソッドでは、return(メソッド終了)を実行する条件にdoingSetupを追加しています〜こうすることにより、doingSetupがtrueの間はEnemyが動かなくなります〜


GameOverメソッド
最後にGameOverメソッドです〜LevelTextのテキスト部分にゲームオーバーメッセージを代入し、LevelImageをアクティブ化しゲームオーバーメッセージを表示しています〜


③Playerメソッド修正
最後にプレイヤーの体力を表すFoodTextを機能させるため、Playerスクリプトに修正を加えていきます〜以下ソースです〜

using UnityEngine;
using System.Collections;
//********** 開始 **********//
using UnityEngine.UI;
//********** 終了 **********//

public class Player : MovingObject {
	
	public int wallDamage = 1;
	public int pointsPerFood = 10;
	public int pointsPerSoda = 20;
	public float restartlevelDelay = 1f;
//********** 開始 **********//
	public Text foodText; //FoodText
//********** 終了 **********//
	
	private Animator animator;
	private int food;

	protected override void Start () {
		animator = GetComponent<Animator>();
		food = GameManager.instance.playerFoodPoints;
//********** 開始 **********//
		foodText.text = "Food: " + food;
//********** 終了 **********//
		base.Start();
	}

	private void OnDisable ()
	{
		GameManager.instance.playerFoodPoints = food;
	}

	void Update ()
	{
		if (!GameManager.instance.playersTurn)
			return;
		
		int horizontal = 0;
		int vertical = 0;
		
		horizontal = (int)Input.GetAxisRaw ("Horizontal");
		vertical = (int)Input.GetAxisRaw ("Vertical");

		if (horizontal != 0) {
			vertical = 0;
		}
		if (horizontal != 0 || vertical != 0) {
			AttemptMove<Wall>(horizontal, vertical);
		}
	}
	
	protected override void AttemptMove <T> (int xDir, int yDir)
	{
		food--;
//********** 開始 **********//
		foodText.text = "Food: " + food;
//********** 終了 **********//
		base.AttemptMove <T> (xDir, yDir);
		
		RaycastHit2D hit;
		
		CheckIfGameOver();
		GameManager.instance.playersTurn = false;
	}
	
	protected override void OnCantMove <T> (T component)
	{
		Wall hitWall = component as Wall;
		hitWall.DamageWall(wallDamage);
		animator.SetTrigger("PlayerChop");
	}
	
	private void OnTriggerEnter2D (Collider2D other)
	{
		if (other.tag == "Exit") {
			Invoke ("Restart", restartlevelDelay);
			enabled = false;
		} else if (other.tag == "Food") {
			food += pointsPerFood;
//********** 開始 **********//
			foodText.text = "+" + pointsPerFood + " Food: " + food;
//********** 終了 **********//
			other.gameObject.SetActive(false);
		} else if (other.tag == "Soda") {
			food += pointsPerSoda;
//********** 開始 **********//
			foodText.text = "+" + pointsPerSoda + " Food: " + food;
//********** 終了 **********//
			other.gameObject.SetActive(false);
		}
	}
	
	private void Restart ()
	{
		Application.LoadLevel(Application.loadedLevel);
	}
	public void LoseFood (int loss)
	{
		animator.SetTrigger("PlayerHit");
		food -= loss;
//********** 開始 **********//
		foodText.text = "-" + loss + " Food: " + food;
//********** 終了 **********//
		CheckIfGameOver();
	}
	
	private void CheckIfGameOver ()
	{
		if (food <= 0) {
			GameManager.instance.GameOver();
		}
	}
}

Player.cs


まずはpublic変数であるfoodTextに、FoodTextオブジェクトを指定してあげましょう〜


f:id:hiyotama:20150602214037p:plain
ドラッグ&ドロップ!


Startメソッド
Startメソッドでは、新しいレベルのステージが開始された時の体力を表示しています〜


AttemptMoveメソッド
AttempMoveメソッドは移動を試みた時に実行されるメソッドで、開始直後に体力が1減るので、そのすぐ下で体力の表示を更新しています〜


OnTriggerEnter2Dメソッド
OnTriggerEnter2Dメソッドではアイテムをとった時の処理を記述しています〜


Foodを取り体力を10回復したあと、即座に「+10 Food: 体力値」と表示させています〜


同じくSodaを取り体力を20回復したあと、即座に「+20 Food: 体力値」と表示させています〜


LoseFoodメソッド
最後はEnemyから攻撃を受けた時に実行されるLoseFoodです〜


体力値が変数loss分マイナスされたあと、即座に「-loss Food: 体力値」と表示させています〜


テスト
以上でUIの実装は終了です〜テストをしてみましょう〜


f:id:hiyotama:20150602214132p:plain
ゲームスタート


f:id:hiyotama:20150603101920p:plain
画面下に体力表示


f:id:hiyotama:20150603101955p:plain
動くと体力が減る


f:id:hiyotama:20150603102017p:plain
アイテムを取得すると回復する


f:id:hiyotama:20150603102408p:plain
攻撃を受けると体力が減る


f:id:hiyotama:20150603102509p:plain
次のステージへ進むとレベルが上がる


f:id:hiyotama:20150603102633p:plain
体力が0になるとゲームオーバー


ゲームを進める上でのひと通りの機能は揃いました〜

今回はここまでです〜
ありがとうございました〜


【Unity開発7】Unity公式チュートリアル2Dローグライクの導入【2Dローグライク1】

【Unity開発7】アニメーションを作りたい!【2Dローグライク2】

【Unity開発7】Floorやアイテムを作成したい!【2Dローグライク3】

【Unity開発7】床や敵キャラ、アイテム等を自動生成させたい!【2Dローグライク4】

【Unity開発7】BoardManagerを呼び出すGameManagerを作成する【2Dローグライク5】

【Unity開発7】キャラクターを動かすための抽象クラス作成【2Dローグライク6】

【Unity開発7】破壊可能な壁(Wall)を作成したい!【ローグライク7】

【Unity開発7】プレイヤーのアニメーターを設定したい!【ローグライク8】

【Unity開発7】プレイヤー用のスクリプトを設定したい!【ローグライク9】

【Unity開発7】敵キャラクターのスクリプトを作成したい!【2Dローグライク10】

【Unity開発7】敵キャラクターのアニメーターを設定したい!【2Dローグライク11】

【Unity開発7】uGUIでUIの表示とレベル機能を実装したい!【2Dローグライク12】

【Unity開発7】BGMと効果音を付ける【2Dローグライク13】

【Unity開発7】タッチパネルに対応させたい!【2Dローグライク14(Fin)】