ZProperty属性封装类——融合Bolt可视化插件

本次主要针对以下几个问题进行优化。

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。

猜你喜欢

转载自blog.csdn.net/FeiBin2013/article/details/81356993