在这一篇,我们来引入最后一个基本概念 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 次 + 号,结果如下:
这个功能很快就完成了。
但是这个时候策划说,希望再增加一个当点击 - 号到 -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;
}
}
}
代码越来越多,但是不难。
运行游戏,笔者点击的结果如下:
结果没问题。
好了,笔者写的成就系统非常简陋,实际上额度成就系统可以写得非常完善,比如可以再成就系统里进行存储加载等操作,而此文的成就系统仅仅是展示目的。
到此,我们就接触到了 QFramework 架构所提供的核心概念。
我们回顾一下第一篇的两张图,如下:
到此,大家应该能看懂这两张图了。
QFramework 总共分四个层级,即
除了四个层级,还接触了为 Controller 的交互逻辑减负的 Command 和 为表现逻辑减负的 Event。
还有一个非常重要的 CQRS 原则的简易版本,Command->Model->State Changed Event。
到目前为止 QFramework 的基本用法我们过了一遍了。
从下一篇开始,我们开始介绍 QFramework 架构提供的剩余功能,这些功能是可选的。
这篇就到这里。