这是一个把项目的系统分割成多个管理类,所有操作都有自己的管理类进行执行,各个管理之间使用消息体进行通讯,优点:各个管理类执行自己相关的操作,每个管理类的事件系统也有自己管理,耦合降低,后期修改起来如果有部分逻辑耦合能快速找到对应位置,然后利用屏蔽、方法重载、重新规划新的事件等方法快速搞定传统的直接使用管理类进行管理,事件中心处理所有事件-在后期修改的时候耦合度会变得很高,改起来费劲。 (QF内部有使用案例,但是没有相应的讲解,初学者可能看不懂)
首先模拟一个使用环境,我们是科学家,研究各种生物。
策划需求:整个游戏,要有各种生物,没有模型先用方块+文本代替;把他们放在一起,他们可以攻击、逃跑、捕食,研究他们的精神、身体等关系。
分析:生物系统:生物管理类、生物类、生物行为事件
我的李姐是用来初始化和做一些全局操作,这个要挂载到场景当中
public class ManagerOfManagers : QMonoBehaviour
{
public override IManager Manager {
get {return UIManager.Instance; }
}
// Start is called before the first frame update
void Start()
{
//初始化
ResKit.Init();
}
// Update is called once per frame
void Update()
{
if (Input.GetMouseButtonDown(0))
{
//全局性的操作
}
}
}
public enum BiologyType {
人,
猴,
老虎
}
/// <summary>
/// 生物
/// </summary>
public class Biology : QMonoBehaviour
{
public BiologyType BiologyType;
public int Life { set; get; }//生命力
public int Spirit { set; get; }//精神
public int Body { set; get; }//身体
public override IManager Manager
{
get { return BiologyManager.Instance; }
}
public void Move() {
//TODO
}
public void Skill(int Id) {
//TODO
}
public void Eat(int Id) {
//TODO
}
}
1、添加生物系统的管理ID
搜一下QMgrID脚本,发现这些都是系统的事件变量,每3000标记一个系统。
public abstract class QMgrID
{
public const int Framework = 0;
public const int UI = Framework + QMsgSpan.Count; // 3000
public const int Audio = UI + QMsgSpan.Count; // 6000
public const int Network = Audio + QMsgSpan.Count;
public const int UIFilter = Network + QMsgSpan.Count;
public const int Game = UIFilter + QMsgSpan.Count;
public const int PCConnectMobile = Game + QMsgSpan.Count;
public const int FrameworkEnded = PCConnectMobile + QMsgSpan.Count;
public const int FrameworkMsgModuleCount = 7;
}
我们的生物系统当然也要占据一席,不过不是在这;搜一下MgrID脚本,将生物系统添加进去,算一下标记递增3000。
public class MgrID
{
public const int Biology = (QMgrID.FrameworkMsgModuleCount + 1) * QMsgSpan.Count;
public const int ???= (QMgrID.FrameworkMsgModuleCount + 2) * QMsgSpan.Count;
}
2、生物系统:#region 单例部分照猫画虎即可,就是一个以系统划分的单例写法,用MgrID驱动和划分。下面就是管理类需要的基本方法,比如创建生物方法。
public class BiologyManager : QMgrBehaviour, ISingleton
{
#region 单例
public static BiologyManager Instance
{
get { return MonoSingletonProperty<BiologyManager>.Instance; }
}
void ISingleton.OnSingletonInit()
{
}
public override int ManagerId
{
get { return MgrID.Biology; }
}
[RuntimeInitializeOnLoadMethod]
public static void InitManager() {
QMsgCenter.RegisterManagerFactory(MgrID.Biology,() => Instance);
}
#endregion
protected override void ProcessMsg(int eventId, QMsg msg)
{
}
private Dictionary<int, Biology> _DicBiology = new Dictionary<int, Biology>();
public void CreateBiology(int Id) {
var biologyObj = new GameObject("生物" + Id);
var biology = biologyObj.AddComponent<Biology>();
_DicBiology.Add(Id, biology);
}
}
之后该方法就可以直接调用了。
BiologyManager.Instance.CreateBiology(1001);
创建生物事件
public static class BiologyEvent
{
public enum Nomal {
Start = MgrID.Biology,
End
}
public enum XXX {
Start = Nomal.End,
End
}
}
现在基本架子搭好,接下来完善逻辑,比如策划设计了一个吃这个行为,可以生吃、烤熟吃(烤?),吃东西的同时要发出声音,我们第一步在生物类完善吃这个方法;
public void Eat(string str) {
Debug.Log(str);
//TODO 其他的逻辑
}
第二步就腰在生物事件去注册一个消息体;
public static class BiologyEvent
{
public enum Nomal {
Start,
走,
End
}
public enum Eat {
Start = Nomal.End,
生吃,
熟吃,
End
}
}
public class BiologyRawEat : QMsg{
public int Id { set; get; }
public string Speak { set; get; }
public BiologyRawEat() : base((int)BiologyEvent.Eat.生吃){
}
}
第三步在BiologyManager脚本中找到ProcessMsg尝试完善她。
protected override void ProcessMsg(int eventId, QMsg msg)
{
if (eventId == (int)BiologyEvent.Eat.生吃)
{
var eat = msg as BiologyRawEat;
var biology = _DicBiology[eat.Id];
biology.Eat(eat.Speak);
}
}
先创建几个生物,然后点击左键发送信息,开吃!
public class ManagerOfManagers : QMonoBehaviour
{
public override IManager Manager {
get {return UIManager.Instance; }
}
// Start is called before the first frame update
void Start()
{
ResKit.Init();
BiologyManager.Instance.CreateBiology(1001);
BiologyManager.Instance.CreateBiology(1002);
BiologyManager.Instance.CreateBiology(1003);
}
// Update is called once per frame
void Update()
{
if (Input.GetMouseButtonDown(0))
{
SendMsg(new BiologyRawEat() { Id = 1001,Speak="准备开动"});
}
}
}
1、CreateBiology方法,这里只有一个Id赋值,可以改成自己想要的方法或者写重载,这里是示例所以什么生物的类型都没有用上哈。
2、消息体构造方法,可以在发送消息的时候方便一点,当然可以有多个构造方法。(这个BiologyRawEat 在某些模块考虑性能也可以是结构体)
public class BiologyRawEat : QMsg{
public int Id { set; get; }
public string Speak { set; get; }
public BiologyRawEat(int Id,string Speak) {
this.Id = Id;
this.Speak = Speak;
}
public BiologyRawEat() : base((int)BiologyEvent.Eat.生吃){
}
}
3、剔除(屏蔽)事件:多系统之间慢慢的会有耦合,可以用这个eventId进行剔除
4、多个行为就如法炮制,不要嫌麻烦
其实这个所谓ManagerOfManagers之前做游戏的时候用过类似的管理方式+ECS(处理大量的加载销毁、特效之类的),性能还行,核心是”将不同的功能模块单独管理“。使用的话主要需要注意新手来一定要教学一下,像我去的时候没人说自己看很费时间,更别说如果来个不懂的可能就开始乱写。
一般来说项目中期就是填写各个逻辑,修改逻辑,修改bug,也就是说该架构让开发者只关注于逻辑功能开发,并且规则大家都遵守的情况下维护起来还是很轻松的。例如游戏中的敌人、关卡、引导根据项目都可以当作系统进行规划,把初步架子搭起来,后面人照猫画虎就能把功能完善个七七八八。