本次主要针对以下几个问题进行优化。
1. 常用函数方法封装,通过反射机制进行调用。Unity的Start Update等,应该也是使用反射的机制。比如OnCreate、OnLoad、OnBind OnUnbind、OnDestory等生命周期函数。
有关反射调用方法参考以下
https://www.cnblogs.com/coderJiebao/p/CSharp09.html
这里通过反射调用的方法一定要是Public吗?Private没有调用成功啊?
OnBind是指在与UI或者3DView 对象进行绑定时进行调用的。但由于一个对象,可以进行多个不层次的绑定,所有可能会调用多次,只能通过输入参数加以区分。
OnLoad对象在加载后进行调用,可能是从持久化文件中加载,可能是从网络传输数据进行加载。
2. Clone
支持Property的Clone,复制后会调用上面的OnLoad方法来完成复制对象的初始化操作。
3. 属性与属性间的关联,比如属性1由属性2定义,属性2变化时,属性1也会进行更新
这个参考了UniRX的可交互属性
public class Enemy
{
public IReactiveProperty<long> CurrentHp { get; private set; }
public IReactiveProperty<bool> IsDead { get; private set; }
public Enemy(int initialHp)
{
// Declarative Property
CurrentHp = new ReactiveProperty<long>(initialHp);
IsDead = CurrentHp.Select(x => x <= 0).ToReactiveProperty();
}
}
也定义了Select方法,其中Count属性是根据List的个数变化为变化的。比如追加一个元素时,Count就会加1。
public void OnCreate(){
Count.Select<ZPropertyList<T >> (Items, (items) => items.Count);
}
4. ZProperty基类,支持了ToString,即直接输出Data的ToString。这样在Debug页面中显示数据内容也是使用的ToString,这样看起来就很直观。
5. 集成Bolt插件(本次优化的重点)
加入状态/事件Property支持,扩展Bolt的结点,以使可视化界面中可以对Property进行操作,并通过事件Property通知View层的变化,比如用户单击了Button,可以在蓝图中把Button和对应的事件Property进行连接。
自定义结点:
其中重要概念是
https://support.ludiq.io/knowledge-bases/4/articles/150-connections-relations
Relations are useful to understand what are the dependencies between each port of a unit. For example, in the above Add unit, you can see that if you want to get the result of A + B, you need to provide a value for A and B. Likewise, you can see that before invoking the Log unit, you should provide a value for its Message input port.
Bolt uses this information in the background for Predictive Debugging. For example, if you tried to get the value of A + B without providing a value for A, the node would show up as orange to indicate that it will fail in play mode:
Relations可以用于约束ControlOutput的依赖输入数值。
在结点中如何获取所在的GameObject对象,即Self结点的功能。这里可以查看Unit基类的定义就可以发现owner属性。以下就可以输出FlowMachine所在的对象名。
public void Action(Flow flow){
Debug.Log ("1233123 " + owner.ToString());
flow.Invoke (exit);
}
自定义Bolt结点对ZProperty的支持,主要包括以下结点:
1. PropertyValue 结点。为数值结点,不包含Control Port。主要做为参数使用。这里主要需要考虑对不同类型数值的支持,可以使用T模板来定义结点。是否需要定义多个呢?一个类型一个结点?至少是后备方案。
[Inspectable, Serialize, TypeFilter (new Type[] {
}, Enums = true, Classes = false, Interfaces = false, Structs = false, Primitives = false), UnitHeaderInspectable]
public Type enumType {
[CompilerGenerated]
get;
[CompilerGenerated]
set;
}
通过以上代码,可以看出一些线索。可以选择不同的类型。
public class SwitchOnInteger : SwitchUnit<int>
{
//
// Constructors
//
public SwitchOnInteger ();
}
看来需要使用不同的类型进行定义了,对于复合属性,也可以直接获取到对应的子属性来进行控制。详细的代码如下所示。
using System;
using Bolt;
using Ludiq;
using UnityEngine;
using UnityEngine.Assertions;
namespace ZLib
{
public class PropertyValueUnit<T> : Unit
{
public PropertyValueUnit ()
{
}
[PortLabel ("PropertyID"), DoNotSerialize]
public ValueInput ID {
get;
private set;
}
[PortLabel ("Property"), DoNotSerialize]
public ValueOutput Prop {
get;
private set;
}
protected override void Definition (){
ID = new Bolt.ValueInput (this, "PropertyID", typeof(string));
this.valueInputs.Add (ID);
Prop = new Bolt.ValueOutput (this, "result", typeof(T), Operation);
this.valueOutputs.Add (Prop);
Bolt.UnitRelation r = new UnitRelation (ID, Prop);
this.relations.Add (r);
//base.Definition ();
}
/// <summary>
/// Operation the specified recursion.
/// </summary>
/// <param name="recursion">Recursion.</param>
public object Operation (Recursion recursion)
{
Transform trans = (base.owner.GetComponent<MonoBehaviour> () as MonoBehaviour).transform;
IZProperty prop = ZViewBuildTools.GetPropertyBySub (trans, ID.GetValue<string>());
Assert.IsNotNull (prop, "not this property " + ID.GetValue<string>());
if (prop != null) {
return prop.Value;
}
return 100;
}
}
}
2. PropertyChanged,用于获取属性值变化的事件接收,其中同上也会支持不同的类型。
从基类Event进行派生。实现Listen方法。这里就是注册属性的变化。
这里需要注意一下 StartListening的调用时机,它从Awake还早,所以这其里面直接获取属性是获取不到的,因为还没有进行绑定
。这里使用了一个技巧,利用UniRX进行延时注册。事件发生时,调用Trigger进行Bolt的Event的触发。
public override void StartListening ()
{
base.StartListening ();
Observable.NextFrame ().Subscribe (_ => RegisterChanged ());
}
private void RegisterChanged(){
Transform trans = (base.owner.GetComponent<MonoBehaviour> () as MonoBehaviour).transform;
IZProperty prop = ZViewBuildTools.GetPropertyBySub (trans, ID.GetValue<string>());
Assert.IsNotNull (prop, "not this property " + ID.GetValue<string>());
if (prop != null) {
prop.OnValueChanged += a => {
//changedValue = (T)a;
base.Trigger();
};
}
}
蓝图如下:
通过Log可以看变化后的当前属性值了。
注意这里是把PropertyChanged和PropertyValue进行合并了
这里的PropertyID也可以从外面设置变化为通过String的方式进行设置。
3.PropertySetter,用于属性的设置,它就是需要ControlInput了。
后续问题;
目前对于属性列表ZpropertyList,不能给其Items父结点进行操作,包括AddComponent、动态创建等。
需要Property支持Parent的获取,问题是获取的属性还是对象。父属性会有一定的不定性,不好统一支持。
属性可见性的问题,需要进行一个调查,比如一些属性可能需要声明Private。