読者です 読者をやめる 読者になる 読者になる

ヒキニートがゲームを作るブログ

Unityでゲームを作る過程を投稿します

ユニットに指示を出すー実装【UnityでRTSを作る 14】

今回は前回の「仕様の決定」に続いて、その実装を行います。
mackysoft.hatenablog.com

HPBarを改良

こちらの記事で紹介したHPBarを改良して、
ユニットが選択されているかどうかを可視化します。
mackysoft.hatenablog.com

今回からはHPBarはHP以外の情報も扱うようになるのでStatusGUIに名称変更します。

CKWBehaviour.csに変更を加えます。

protected HPBar hpBar = null;

から

protected StatusGUI status = null;

に名前を変更します。

レイアウトに変更を加える

HPBarに矢印を付け加えました。
f:id:MackySoft:20170514213942j:plain
この矢印の色を変化させて「無所属・味方・敵・選択中の味方」を判別します。

StatusGUI.cs

StatusGUIを制御するStatusGUI.csを用意します。
HPBar.csの名前を変更したものに、いろいろ手を加えました。

StatusGUI.cs

using System;
using UnityEngine;
using UnityEngine.UI;

[RequireComponent(typeof(UIFollowTarget))]
public class StatusGUI : MonoBehaviour {

	#region Variables

	private static StatusGUI _Prefab = null;
	private static Canvas _Canvas = null;

	[Header("References")]
	public Image hpBar;
	public Image arrow;
	
	[Header("Color")]
	public bool colorChange = true;
	public Color safetyColor = Color.blue;
	public Color warningColor = Color.yellow;
	public Color dangerColor = Color.red;

	private RectTransform hpBarTr;
	private UIFollowTarget uft;
	
	#endregion

	#region Properties

	public static StatusGUI Prefab {
		get { return _Prefab ? _Prefab : (_Prefab = Resources.Load<StatusGUI>("Prefabs/UI/StatusGUI")); }
	}
	
	public static Canvas Canvas {
		get { return _Canvas ? _Canvas : (_Canvas = FindObjectOfType<Canvas>()); }
		set { _Canvas = value; }
	}
	
	public CKWBehaviour Behaviour { get; private set; }

	#endregion

	public static StatusGUI Instantiate (CKWBehaviour behaviour) {
		var ins = Instantiate(Prefab,Canvas.transform);
		ins.Initialize(behaviour);
		return ins;
	}
	
	private void Awake () {
		hpBarTr = hpBar.GetComponent<RectTransform>();
		uft = GetComponent<UIFollowTarget>();
	}

	public void Initialize (CKWBehaviour behaviour) {
		if (Behaviour) Disable();
		Behaviour = behaviour;

		uft.target = Behaviour.Tr;
		Behaviour.Health.onValueChanged.AddListener(OnHealthValueChanged);
		UpdateArrowColor();

		gameObject.SetActive(true);
	}

	public void Disable () {
		if (!Behaviour)
			throw new NullReferenceException();

		Behaviour.Health.onValueChanged.RemoveListener(OnHealthValueChanged);
		Behaviour = null;

		gameObject.SetActive(false);
	}

	public void UpdateArrowColor () {
		if (!Behaviour)
			throw new NullReferenceException();
		switch (Behaviour.Team) {
			case Team.Independent: arrow.color = Color.white; break;
			case Team.Ally: arrow.color = Color.blue; break;
			case Team.Enemy: arrow.color = Color.red; break;
		}
	}
	
	private void OnHealthValueChanged (Health health) {
		float p = (float)health.Value / (float)health.Max;
		hpBarTr.localScale = new Vector3(p,1,1);
		if (colorChange) {
			if (p > 0.5f)
				hpBar.color = safetyColor;
			else if (p > 0.25f)
				hpBar.color = warningColor;
			else
				hpBar.color = dangerColor;
		}
	}
}
名前 説明
arrow 矢印UIの参照。
UpdateArrowColor 矢印の色を更新します。

インスペクターではこのように設定します。
f:id:MackySoft:20170514214933j:plain

ユニットを選択

ここからユニットへの指示を実装します。

準備

まず最初にプレイヤーに笛の当たり判定を用意します。
ピクミンを参考にしているので「笛」です)

この笛の当たり判定に当たった味方が選択状態になります。

コライダーのisTriggerにはチェックを入れます。
f:id:MackySoft:20170514220733j:plain

本当はパーティクルを使って範囲を表現したいのですが、
今はめんどくさいのでSphereメッシュで済ませます。
f:id:MackySoft:20170514221041j:plain

選択処理

NPCに選択・選択解除を実装しました。

NPC.cs(重要部分を抜粋)

private Player player;

public void Select (Player player) {
	this.player = player;
	player.selectingList.Add(this);
	status.arrow.color = Color.green;
}

public void Deselect () {
	player.selectingList.Remove(this);
	status.UpdateArrowColor();
	player = null;
}
名前 説明
player 自身を選択しているPlayerの参照。
Select 指定したPlayerで自身を選択する。
StatusGUIの矢印を緑色にする。
Deselect 自身の選択を外す。

笛の制御

笛の大きさの制御や当たったときの処理を実装します。

Player.cs(重要部分を抜粋)

public class Player : CubeKun {

	public float whistleRange = 1;

	public List<NPC> selectingList = new List<NPC>();

	private Transform whistle;

	public bool IsWhistling { get; private set; }

	protected override void Awake () {
		whistle = Tr.FindChild("Whistle");
		whistle.localScale = Vector3.zero;
	}

	protected override void Update () {
		if (Input.GetMouseButtonDown(0)) {
			if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition),out hit)) {
				IsWhistling = hit.collider.GetComponentInParent<Player>() == this;
			}
		}

		Mover.canMove = Input.GetMouseButton(0);
		Mover.canSearch = Mover.canMove;
		GameManager.Clock.paused = !Mover.canMove;
		IsWhistling = IsWhistling && Mover.canMove;

		if (IsWhistling) {
			whistle.localScale = Vector3.one * whistleRange;
		} else {
			whistle.localScale = Vector3.zero;
		}

		if (Mover.canMove && Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition),out hit)) {
			Pointer.position = hit.point;
			Mover.Move();
			RotateArm();
		}
	}

	protected virtual void OnTriggerEnter (Collider other) {
		if (IsWhistling) {
			var npc = other.GetComponentInParent<NPC>();
			if (npc && GetTeamRelative(npc) == Team.Ally && !selectingList.Contains(npc)) {
				selectingList.Add(npc);
				npc.Select(this);
			}
		}
		
	}
}
名前 説明
whistleRange 笛の当たり判定の大きさ。
selectingList 選択中のNPCのリスト。
whistle 笛の当たり判定の参照。
IsWhistling 笛を鳴らしているかどうか。

目標設定

良いアイデアがなかったので今回は
「選択したユニットはプレイヤーと同じ目標を狙い続ける」
という形で仮実装しました。

イメージ

地味ですが選択したユニットの矢印が緑色に変化します。