ZProperty属性封装类简介

有关属性使用的封装,包括功能,修饰、持久化、表现层绑定等。类图如下:

 

附加Attribute修饰

属性在使用时会带一些附加信息,描述信息也有一定的层次的,包括:名称、简介、详细描述、图片介绍、链接等多种形式。即属性又由其子属性组成。属性的附加信息,可以通过Attribute类【标签】来加以定义。

但属性的这些信息,每个属性不能不同的类实例都自己保存一份,那样开销很大的。所有本例中使用外部保存这些Attribute结点信息。

以下面Attribute为例,支持两种配置名称与描述等修饰方法,第一个是通过Attribute属性,第二个是通过持久化文件 (即JSON),当然后者也需要Attribute属性进行配置,后面的属性Attribute会一一列举出来。

其中描述的Attribute定义如下:

	/// <summary>
	/// Property description attribute, the item is the localization item's ID.
	/// </summary>
	[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = true)]
	public class PropertyDescriptionAttribute : Attribute {
		public string description;
		public PropertyDescriptionAttribute(string description)
		{
			this.description = description;
		}
	}

通过以下代码可以获取到其属性的值

		static public string GetName(){
			var type = typeof(PropertyNameAttribute);
			 
			var attribute = type.GetCustomAttributes(typeof(PropertyNameAttribute), true).FirstOrDefault();
			 
			if (attribute == null)
			{
			 	return null;
			}
			 
			return ((PropertyNameAttribute)attribute).name;
		}

 

用法如下:

		[PropertyDescription("等级", "玩家的等级信息", true)]
		public ZRuntimableProperty<int> rank = new ZRuntimableProperty<int>();

 

有关这些文言Localization的支持问题,以上参数设置对应文言字符串的ID,然后通过其获取到各Localization的文言。是要依赖Localization的模块的。参考以下插件。

 

https://blog.csdn.net/u011597114/article/details/54291247

其实大部分Localization插件都是使用字符串进行标识文言Item的。

可视化绑定

这部分提供了如何与UI能进行自动化的绑定,动态更新等机制,同时对一些常用框架的支持,比如上接UGUI、下连UniRX、ZECS等。同时也支持创建动态UIItem,比如对一些列表的支持等。当然这里的可视化绑定也适用于3D表现。

需求实例(以下面率土为例,列举常用UI Item)

可以分解出如下类型的属性,以及对应的UI表现。

等级/稀有度:int/byte类型,使用小星星作为表现

血条/兰条:float类型,进度条表现,也会有数值显示,参考包括最大值、颜色。其实就是做为不同的Prefab但是UI类是一个。

数值属性:比如攻击力防御值等属性。会显示附加值即“()”里的内容。类型的附加值,如果固定可通过Attribute的方式进行定义,当然也可以使用下面介绍的复合类型。

ICON:类型左侧卡片上的攻击距离的表现,由一个图标和数值组成。

复合类型:即左侧卡片做为一个整体属性,其本身又由子属性组成。如下代码所示,武器类包括子属性。

	public class Weapon {

		[PropertyDescription("power", "a power data")]
		public ZProperty<float> power = new ZProperty<float>();
	}

	public class Person {

		public string TestID;
			
		[PropertyDescription("等级", "玩家的等级信息")]
		public ZProperty<int> rank = new ZProperty<int>();

		[PropertyDescription("血量", "玩家的名字")]
		[PropertyUIItemRes("Test/Image")]
		public ZProperty<int> blood = new ZProperty<int>();

		[PropertyDescription("weapon007", "a power weapon")]
		public ZProperty<Weapon> weapon = new ZProperty<Weapon>();
	}

对应的UI结构如下,其中结点的名称如下格式。

调用Bind方法后,就可以实现属性值与表现层自动进行绑定,当然各结点需要绑定对应的UIitem脚本类,才可以进行属性的表现。

属性的持久化支持

正常的持久化,一般常常使用[Serializable]标签进行说明。如果使用这种方法的话,对JSON的支持是个问题。即需要有一些多于的信息被持久化,或者JSON的中属性也使用List的方法,但这样就不是很直观了,当然可以通过Editor扩展工具进行配置。本例中使用的是LitJson,它支持通过JsonData(类似字典结构)持久化为JSON一步步串行的进行持久化工作,中间加入这一层进行转换。

LitJson的官方地址如下,

https://litjson.net

其中进行类型的变换,比较繁琐,还没有找到比较好的方法。

		private void ConvertToObject<T>(T obj, JsonData data){
			List<IZProperty> props = ZPropertyMgr.GetProperties (obj);

			foreach (var p in props) {
				if (ZPropertyMgr.IsPropertable (p.Value.GetType ())) {
					//p.Value = data[p.PropertyID];
					ConvertToObject<T>((T)(p.Value), data[p.PropertyID]);
				}
				else {
					//ret [p.PropertyID] = p.Value;

					var data1 = data[p.PropertyID];

					if (data1.IsDouble)
						p.Value = (double)data1;
					else if (data1.IsInt)
						p.Value = (int)data1;
					else if (data1.IsBoolean)
						p.Value = (bool)data1;
					else if (data1.IsLong)
						p.Value = (long)data1;
					else if (data1.IsString)
						p.Value = (string)data1;
				}
			}

		}

当然也可以根据需求定义相应的持久化类,比如,使用Http等,或者复合形式,只要实现以下接口就可以

	public interface IZPropertyPrefs
	{
		void Save(object obj, string path);
			
		void Load<T>(T obj, string path);

		void LoadFromStr<T>(T obj, string strData);
	}

