1. 程式人生 > >通過委託與事件監聽狀態改變來更新UI

通過委託與事件監聽狀態改變來更新UI

該博文涵蓋的知識點

1. C# 委託 2. 如何利用委託監聽事件以此更新顯示

所要完成目標

我們剛開始做好的部分UI介面如下圖所示:


觀察左邊的紅色方框,我們先做了例項的姓名,頭像,slider,和Label等,在運行遊戲時候需要更新相應的屬性。

在下面的指令碼中,我們分別更新:左邊方框中的nameLabel(黃曉明),energyLabel(50/100),toughenLabel(32/50)以及levelLabel(89);和右邊的兩個Label(12345678和12345678)。如何更新呢?我們藉助委託註冊事件來監聽屬性的改變,下面不再做解釋,指令碼中註釋說明了一切!!!!

指令碼、Unity相關設定


這裡只使用三個指令碼來學習如何使用委託來註冊事件,以此監聽屬性的變化。如上圖所示,PlayerBar指令碼繫結在player-bar上,PlayerInfo指令碼繫結在GameObject(空的遊戲物體,作為控制)上,TopBar指令碼繫結在top-bar上,分別在這些指令碼中獲取Unity Hierarchy中的相關屬性,然後進行更新。下面分別是三個指令碼:PlayerInfo,PlayerBar以及TopBar
using UnityEngine;
using System.Collections;

public enum InfoType{
	Name,
	HeadPortrait,
	Level,
	Power,
	Exp,
	Diamond,
	Coin,
	Energy,
	Toughen,
	All
}

public class PlayerInfo : MonoBehaviour {

	// 建立該類 為單例模式、
	public static PlayerInfo _instance;

	#region property
	private string _name;
	private string _headPortrait;
	private int _level = 1;
	private int _power = 1;
	private int _exp = 0;
	private int _diamond;
	private int _coin;
	private int _energy;
	private int _toughen;
	#endregion

	// 體力和歷練的計時器;一分鐘體力加1
	private float energyTimer = 0;
	private float toughenTimer = 0;

	// ----------------- 委託 -----------------------------------
	public delegate void OnPlayerInfoChangedEvent(InfoType type);
	// 利用委託定義事件,通過事件監聽屬性的更改
	public event OnPlayerInfoChangedEvent OnPlayerInfoChanged;


	#region get set method
	public string Name{
		get{ return _name; }
		set{ _name = value; }
	}

	public string HeadPortrait{
		get { return _headPortrait; }
		set { _headPortrait = value; }
	}

	public int Level{
		get { return _level; }
		set { _level = value; }
	}

	public int Power{
		get { return _power; }
		set { _power = value; }
	}

	public int Exp{
		get { return _exp; }
		set { _exp = value; }
	}

	public int Diamond{
		get { return _diamond; }
		set { _diamond = value; }
	}

	public int Coin{
		get { return _coin; }
		set { _coin = value; }
	}

	public int Energy{
		get { return _energy; }
		set { _energy = value; }
	}

	public int Toughen{
		get { return _toughen; }
		set { _toughen = value; }
	}
	#endregion

	void Start(){
		Init ();// 初始化
	}

	void Awake(){
		_instance = this;
	}

	void Update(){
		// ------------- 實現體力和歷練的自由增長 ---------------------------- 
		// 每一分鐘體力和歷練增長1,其中體力總數為100,歷練總是為50
		// 實現體力的自動增長
		if (this.Energy < 100) {
			energyTimer += Time.deltaTime;
			if (energyTimer > 60) {
				Energy += 1;
				energyTimer -= 60;
				OnPlayerInfoChanged (InfoType.Energy);
			}
		} else {
			energyTimer = 0;
		}

		// 實現歷練的自由增長
		if (this.Toughen < 50) {
			toughenTimer += Time.deltaTime;
			if (toughenTimer > 60) {
				Toughen += 1;
				toughenTimer -= 60;
				OnPlayerInfoChanged (InfoType.Toughen);
			}
		} else {
			toughenTimer = 0;
		}
		// ------------- ----------------------- ---------------------------- 
	}

	// 初始化屬性
	void Init(){
		this.Coin = 9870;
		this.Diamond = 1234;
		this.Energy = 78;
		this.Exp = 123;
		this.HeadPortrait = "頭像底板女性";
		this.Level = 12;
		this.Name = "千頌伊";
		this.Power = 1745;
		this.Toughen = 34;
		OnPlayerInfoChanged (InfoType.All);
	}
}
using UnityEngine;
using System.Collections;

/**2016-5-13 By BigoSprite
 * 設定PlayerBar指令碼的執行順序次於PlayerInfo指令碼
 * 步驟如下:
 * Edit>Project Settings>Script Execution Order
 * 注意:值小的,先執行
*/

public class PlayerBar : MonoBehaviour {
	// 該指令碼用於更新左上角這一區域的屬性
	private UISprite headSprite;
	private UILabel nameLabel;
	private UILabel energyLabel;
	private UISlider energySlider;
	private UILabel levelLabel;
	private UILabel toughenLabel;
	private UISlider toughenSlider;
	private UIButton energyPlusButton;
	private UIButton toughenPlusButton;

