在这篇我们介绍一个新的概念 BindableProperty。
BindableProperty 是包含 数据 + 数据变更事件 的一个对象。
BindableProperty 基本使用
简单的用法如下:
var age = new BindableProperty<int>(10);
age.Register(newAge=>{
Debug.Log(newAge)
}).UnRegisterWhenGameObjectDestoryed(gameObject);
age++;
age--;
// 输出结果
// 11
// 10
非常简单,就是当调用 age++ 和 age-- 的时候,就会触发数据变更事件。
BindableProperty 除了提供 Register 这个 API 之外,还提供了 RegisterWithInitValue API,意思是 注册时 先把当前值返回过来。
具体用法如下:
var age = new BindableProperty<int>(5);
age.RegisterWithInitValue(newAge => {
Debug.Log(newAge);
});
// 输出结果
// 5
这个 API 就是,没有任何变化的情况下,age 先返回一个当前的值,比较方便用于显示初始界面。
BindableProperty 是一个独立的工具,可以脱离 QFramework 架构使用,也就是说不用非要用 QFramework 的 MVC 才能用 BindableProperty,而是可以再自己项目中随意使用。
使用 BindableProperty 优化 CounterApp 的代码
我们直接优化即可,优化后代码如下:
using UnityEngine;
using UnityEngine.UI;
namespace QFramework.Example
{
// 1. 定义一个 Model 对象
public class CounterAppModel : AbstractModel
{
public BindableProperty<int> Count { get; } = new BindableProperty<int>();
protected override void OnInit()
{
var storage = this.GetUtility<Storage>();
// 设置初始值(不触发事件)
Count.SetValueWithoutEvent(storage.LoadInt(nameof(Count)));
// 当数据变更时 存储数据
Count.Register(newCount =>
{
storage.SaveInt(nameof(Count),newCount);
});
}
}
public class AchievementSystem : AbstractSystem
{
protected override void OnInit()
{
this.GetModel<CounterAppModel>() // -+
.Count
.Register(newCount =>
{
if (newCount == 10)
{
Debug.Log("触发 点击达人 成就");
}
else if (newCount == 20)
{
Debug.Log("触发 点击专家 成就");
}
else if (newCount == -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());
}
}
// 引入 Command
public class IncreaseCountCommand : AbstractCommand
{
protected override void OnExecute()
{
var model = this.GetModel<CounterAppModel>();
model.Count.Value++; // -+
}
}
public class DecreaseCountCommand : AbstractCommand
{
protected override void OnExecute()
{
this.GetModel<CounterAppModel>().Count.Value--; // -+
}
}
// 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(/* 这里可以传参(如果有) */));
});
// 表现逻辑
mModel.Count.RegisterWithInitValue(newCount => // -+
{
UpdateView();
}).UnRegisterWhenGameObjectDestroyed(gameObject);
}
void UpdateView()
{
mCountText.text = mModel.Count.ToString();
}
// 3.
public IArchitecture GetArchitecture()
{
return CounterApp.Interface;
}
private void OnDestroy()
{
// 8. 将 Model 设置为空
mModel = null;
}
}
}
代码改动很多,重要的改动为:
- Model 中的 Count 和 mCount 改成了一个叫做 Count 的 BindableProperty
- 删掉了 CountChangeEvent 改用监听 BindableProperty
- Controller 在初始化中去掉一次 UpdateView 的主动调用
可以说代码量一下子少了很多。
我们看下运行结果:
运行没问题。
由于我们的 Count 数据,是单个数据 + 事件变更的形式,所以用 BindableProperty 非常合适,可以少写很多代码。
一般情况下,像主角的金币、分数等数据非常适合用 BindableProperty 的方式实现。
好了 BindableProperty 我们就介绍到这里。
更多内容
- 转载请注明地址: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/