Unity Dots下有限状态机的实现

     在我上一篇博客https://blog.csdn.net/moonstrace/article/details/114560794的文章里,针对如何利用ASTAR插件实现自动寻路时,利用了两个ENUM实现了一个简单的状态机。但这个状态机并不能具普遍适用。本文试图在这个基础上实现一个简单通用的有限状态机。

     首先我们还是对有限状态机的功能进行拆解。首先,状态机应该与外部有交互功能,外部的输入要能够输入到状态机中。其次,状态机的核心功能是管理状态本身的改变,最后,要实现状态机的功能,其每个子状态显然应该有独特的功能实现才能实现普适性。从数据结构上看,我们可以简单的区分出stateManager以及具体state两个结构。StateManager保存状态目前的状态,而具体的state则负责执行具体的功能。

     接下来我们看看分析一下具体的实现。首先,我们分别使用多个ENUM来管理状态。对每个StateManager,需要知道自身是处于延续还是转换当中;对FSM下的每个state我们都给一个int类型的编号来进行区分,这个有个小技巧是为了在inspector中方便使用,在inspecto可以定义为ENUM,而运行中使用int的值,从而避免使用模板。而为了便于每个状态管理自己,针对每个状态的活跃、淡入、淡出、结束销毁等状态再用一个enum来标记。其次,为了实现与外部的交互,仅需要在StateManager中暴露一个Target字段,告诉StateManger要向哪个状态转换即可。最后,每个子状态的多样化功能的延展显然也不是FSM本身的内容,但如何体现出来呢,我们将子状态抽象成一个Entity即可,具体子状态功能的system实现显然也不应该体现在FSM中。如此,一个StateManager应该具备如下结构:

    public enum ManagerCondition
    {
        Init,
        Continue,
        Fading,
        Destroy
    }


    public enum StateConditionEnumValue
    {
        Active,
        Destroy,
        Pause,
        Entering,
        Outing,
    }

    public struct StateManager : IComponentData
    {
        public ManagerCondition condition;

        public int currentTag;
        public int targetTag;

        public Entity currentEntity;
        public Entity targetEntity;

        public float maxTranslationTime;
        public float translatedTime;
    }

       通过Entity 来管理各个不同的子状态,可以在转化时,将各个子状态保存为一个(int,Entity)的IbufferElement,如此,要生成或销毁各个子状态只需要使用Instantiate接口就可以了,如此可以完美避开在StateManager中使用模板的问题。(吐槽一下Unity的job系统使用模板真的太复杂了。。我初期写了好几个模板来实现FSM后面全放弃了。。)转化的类大致如下,使用一个enum的模板可以在inspector中不再面对int的难以理解的数字:

public class InitStateManagerSystem<EnumTag> : MonoBehaviour, IConvertGameObjectToEntity, IDeclareReferencedPrefabs
    where EnumTag : System.Enum
    {
        [Serializable]
        public struct StateSetup
        {
            public EnumTag Tag;
            public GameObject Prefab;
            public float FadeOutTime;
        }
        public StateSetup[] States;
        public bool UseLTW;
        public float FadeTime;
        public void Convert()
    }

 

Unity DOTS(Data-Oriented Technology Stack)中,序列帧动画可以使用ECS(Entity Component System)和Burst Compiler来提高游戏性能。以下是基本步骤: 1. 创建一个Animation组件,用于存储动画帧的信息。 ``` [Serializable] public struct Animation : IComponentData { public int frameCount; public float frameTime; public float elapsedTime; } ``` 2. 创建一个AnimationSystem系统,用于更新动画帧的信息。 ``` public class AnimationSystem : SystemBase { protected override void OnUpdate() { float deltaTime = Time.DeltaTime; Entities.ForEach((ref Animation animation) => { animation.elapsedTime += deltaTime; int frameIndex = (int)(animation.elapsedTime / animation.frameTime) % animation.frameCount; // 更新动画帧 }).ScheduleParallel(); } } ``` 3. 创建一个RenderMesh组件,用于显示动画帧。 ``` public struct RenderMesh : IComponentData { public Mesh mesh; public Material material; public int subMesh; } ``` 4. 创建一个RenderSystem系统,用于显示动画帧。 ``` public class RenderSystem : SystemBase { private EndSimulationEntityCommandBufferSystem _entityCommandBufferSystem; protected override void OnCreate() { _entityCommandBufferSystem = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>(); } protected override void OnUpdate() { EntityCommandBuffer commandBuffer = _entityCommandBufferSystem.CreateCommandBuffer(); Entities.ForEach((Entity entity, ref RenderMesh renderMesh, in Animation animation) => { // 更新RenderMesh的Mesh和Material commandBuffer.SetSharedComponent(entity, new RenderMesh { mesh = ..., // 根据动画帧更新Mesh material = renderMesh.material, subMesh = renderMesh.subMesh }); }).ScheduleParallel(); } } ``` 5. 在场景中创建一个实体,并添加Animation和RenderMesh组件。 ``` Entity entity = EntityManager.CreateEntity(); EntityManager.AddComponentData(entity, new Animation { frameCount = ..., // 动画帧数 frameTime = ..., // 动画帧时间间隔 elapsedTime = 0f }); EntityManager.AddSharedComponentData(entity, new RenderMesh { mesh = ..., // 初始Mesh material = ..., // 初始Material subMesh = 0 }); ``` 这样,序列帧动画就可以在Unity DOTS中实现了。注意,这只是一个基本示例,具体实现可能会因游戏需求而有所不同。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值