赞
踩
Unity的ECS里的组件只能保存Blittable,Unmanaged的数据,所以C#里的Action,Func,delegate都无法使用了。这里用了一个比较“歪门邪道”的方法来实现,会比较破坏ECS的设计,也可能对性能有点影响。其实ECS的System遍历和处理各种数据是很方便的,不是必须的情况下,不要在组件里保存回调。
实现上就是在IComponentData里保存一个Unmanaged的函数指针,C#里的函数指针写法有点特别,跟C里的很不一样,且必须在C#9.0
及以上才有。然后这个回调函数必须是static的,不能是class或struct的实例方法。还有它的传参有一定限制,无法像常规方法那样随便传参,如果要BurstCompile的话,限制就更多了,不过可以随便传指针进去。如果要用Entities.ForEach,也会有点不方便,但是一般可以根据Unity编辑器里的报错提示进行一定的适配。
其他没啥好说的,用一个简单的例子演示一下,然后直接贴代码,看代码就清楚了。例子就是创建一个Entity添加一个带double字段的Component,然后再创建一个Entity添加一个带函数指针的Component,再实现一个回调函数,里边就是修改带double字段的Component的数据,并返回一个double数据。看完整代码:
using Unity.Burst; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Entities; public unsafe readonly struct Callback { public readonly delegate* unmanaged[Cdecl]<SystemState*, double> FunctionPointer; public Callback(delegate* unmanaged[Cdecl]<SystemState*, double> fp) { FunctionPointer = fp; } } public struct CallbackInstance : IComponentData { public Callback Callback; } public struct ValueInstance : IComponentData { public double Value; } public partial struct CallbackComponentSystem : ISystem { [BurstCompile] static unsafe double StaticFunction(SystemState* statePtr) { ref var state = ref UnsafeUtility.AsRef<SystemState>(statePtr); var time = state.Time.ElapsedTime; var componentTypes = new NativeArray<ComponentType>(1, Allocator.Temp); componentTypes[0] = ComponentType.ReadOnly<ValueInstance>(); var query = state.GetEntityQuery(componentTypes); var entities = query.ToEntityArray(Allocator.Temp); foreach (var entity in entities) { state.EntityManager.SetComponentData(entity, new ValueInstance{ Value = time }); } componentTypes.Dispose(); entities.Dispose(); return time; } public void OnCreate(ref SystemState state) { var valueEntity = state.EntityManager.CreateEntity(); state.EntityManager.AddComponent<ValueInstance>(valueEntity); unsafe { var callbackEntity = state.EntityManager.CreateEntity(); delegate* managed<SystemState*, double> fp = &StaticFunction; state.EntityManager.AddComponentData(callbackEntity, new CallbackInstance{ Callback = new Callback((delegate* unmanaged[Cdecl]<SystemState*, double>)fp) }); } } public void OnUpdate(ref SystemState state) { unsafe { var callbackInstance = state.GetSingleton<CallbackInstance>(); var value = callbackInstance.Callback.FunctionPointer((SystemState*)UnsafeUtility.AddressOf<SystemState>(ref state)); // state.Entities.ForEach((ref ValueInstance vi) => { // vi.Value = value; // }).Run(); } } public void OnDestroy(ref SystemState state) { } }
运行环境:必须Unity2021级以上、Entities0.51及以上。因为2021开始才支持C#9.0,然后2021只能运行0.51的ECS,其他版本会报错。也可以用2022和Entities1.0。
点击运行并选中那个带double组件的Entity,就可以看到数据被一直改变了。注意不要选那个带回调组件的Entity,不然会报错,因为这个非常规数据类型ECS还不支持,选中后右侧面板也看不到啥。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。