QFramework v1.0 使用指南 架构篇:06. 引入 System-职场生存日志漫画
在这一篇,我们来引入最后一个基本概念 System。
首先我们来看下代码,如下:
using UnityEngine;using UnityEngine.UI;namespace QFramework.Example{ // 1. 定义一个 Model 对象 public class CounterAppModel : AbstractModel { private int mCount; public int Count { get => mCount; set { if (mCount != value) { mCount = value; PlayerPrefs.SetInt(nameof(Count),mCount); } } } protected override void OnInit() { var storage = this.GetUtility<Storage>(); Count = storage.LoadInt(nameof(Count)); // 可以通过 CounterApp.Interface 监听数据变更事件 CounterApp.Interface.RegisterEvent<CountChangeEvent>(e => { this.GetUtility<Storage>().SaveInt(nameof(Count), Count); }); } } // 定义 utility 层 public class Storage : IUtility { public void SaveInt(string key, int value) { PlayerPrefs.SetInt(key,value); } public int LoadInt(string key, int defaultValue = 0) { return PlayerPrefs.GetInt(key, defaultValue); } } // 2.定义一个架构(提供 MVC、分层、模块管理等) public class CounterApp : Architecture<CounterApp> { protected override void Init() { // 注册 Model this.RegisterModel(new CounterAppModel()); // 注册存储工具的对象 this.RegisterUtility(new Storage()); } } // 定义数据变更事件 public struct CountChangeEvent // ++ { } // 引入 Command public class IncreaseCountCommand : AbstractCommand { protected override void OnExecute() { this.GetModel<CounterAppModel>().Count++; this.SendEvent<CountChangeEvent>(); // ++ } } public class DecreaseCountCommand : AbstractCommand { protected override void OnExecute() { this.GetModel<CounterAppModel>().Count--; this.SendEvent<CountChangeEvent>(); // ++ } } // Controller public class CounterAppController : MonoBehaviour , IController /* 3.实现 IController 接口 */ { // View private Button mBtnAdd; private Button mBtnSub; private Text mCountText; // 4. Model private CounterAppModel mModel; void Start() { // 5. 获取模型 mModel = this.GetModel<CounterAppModel>(); // View 组件获取 mBtnAdd = transform.Find("BtnAdd").GetComponent<Button>(); mBtnSub = transform.Find("BtnSub").GetComponent<Button>(); mCountText = transform.Find("CountText").GetComponent<Text>(); // 监听输入 mBtnAdd.onClick.AddListener(() => { // 交互逻辑 this.SendCommand<IncreaseCountCommand>(); }); mBtnSub.onClick.AddListener(() => { // 交互逻辑 this.SendCommand(new DecreaseCountCommand(/* 这里可以传参(如果有) */)); }); UpdateView(); // 表现逻辑 this.RegisterEvent<CountChangeEvent>(e => { UpdateView(); }).UnRegisterWhenGameObjectDestroyed(gameObject); } void UpdateView() { mCountText.text = mModel.Count.ToString(); } // 3. public IArchitecture GetArchitecture() { return CounterApp.Interface; } private void OnDestroy() { // 8. 将 Model 设置为空 mModel = null; } }}
这里我们假设一个功能,即策划提出了一个成就达成的功能,即 Count 到 10 的时候,触发一个点击达人成就,点击二十次 则触发一个 点击专家成就。
逻辑听起来很简单,我们直接在 IncreaseCountCommand 里编写即可,如下:
public class IncreaseCountCommand : AbstractCommand { protected override void OnExecute() { var model = this.GetModel<CounterAppModel>(); model.Count++; this.SendEvent<CountChangeEvent>(); // ++ if (model.Count == 10) { Debug.Log("触发 点击达人 成就"); } else if (model.Count == 20) { Debug.Log("触发 点击专家 成就"); } } }
代码很简单,我们运行测试一下。
运行之后,笔者点击了 20 次 + 号,结果如下:
![](https://file.jqhtml5.com/file/view/20221015/y1guqr3sekq.jpeg)
这个功能很快就完成了。
但是这个时候策划说,希望再增加一个当点击 - 号到 -10 时,触发一个 点击菜鸟成就,然后策划还说,点击达人 和 点击专家 成就太容易达成了,需要分别改成 1000 次 和 2000 次。
而这次策划提出的需求,需要我们修改两处的代码,即 IncreaseCountCommand 里需要修改数值为 1000 和 2000,然后再 DecreaseCountCommand 增加一个判断逻辑。
一次提出的需求,结果造成了多处修改,这说明代码有问题。
首先像这种规则类的逻辑,比如分数统计或者成就统计等代码,不适合分散写在 Command 里,而适合统一写在一个对象里,而这种对象,在 QFramework 里有提供,就是 System 对象。
使用代码如下:
using UnityEngine;using UnityEngine.UI;namespace QFramework.Example{ // 1. 定义一个 Model 对象 public class CounterAppModel : AbstractModel { private int mCount; public int Count { get => mCount; set { if (mCount != value) { mCount = value; PlayerPrefs.SetInt(nameof(Count),mCount); } } } protected override void OnInit() { var storage = this.GetUtility<Storage>(); Count = storage.LoadInt(nameof(Count)); // 可以通过 CounterApp.Interface 监听数据变更事件 CounterApp.Interface.RegisterEvent<CountChangeEvent>(e => { this.GetUtility<Storage>().SaveInt(nameof(Count), Count); }); } } public class AchievementSystem : AbstractSystem // + { protected override void OnInit() { var model = this.GetModel<CounterAppModel>(); this.RegisterEvent<CountChangeEvent>(e => { if (model.Count == 10) { Debug.Log("触发 点击达人 成就"); } else if (model.Count == 20) { Debug.Log("触发 点击专家 成就"); } else if (model.Count == -10) { Debug.Log("触发 点击菜鸟 成就"); } }); } } // 定义 utility 层 public class Storage : IUtility { public void SaveInt(string key, int value) { PlayerPrefs.SetInt(key,value); } public int LoadInt(string key, int defaultValue = 0) { return PlayerPrefs.GetInt(key, defaultValue); } } // 2.定义一个架构(提供 MVC、分层、模块管理等) public class CounterApp : Architecture<CounterApp> { protected override void Init() { // 注册 System this.RegisterSystem(new AchievementSystem()); // + // 注册 Model this.RegisterModel(new CounterAppModel()); // 注册存储工具的对象 this.RegisterUtility(new Storage()); } } // 定义数据变更事件 public struct CountChangeEvent // ++ { } // 引入 Command public class IncreaseCountCommand : AbstractCommand { protected override void OnExecute() { var model = this.GetModel<CounterAppModel>(); model.Count++; this.SendEvent<CountChangeEvent>(); // ++ } } public class DecreaseCountCommand : AbstractCommand { protected override void OnExecute() { this.GetModel<CounterAppModel>().Count--; this.SendEvent<CountChangeEvent>(); // ++ } } // Controller public class CounterAppController : MonoBehaviour , IController /* 3.实现 IController 接口 */ { // View private Button mBtnAdd; private Button mBtnSub; private Text mCountText; // 4. Model private CounterAppModel mModel; void Start() { // 5. 获取模型 mModel = this.GetModel<CounterAppModel>(); // View 组件获取 mBtnAdd = transform.Find("BtnAdd").GetComponent<Button>(); mBtnSub = transform.Find("BtnSub").GetComponent<Button>(); mCountText = transform.Find("CountText").GetComponent<Text>(); // 监听输入 mBtnAdd.onClick.AddListener(() => { // 交互逻辑 this.SendCommand<IncreaseCountCommand>(); }); mBtnSub.onClick.AddListener(() => { // 交互逻辑 this.SendCommand(new DecreaseCountCommand(/* 这里可以传参(如果有) */)); }); UpdateView(); // 表现逻辑 this.RegisterEvent<CountChangeEvent>(e => { UpdateView(); }).UnRegisterWhenGameObjectDestroyed(gameObject); } void UpdateView() { mCountText.text = mModel.Count.ToString(); } // 3. public IArchitecture GetArchitecture() { return CounterApp.Interface; } private void OnDestroy() { // 8. 将 Model 设置为空 mModel = null; } }}
代码越来越多,但是不难。
运行游戏,笔者点击的结果如下:
![](https://file.jqhtml5.com/file/view/20221015/buxs1stw1ok.jpeg)
结果没问题。
好了,笔者写的成就系统非常简陋,实际上额度成就系统可以写得非常完善,比如可以再成就系统里进行存储加载等操作,而此文的成就系统仅仅是展示目的。
到此,我们就接触到了 QFramework 架构所提供的核心概念。
我们回顾一下第一篇的两张图,如下:
![](https://file.jqhtml5.com/file/view/20221015/jdagngzqwch.jpeg)
![](https://file.jqhtml5.com/file/view/20221015/hvakixty3zy.jpeg)
到此,大家应该能看懂这两张图了。
QFramework 总共分四个层级,即
表现层:IController
系统层:ISystem
数据层:IModel
工具层:IUtility
除了四个层级,还接触了为 Controller 的交互逻辑减负的 Command 和 为表现逻辑减负的 Event。
还有一个非常重要的 CQRS 原则的简易版本,Command->Model->State Changed Event。
到目前为止 QFramework 的基本用法我们过了一遍了。
从下一篇开始,我们开始介绍 QFramework 架构提供的剩余功能,这些功能是可选的。
这篇就到这里。
更多内容转载请注明地址:liangxiegame.com (首发) 微信公众号:凉鞋的笔记
QFramework 主页:qframework.cn
QFramework 交流群: 623597263
QFramework Github 地址: https://github.com/liangxiegame/qframework
QFramework Gitee 地址:https://gitee.com/liangxiegame/QFramework
GamePix 独立游戏学院 & Unity 进阶小班地址:https://www.gamepixedu.com/