Unclaimed Package Is this your package? Claim it to unlock full analytics and manage your listing.
Claim This Package

Install via UPM

Add to Unity Package Manager using this URL

https://www.pkglnk.dev/dynamic.git

README Markdown

Copy this to your project's README.md

Style
Preview
pkglnk installs badge
## Installation

Add **Dynamic For Unity** to your Unity project via Package Manager:

1. Open **Window > Package Manager**
2. Click **+** > **Add package from git URL**
3. Enter:
```
https://www.pkglnk.dev/dynamic.git
```

[![pkglnk](https://www.pkglnk.dev/badge/dynamic.svg?style=pkglnk)](https://www.pkglnk.dev/pkg/dynamic)

README

Dynamic For Unity

1.0.1

将Type.FullName作为Key,以支持泛型

DynamicObject添加Api

RecordObject<T>
RecordStruct<T>  
用于添加不需要指定Key索引的存储内容,
仅可通过DynamicObject.Every(T)查询
删除方式如下
var enumtor = this.dynamicObject.Every<T>().GetEnumerator();
while (enumtor.MoveNext())
{
    enumtor.ResetCurrent();
}

简介

Dynamic For Unity,为Unity实现了dynamic能力

主要有如下两个功能

动态字段 - DynamicField 【核心】

在Unity中,实现类似【dynamic关键字】的功能

DynamicField,可以储存"任意"类型,不论是class还是struct,且类型转换0GC,无装/拆箱,无反射

注:["任意"],指具有一定的限制,问题不大,后面会讲

动态对象 - DynamicObject

在Unity中,实现类似【jsObject (就是js中的object)】动态字段的功能

DynamicObject,可以拥有任意数量,"任意"类型的字段(基于上面的DynamicField实现)

安装

Unity最小版本 2022.3.23f1

第1步. 通过 NuGetForUnity 安装 System.Runtime.CompilerServices.Unsafe 6.0.0

第2步. 通过 OpenUPM 安装 https://openupm.com/packages/com.gm.dynamic/ 本插件

使用

动态字段 - DynamicField 【核心】

以下功能不包含任何GC,且无装箱拆箱,无反射

DynamicField 可以存储"任意"类型

API介绍

using GM.Dynamic;

// DynamicField is struct
DynamicField dynamicField;

// -------------
// As 是 可读可写的
// -------------
dynamicField.As<T>() = (T)value;
T value = dynamicField.As<T>();
// 特别的 一旦As,若当前DynamicField的值类型不匹配,DynamicField的值会重置为default(T)



// -------------
// Is 类型判断
// -------------
bool is_T = dynamicField.Is<T>();
// 对于值类型的T,直接判断是否是同类型
// 对于引用类型,有两种情况
// 1. value != null 时, return value is T
// 2. value == null 时, return false



// -------------
// TryAs 是 只读的
// -------------
dynamicField.As<T1>() = value_T1;
// 这会得到 default(T2),且不改变原有值,值依旧是 value_T1
T2 value = dynamicField.TryAs<T2>();

以下是详细是示例

using GM.Dynamic;

// DynamicField is struct
private DynamicField dynamicField;

public int t_Int;
public float t_Float;
public string t_String;
public bool t_Bool;
public Vector3 t_Vector3;
public GameObject t_GameObject;
public Transform t_Transform;
public UnityEngine.Object t_UnityObject;

// -------------DynamicField.As<T>() 示例
private void As_Sample()
{
  // DynamicField.As<T>() 方法是可读可写的

  dynamicField.As<int>() = Time.frameCount;
  this.t_Int = dynamicField.As<int>();

  dynamicField.As<float>() = Time.deltaTime;
  this.t_Float = dynamicField.As<float>();

  dynamicField.As<string>() = "Hello";
  this.t_String = dynamicField.As<string>();

  dynamicField.As<Vector3>() = Vector3.one;
  dynamicField.As<Vector3>().x = 2;
  this.t_Vector3 = dynamicField.As<Vector3>();

  // As<T> T 为class对象时,可自动进行多态判断
  dynamicField.As<GameObject>() = this.gameObject;
  this.t_UnityObject = dynamicField.As<UnityEngine.Object>();
  this.t_GameObject = dynamicField.As<GameObject>();

  dynamicField.As<UnityEngine.Object>() = this.transform;
  this.t_UnityObject = dynamicField.As<UnityEngine.Object>();
  this.t_Transform = dynamicField.As<Transform>();

   // 特别情况 ------ 当类型不匹配时
   // 当T和已经保存的值的类型不一致时,会将DynamicField的值重置为default(T)
   dynamicField.As<bool?>() = true;
   // 类型不匹配 bool? != bool 
   // 导致 DynamicField的值重置为default(T)
   // 所以下面的结果是 this.t_Bool == false
   this.t_Bool = dynamicField.As<bool>();

   dynamicField.As<UnityEngine.Object>() = this.transform;
   // 类型不匹配 Transform != GameObject
   // 导致 DynamicField的值重置为default(T)
   // 所以下面两个都是null
   this.t_GameObject = dynamicField.As<GameObject>();
   this.t_UnityObject = dynamicField.As<UnityEngine.Object>();
}

// -------------DynamicField.TryAs<T>() 示例
private void TryAs_Sample()
{
  // DynamicField.TryAs<T>() 方法是只读的
  // 所以不可以 dynamicField.TryAs<int>() = 10;

  dynamicField.As<int>() = 10;
  // 类型不匹配时,返回default(T),且不修改已保存的值
  // 下面的 this.t_Float == 0
  this.t_Float = dynamicField.TryAs<float>();
  // this.t_Int 仍然等于 10
  this.t_Int = dynamicField.TryAs<int>();

  dynamicField.As<UnityEngine.Object>() = this.transform;
  // 类型不匹配时,返回default(T),且不修改已保存的值
  // 下面的 this.t_GameObject == null
  this.t_GameObject = dynamicField.TryAs<GameObject>();
  // this.t_Transform == this.transform
  this.t_Transform = dynamicField.TryAs<Transform>();
}

// -------------DynamicField.Is<T>() 示例
private void Is_Sample()
{
  // DynamicField.Is<T>() 类型判断

  // 值类型---------
  dynamicField.As<int>() = 10;
  Debug.Log(dynamicField.Is<int>());// true
  Debug.Log(dynamicField.Is<float>());// false    

  // 引用类型---------
  dynamicField.As<Transform>() = this.transform;
  Debug.Log(dynamicField.Is<UnityEngine.Object>());// true
  Debug.Log(dynamicField.Is<GameObject>());// false

  // 引用类型---------
  dynamicField.As<UnityEngine.Object>() = this.gameObject;
  Debug.Log(dynamicField.Is<UnityEngine.Object>());// true
  Debug.Log(dynamicField.Is<GameObject>());// true
  Debug.Log(dynamicField.Is<Transform>());// false


  dynamicField.As<UnityEngine.Object>() = null;
  // 引用类型,且值为空时,Is均返回false
  Debug.Log(dynamicField.Is<UnityEngine.Object>());// false
  Debug.Log(dynamicField.Is<GameObject>());// false
  Debug.Log(dynamicField.Is<Transform>());// false
  Debug.Log(dynamicField.Is<Vector3>());// false
}

"任意"


重要!!!"任意"类型 指 任何sizeof(T) <= 56 的值类型 和 任意引用类型(sizeof(引用类型) == 地址长度)

说明:为什么是56呢,因为虽然是“动态”字段,但是总得存值,总得确认内存分配大小

当尝试使用 sizeof(T) > 56 时。会抛出 StructOverSizeException 异常

TODO:后续可能会考虑一定程度上开放MaximumSize的设置

动态对象 - DynamicObject 的使用方法

动态对象,是基于DynamicField实现的,模仿了jsObject动态字段的功能

API介绍

using GM.Dynamic;

// DynamicObject is class
DynamicObject dynamicObject = new();

// 动态对象,本质上是一个"字典",string为key,DynamicField为value
// DynamicObject 的基本API与 DynamicField 一致:
// ------- As<T>()      等价于 As<T>(typeof(T).Name)
// ------- TryAs<T>()   等价于 TryAs<T>(typeof(T).Name)
// ------- Is<T>()      等价于 Is<T>(typeof(T).Name)
// 事实上 typeof(T).Name 有GC,所以实际代码做了缓存
dynamicObject.As<int>() = 1;
int int_1 = dynamicObject.TryAs<int>();
bool is_Int = dynamicObject.Is<int>();
// 上下相等
dynamicObject.As<int>(typeof(int).Name) = 1;
int int_1 = dynamicObject.TryAs<int>(typeof(int).Name);
bool is_Int = dynamicObject.Is<int>(typeof(int).Name);

// ----------
// DynamicObject.Reset
// 清空一个字段,可以腾出空间
// ----------
dynamicObject.Reset("Field Key");
dynamicObject.Reset<T>();// dynamicObject.Reset(typeof(T).Name);

// ----------
// DynamicObject.Every<T>()
// 可遍历所有T类型的字段
// ----------
DynamicEnumerable<T> every_T_Enumerable = dynamicObject.Every<T>();
DynamicEnumerator<T> every_T_Enumerator = every_T_Enumerable.GetEnumerator();
// 可foreach 遍历 (只读遍历)
foreach (T item in every_T_Enumerable){ }
// 可手动遍历 (可写遍历)
while (every_T_Enumerator.MoveNext())
{
  // RefCurrent 可写(假设T = int)
  every_T_Enumerator.RefCurrent += 1;
  // Current 只读(假设T = int)
  int i = every_T_Enumerator.Current;
}

以下是详细是示例

using GM.Dynamic;

// DynamicObject is class
DynamicObject dynamicObject = new();

// DynamicObject 基本 示例
private void Object_Sample()
{
  dynamicObject.As<int>("Int_1") = 4;
  int int_1 = dynamicObject.As<int>("Int_1");

  dynamicObject.As<float>("Float_1") = 5f;
  float float_1 = dynamicObject.As<float>("Float_1");

  dynamicObject.As<bool>("Bool_1") = true;
  // 这不会修改已经存在“Bool_1”的值
  float not_Float = dynamicObject.TryAs<float>("Bool_1");
  bool bool_1 = dynamicObject.TryAs<bool>("Bool_1");

  // console = 4_5_0_True
  Debug.Log($"{int_1}_{float_1}_{not_Float}_{bool_1}");

  dynamicObject.As<GameObject>("BattleTarget") = new GameObject();
  if (dynamicObject.TryAs<GameObject>("BattleTarget") != null)
  {
      // Fight
  }



  // 下面等于 dynamicObject.As<GameObject>(typeof(GameObject).Name) = this.gameObject 
  dynamicObject.As<GameObject>() = this.gameObject;
  // 下面等于 dynamicObject.As<Transform>(typeof(Transform).Name) = this.transform 
  dynamicObject.As<Transform>() = this.transform;

  // 不指定Key的写法,可以让一个DynamicObject使用起来好像是任何东西的多态一样
  if (dynamicObject.Is<GameObject>())
  {
      dynamicObject.TryAs<GameObject>().SetActive(false);
  }
  if (dynamicObject.Is<Transform>())
  {
      dynamicObject.TryAs<Transform>().position = Vector3.zero;
  }
  dynamicObject.TryAs<Rigidbody>()?.Move(Vector3.zero, Quaternion.identity);
}

// DynamicObject 特别功能 示例
private void Object_Reset_Sample()
{
  // 清空一个字段,可以腾出空间
  dynamicObject.Reset("Some Field");
  // 等价于 dynamicObject.Reset("GameObject");
  dynamicObject.Reset<GameObject>();

  dynamicObject.As<GameObject>() = this.gameObject;
  dynamicObject.As<Transform>() = this.transform;
  dynamicObject.As<int>("Int_1") = 1;
  dynamicObject.As<int>("Int_2") = 2;
  dynamicObject.As<int>("Int_3") = 3;

  // 只读 遍历
  foreach (var unityObject in dynamicObject.Every<UnityEngine.Object>())
  {
      // 会打印 this.gameObject
      // 会打印 this.transform
      Debug.Log(unityObject);
  }

  // 可写 遍历
  var everyInt = dynamicObject.Every<int>().GetEnumerator();
  while (everyInt.MoveNext())
  {
      everyInt.RefCurrent += 10;
  }

  foreach (var intValue in dynamicObject.Every<int>())
  {
      // 会打印 11 12 13
      Debug.Log(intValue);
  }
}

最后

觉得有趣的点个Star~

谢谢~

Comments

No comments yet. Be the first!