今天我想重構的,是下圖中那個更新麵包獲取數的UI程式
GetFoodAndFleeGameEndCondition.cs
private void Awake()
{
...
m_StageTooltipGUI.SetFoodCount(m_CurrentPlayerHaveFoods, m_NumberOfNeedFoods);
...
}
private void GetFoodEvent(List<Unit> stayUnit, BreadUnitObstacle bread)
{
foreach(Unit unit in stayUnit)
{
if(unit.PlayerNumber == (int)UnitType.Player)
{
m_StageTooltipGUI.SetFoodCount(m_CurrentPlayerHaveFoods, m_NumberOfNeedFoods);
}
...
}
}
雖然這看起來還好,但我希望他今天依賴的是像是CellGrid
這種主要作為全系統溝通介面的程式,而不是直接使用StageTooltipGUI
去做呼叫和更改,才不會之後的引用越來越亂。除此之外,要是今天這種想法改成訂閱制的話,要是今天的條件判斷不需要麵包數量的話,系統就不用多跑一個根本不存在的事件。
FoodCountEventArgs.cs
using System;
namespace LinXuan.Common.GameEvent
{
public class FoodCountEventArgs : EventArgs //此FoodCountEventArgs是用來作為傳遞食物數量的數值的暫時存放點
{
public int FoodCollectedCount{ get; private set; }
public int FoodOfNeedCount { get; private set; }
public FoodCountEventArgs(int foodCollectedCount, int foodOfNeedCount)
{
FoodCollectedCount = foodCollectedCount;
FoodOfNeedCount = foodOfNeedCount;
}
}
}
IGameEventObserver.cs
namespace LinXuan.Common.GameEvent
{
public interface IGameEventObserver
{
void SetSubject(IGameEventSubject subject);
void ObserverUpdate();
}
}
FoodCollectedObserverGUI.cs
using LinXuan.Common.GameEvent;
using TbsFramework.Grid;
namespace LinXuan.TBSF.GameEvent
{
//用來更新m_CellGrid的UI更新
public class FoodCollectedObserverGUI : IGameEventObserver
{
private BreadUnitCollectedSubject m_BreadUnitCollectedSubject = null;
private CellGrid m_CellGrid = null;
public FoodCollectedObserverGUI(CellGrid cellGrid)
{
m_CellGrid = cellGrid;
}
public void ObserverUpdate()
{
m_CellGrid.GUIUpdate(m_BreadUnitCollectedSubject.FoodCountEventArgs);
}
public void SetSubject(IGameEventSubject subject)
{
m_BreadUnitCollectedSubject = subject as BreadUnitCollectedSubject;
}
}
}
IGameEventSubject.cs
using System.Collections.Generic;
namespace LinXuan.Common.GameEvent
{
public abstract class IGameEventSubject
{
private List<IGameEventObserver> m_Observers = new List<IGameEventObserver>();
private System.Object m_Parameter = null;
public void Attach(IGameEventObserver observer)
{
m_Observers.Add(observer);
}
public void Detach(IGameEventObserver observer)
{
m_Observers.Remove(observer);
}
public void Notify()
{
foreach (IGameEventObserver observer in m_Observers)
observer.ObserverUpdate();
}
public virtual void SetParameter(object parameter)
{
m_Parameter = parameter;
}
}
}
BreadUnitCollectedSubject.cs
namespace LinXuan.Common.GameEvent
{
//用來訂閱與獲取麵包數量的資料
public class BreadUnitCollectedSubject : IGameEventSubject
{
public FoodCountEventArgs FoodCountEventArgs { get; private set; }
public BreadUnitCollectedSubject()
{
}
public override void SetParameter(System.Object foodCountEventArgs)
{
base.SetParameter(foodCountEventArgs);
FoodCountEventArgs = foodCountEventArgs as FoodCountEventArgs;
Notify();
}
}
}
GameEventSystem.cs
using LinXuan.TBSF;
using LinXuan.TBSF.Enums;
using System.Collections.Generic;
using TbsFramework.Grid;
using UnityEngine;
namespace LinXuan.Common.GameEvent
{
//創建事件的地方,雖然有其他事件不過這次只實作出麵包數量更新的部分
public class GameEventSystem
{
private Dictionary<GameEventType, IGameEventSubject> m_GameEvents = new Dictionary<GameEventType, IGameEventSubject>();
public GameEventSystem(CellGrid cellGrid)
{
Initialize();
}
private void Initialize()
{
}
public void Release()
{
m_GameEvents.Clear();
}
public void RegisterObserver(GameEventType gameEventType, IGameEventObserver observer)
{
IGameEventSubject subject = GetGameEventSubject(gameEventType);
if(subject != null)
{
subject.Attach(observer);
observer.SetSubject(subject);
}
}
private IGameEventSubject GetGameEventSubject(GameEventType gameEventType)
{
if (m_GameEvents.ContainsKey(gameEventType))
return m_GameEvents[gameEventType];
IGameEventSubject subject = null;
switch (gameEventType)
{
case GameEventType.EnemyUnitKilled:
subject = new EnemyKilledSubject();
break;
case GameEventType.PlayerUnitKilleds:
subject = new PlayerKilledSubject();
break;
case GameEventType.GetFoodUnit:
subject = new FoodUnitCollectedSubject();
break;
case GameEventType.GetBreadUnitObstacle:
subject = new BreadUnitCollectedSubject();
break;
default:
Debug.LogError("Can't create["+gameEventType+"] subject class.");
return null;
}
m_GameEvents.Add(gameEventType, subject);
return subject;
}
public void NotifySubject(GameEventType gameEventType, System.Object parameter)
{
if (m_GameEvents.ContainsKey(gameEventType) == false)
return;
m_GameEvents[gameEventType].SetParameter(parameter);
}
}
}
CellGrid.cs
private GameEventSystem m_GameEventSystem;
public event EventHandler<FoodCountEventArgs> FoodCollectedUpdate;
...
private void Awake()
{
UIInputObserver.CellGrid = this;
m_GameEventSystem = new GameEventSystem(this);
}
public void RegisterGameEvent(GameEventType gameEventType, IGameEventObserver observer)
{
m_GameEventSystem.RegisterObserver(gameEventType, observer);
}
public void NotifyGameEvent(GameEventType gameEventType, System.Object parameter)
{
m_GameEventSystem.NotifySubject(gameEventType, parameter);
}
public void FoodCollectedGUIUpdate(FoodCountEventArgs foodCountEventArgs)
{
FoodCollectedUpdate.Invoke(this, foodCountEventArgs);
}
CALGUIController.cs
private void Awake()
{
CellGrid.FoodCollectedUpdate += FoodCollectedUpdate;
}
...
private void FoodCollectedUpdate(object sender, FoodCountEventArgs e)
{
m_StageTooltipGUI.SetFoodCount(e.FoodCollectedCount, e.FoodOfNeedCount);
}
GetFoodAndFleeGameEndCondition.cs
private void Awake()
{
UIInputObserver.CellGrid.RegisterGameEvent(LinXuan.TBSF.Enums.GameEventType.GetBreadUnitObstacle, new FoodCollectedObserverGUI(UIInputObserver.CellGrid));
}
private void GetFoodEvent(List<Unit> stayUnit, BreadUnitObstacle bread)
{
foreach(Unit unit in stayUnit)
{
if(unit.PlayerNumber == (int)UnitType.Player)
{
...
UIInputObserver.CurrentPlayerHaveFoods = m_CurrentPlayerHaveFoods;
UIInputObserver.CellGrid.NotifyGameEvent(LinXuan.TBSF.Enums.GameEventType.GetBreadUnitObstacle, new FoodCountEventArgs(m_CurrentPlayerHaveFoods, m_NumberOfNeedFoods));
...
}
...
}
}
經過這樣的更改後,GetFoodAndFleeGameEndCondition
的更新獲取麵包數量的方式就由StageTooltipGUI
改成CellGrid
,於此同時要是今天沒有需要去做麵包數量的判斷,那系統就擁有不會去訂閱此事件的能力了。
問:為什麼CellGrid
是用UIInputObserver
獲取?
答:作者的CellGrid
本身不是Singleton模式,所以目前才暫時用UIInputObserver
的方式獲取,下面是裡面的程式碼
UIInputObserver.cs
using System;
using System.Collections.Generic;
using TbsFramework.Grid;
using TbsFramework.Units;
using UnityEngine;
namespace Assets.Scripts.GUI.GUITool
{
public static class UIInputObserver
{
public static bool IsPlayerCanInput { get; set; }
public static bool IsUnitTurnChange { get; set; }
public static bool HaveCharacterStayOnFleeCell { get; set; }
public static List<Unit> StayFleeCellCharacter { get; set; }
public static List<Unit> AlreadyFleeCharacter { get; set; }
public static int FleeCharacterCount { get; set; }
public static Action FleeAction { get; set; }
public static bool HaveBuffAdd { get; set; }
public static Action<Transform, bool> FleeInfoAction { get; set; }
public static int NumberOfNeedFood { get; set; }
public static int CurrentPlayerHaveFoods { get; set; }
public static Camera MainCamera { get; set; }
public static CellGrid CellGrid { get; set; }
}
}
老實說GetFoodAndFleeGameEndCondition
其實寫的也挺亂的,鐵人弄完後真的應該要好好整理一下Orz
設計模式與遊戲開發的完美結合(暢銷回饋版)
Turn Based Strategy Framework
流離之歌