当前位置:   article > 正文

Unity Entities在IComponentData中保存回调函数

unity entities

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)
    {
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70

运行环境:必须Unity2021级以上、Entities0.51及以上。因为2021开始才支持C#9.0,然后2021只能运行0.51的ECS,其他版本会报错。也可以用2022和Entities1.0。

点击运行并选中那个带double组件的Entity,就可以看到数据被一直改变了。注意不要选那个带回调组件的Entity,不然会报错,因为这个非常规数据类型ECS还不支持,选中后右侧面板也看不到啥。
在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/112441
推荐阅读
相关标签
  

闽ICP备14008679号