	void Awake(){
		headSprite = transform.Find ("head-sprite").GetComponent<UISprite> ();
		nameLabel = transform.Find ("name-label").GetComponent<UILabel> ();
		energyLabel = transform.Find("energy-progressBar/Label").GetComponent<UILabel> ();
		energySlider = transform.Find ("energy-progressBar").GetComponent<UISlider> ();
		levelLabel = transform.Find ("level-label").GetComponent<UILabel> ();
		toughenLabel = transform.Find ("toughen-progressBar/Label").GetComponent<UILabel> ();
		toughenSlider = transform.Find ("toughen-progressBar").GetComponent<UISlider> ();
		energyPlusButton = transform.Find ("energyPlusButton").GetComponent<UIButton> ();
		toughenPlusButton = transform.Find ("toughenPlusButton").GetComponent<UIButton> ();
		energyPlusButton = transform.Find ("energyPlusButton").GetComponent<UIButton> ();
		toughenPlusButton = transform.Find("toughenPlusButton").GetComponent<UIButton> ();
		// 使用單例模式前,要保證先初始化。因為我們設定PlayerInfo指令碼先執行,後執行PlayerBar指令碼
		// 而已經在PlayerInfo指令碼中的Awake方法中初始化了,因此可保證_instance肯定存在。
		// ------- 註冊PlayerInfo中的委託事件(+=), PlayerInfo是單例模式,公有的類,所以 ----------
		PlayerInfo._instance.OnPlayerInfoChanged += this.OnPlayerInfoChanged;
	}

	// 登出事件 -=
	void OnDestory(){
		PlayerInfo._instance.OnPlayerInfoChanged -= this.OnPlayerInfoChanged;
	}
	// 當我們的主角資訊發生改變的時候,會觸發這個方法
	void OnPlayerInfoChanged(InfoType type){
		// 首先判斷角色的哪部分資訊改變了
		// 注意這個指令碼(PlayerBar)繫結在PlayerBar身上,即左上角的區域
		if(type == InfoType.All ||type == InfoType.Name || type == InfoType.Level 
			|| type == InfoType.Energy || type == InfoType.Toughen){
			UpdateShow();// 更新資訊的顯示
		}
	}

	// 更新顯示
	// 如何更新資訊的顯示?怎麼確定具體哪一個資訊改變了???
	// 這裡管他具體哪個改變了,全部給他更新,哈哈!!!
	void UpdateShow(){
		PlayerInfo info = PlayerInfo._instance;// 初始化一個物件
		headSprite.spriteName = info.HeadPortrait;// 頭像
		levelLabel.text = info.Level.ToString();
		nameLabel.text = info.Name;
		// 注意: 這裡需要除以100f和50f,得到浮點型;如果除以100和50,slider不會顯示!
		energySlider.value = info.Energy / 100f;// energy Slider
		energyLabel.text = info.Energy.ToString () + "/100";
		toughenSlider.value = info.Toughen / 50f;// toughen Slider
		toughenLabel.text = info.Toughen.ToString()+"/50";
	}
}
using UnityEngine;
using System.Collections;

public class TopBar : MonoBehaviour {
	private UILabel coinLabel;
	private UILabel diamondLabel;
	private UIButton coinPlusButton;
	private UIButton diamondPlusButton;

	void Awake(){
		coinLabel = transform.Find ("coin-bg/Label").GetComponent<UILabel> ();
		diamondLabel = transform.Find ("diamond-bg/Label").GetComponent<UILabel> ();
		coinPlusButton = transform.Find ("coin-bg/coinPlusButton").GetComponent<UIButton> ();
		diamondPlusButton = transform.Find ("diamond-bg/diamondPlusButton").GetComponent<UIButton> ();
		// 1. 註冊事件,同樣是在設定指令碼執行次序的前提下
		// 加入未設定執行次序,PlayerInfo不先於TopBar執行,會報錯:說
		// Object reference not set to an instance of an object.
		// 即PlayerInfo指令碼中的_instance = this;次於TopBar指令碼執行,TopBar指令碼使用例項,但例項還未初始化呢?所以需要指定次序。
		PlayerInfo._instance.OnPlayerInfoChanged += this.OnPlayerInfoChanged;
	}

	// 登出事件
	void OnDestory(){
		PlayerInfo._instance.OnPlayerInfoChanged -= this.OnPlayerInfoChanged;
	}
		
	// ---------------------------------------------------------------------
	// 1. 註冊和登出事件
	// 2. 更新顯示
	// 注意:更改指令碼執行順序,PlayerInfo先於TopBar執行

	void OnPlayerInfoChanged(InfoType type){
		if(type == InfoType.All || type == InfoType.Coin || type == InfoType.Diamond){
			UpdateShow ();			
		}
	}

	void UpdateShow(){
		PlayerInfo info = PlayerInfo._instance;
		coinLabel.text = info.Coin.ToString ();
		diamondLabel.text = info.Diamond.ToString ();
	}
}
其中,設定指令碼執行次序如下圖所示:
執行Unity,效果如下圖所示:

注:本博文參考SIKI老師的《泰斗破壞神》,用於學習