其中Json持久后的例子如下

 

{

"Person.weapon":{

"Weapon.power":88.2

},

"Person.rank":190,

"Person.blood":98

}

对于double类型,在LitJson中是不能使用整数的,比如上面写为88,就会出现转换错误。这个在LitJson中有说明。

 

数组(队列)支持

下面看一下,对于数组的支持

		[PropertyDescription("testlist", "a test list")]
                [PropertyUIItemRes("Test/Image")]
		public ZPropertyList<int> testList = new ZPropertyList<int>();PropertyUIItemRes("Test/Image")]
		public ZPropertyList<int> testList = new ZPropertyList<int>();

它其实理解为List<ZProperty<int>>,即每个元素也是一个属性,其自身也是一个属性,所有子属性继承父属性的值,比如其PropertyID是一样的。这样在做针对数组属性的表现层时需要了解其PropertyID的含义。如下图所示,有两项      

        person.testList.Add (900);

        person.testList.Add (100);

可以看到对应的UI出现两项。内容如何可以使用Layout等功能,这个不是本框架需要支持的,目前List属性只支持自定义UIItem。

 

多态支持

主要是对接口属性的支持,定义之后可以后续组合不同的对象,代码使用方法如下:

	public interface ITestInterface
	{
		void TestFunc();
	}

	public class TestObj : ITestInterface{
		void ITestInterface.TestFunc(){
			Debug.Log ("Test Func");
		}
	}
		[PropertyDescription("ITestInterface", "a test interface")]
		public ZPropertyList<ITestInterface> testInterface = new ZPropertyList<ITestInterface>();

以上为定义。以下为进行赋值,也需要使用CreateObject方法进行创建。

		var testObj = ZPropertyMgr.CreateObject<TestObj> ();
		testObj.testData.Value = 201;
		person.testInterface.Value = testObj;

这样生成Json也会包含testobj的数据的

也是可以支持UI自动化配置的,如下图所示。当然由于interface的多态性,这里的显示一般使用自定义UIItem,在UI类中再根据不同的实例,进行显示不同的内容,比如,不同的设备或者武器有不同的表现UI等。后续,也可以考虑这一自动化,比如,通过UIitem类Attribute用于类的修饰来完成。(TBD:后续需求再进行追加)

基础模板View表现层

泛型的支持,提供一套基础属性模板封装?

ZECS框架支持

如何在Editor扩展中进行显示在属性面板?自定义接口的话,不好在属性面板中获取了。这里需要考虑对ZECS框架的支持。

using System;
using UnityEditor;

namespace Entitas.VisualDebugging.Unity.Editor {

    public class IntTypeDrawer : ITypeDrawer {

        public bool HandlesType(Type type) {
            return type == typeof(int);
        }

        public object DrawAndGetNewValue(Type memberType, string memberName, object value, object target) {
            return EditorGUILayout.IntField(memberName, (int)value);
        }
    }
}

可以通过以上的代码为每种类型自定义一个属性面板类。详细的代码,请参考:

https://github.com/bennychao/ZECS/tree/b90d5a1e761c18445d75cc8c349441f1fb485b06/TestZECS/Assets/Libs/ZECSer/Src/TypeDrawer

https://www.cnblogs.com/xiaofeixiang/p/4018066.html

 

缺点

1. 这样自定义的属性需要通过.Data进行访问数据源,不支持使用Set、Get访问器,C#不支持有类似的扩展,目前是没有找到方法。这时只能做一个属性类到数据类型的一个强制转换的方法。但在赋值时会生成临时对象,造成GC性能影响。

有关定义转换运算符的方法,其中要注意显式还是隐式转换。

https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/statements-expressions-operators/using-conversion-operators

代码如下:

 
	public class ZProperty<T>
	{
		private T data;
		public ZProperty (T data)
		{
		}

		public static implicit operator T(ZProperty<T> d)  // implicit digit to byte conversion operator
		{
			return d.data;  // implicit conversion
		}

		public static implicit operator ZProperty<T>(T d)  // implicit digit to byte conversion operator
		{
			return new ZProperty<T>(d);  // implicit conversion
		}
	}

可以看出是支持模板的。但对于第二个转换,即从Data类型转换为Property封装类,会生成临时的对象。这个还是通过Pool进行优化掉。

2. 使用反射进行属性的绑定与遍历,对性能有一定的影响,所有要保证不是在Update中调用。

使用Demo代码

		person = ZPropertyMgr.CreateObject<Person> ();
		person.blood.Value = 100;
		person.rank.Value = 2;

		Weapon sword = person.weapon.Value;
		sword.power.Value = 991.0f;

		ZUICommonTools.BindObject (person, ZUI.transform);

		Debug.Log ("perion's l " + person.weapon.Value.power.Value);

		//ZPropertyPrefs.Save((object)sword, "");
		ZPropertyPrefs.LoadFromStr(person, strData.text);

 

Attribute列表:

 

PropertyUIItemTypeAttribute:定义绑定UIItem的类型,主要用于在UI中需要动态创建的Item,即预先没有在UI中定义GameObject的数据属性项。如果,预先已经定义了(通过名称进行匹配到了UIItem对象)那么以预定义为准。参数Type为UIitem类型,Parent为父结点GameObject的ID,默认为当前UI GameObject结点。

BUG列表:

1. 对于List如果设置UIItem,并当没有定义GameObject结点时,创建了结点后,再绑定子结点就会出现问题,因为UIItem也会被继承到子结点上。

2. 可以直接支持一个支持Enum的Property,会自动变换为Enum与Int类型的支持。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值