高级状态机创建

部署运行你感兴趣的模型镜像

首先知道简单状态机

这种方法中我们把状态和条件判断都集中在一个类里面,代码量冗多,不方便管理,很容易搞混

高级状态机则是通过把状态,状态引用,角色引用分离,是代码看起来十分清晰;


首先先定义一个抽象状态基类(FSMState),枚举切换状态的条件,状态若干;新建一个字典《看见什么条件,切换为什么状态》;添加字典是必须判断输入是否规范,增加程序的 健壮性。比如说条件是否为空,状态是否为空,条件是否已经存在。再定义一个类通过传条件获取当前的状态。接着定义虚方法进入状态之前和状态之后,定义抽象方法状态进行时和条件判断,最后引用一个状态机类(FSMSystem),在构造函数赋值;

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public enum Transition
{
    NullTransition=0,
    SeePlayer,
    LostPlayer,
}
public enum StateID
{
    NullStateId=0,
    Patrol,
    Chase,
}

public abstract class FSMState{


    protected StateID stateID;
    public StateID ID { get { return stateID; } }                    //返回stateID
    public Dictionary<Transition, StateID> map = new Dictionary<Transition, StateID>();
    protected FSMSystem fsm;                                        //引用的FSMSystem

    public FSMState(FSMSystem fsm)                                  //先调用构造函数更新引用的FSMSystem;
    {
        this.fsm = fsm;
    }



    public void AddTransition(Transition tran,StateID id)         //添加转化条件(构造)
    {
        if (tran==Transition.NullTransition)
        {
            Debug.LogError("不允许条件为空");return;
        }
        if (id == StateID.NullStateId)
        {
            Debug.LogError("不允许状态为空"); return;
        }
        if (map.ContainsKey(tran))
        {
            Debug.LogError("条件已经存在"); return;
        }
        map.Add(tran,id);
    }


    public void DeleteTransition(Transition tran,StateID id)
    {
        if (tran == Transition.NullTransition)
        {
            Debug.LogError("不允许条件为空"); return;
        }
        if (map.ContainsKey(tran)==false)
        {
            Debug.LogError("条件不存在"); return;
        }
        map.Remove(tran);
    }

    public StateID GetState(Transition tran)
    {
        if (map.ContainsKey(tran))
        {
            return map[tran];
        }
        return StateID.NullStateId;
    }

    public virtual void BeforeEnter() { }
    public virtual void AfterExit() { }
    public abstract void Act(GameObject npc);                 //进入状态之时(抽象类每个子类不同必须重写)
    public abstract void Reason(GameObject npc);              //判断转换条件,条件机



    // Use this for initialization
    void Start () {
		
	}
	
	// Update is called once per frame
	void Update () {
		
	}
}


定义一个状态机类(FSMSystem),为不用遍历采用字典《状态,状态抽象类》,变量有,当前状态和当前 抽象状态基类

函数一:通过传抽象状态基类为字典赋值

函数二:执行条件(如果条件没报错的话),赋值当前状态和当前 抽象状态基类。

函数三:传值gameobject给update;时刻调用状态进行时和条件判断

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FSMSystem  {                                                                    //状态机

    private Dictionary<StateID, FSMState> states = new Dictionary<StateID, FSMState>();     //状态,条件状态

    private StateID currentStateID;         //当前状态
    private FSMState currentState;          //当前状态和条件

    public void AddState(FSMState s)
    {
        if (s==null)
        {
            Debug.LogError("FSM不能为空");return;
        }
        if (currentState ==null)
        {
            currentState = s;
            currentStateID = s.ID;
        }
        if (states.ContainsKey(s.ID))
        {
            Debug.LogError("状态存在"+s.ID); return;

        }
        states.Add(s.ID,s);
    }
    public void DeleState(StateID id)
    {
        if (id==StateID.NullStateId)
        {
            Debug.LogError("无法删除空状态");return;
        }
        if (states.ContainsKey(id)==false)
        {
            Debug.LogError("无法删除不存在的状态"+id);
        }
        states.Remove(id);
    }

    public void PerformTransition(Transition tran)              //传进来一个条件,执行条件
    {
        if (tran==Transition.NullTransition)
        {
            Debug.LogError("无法执行空的转换条件");return;
        }
        StateID id= currentState.GetState(tran);                //通过条件获取ID
        if (id==StateID.NullStateId)
        {
            Debug.LogError("该条件没有对应的状态");return;
        }
        if (states.ContainsKey(id)==false)                      //当前状态机字典里是否有这个状态
        {
            Debug.LogError("状态机不存在这个状态"); return;
        }
        FSMState state = states[id];                            //把状态和条件赋值给 局部变量
        currentState.AfterExit();                               //播放状态离开部分
        currentState = state;                                   //赋值给当前条件和状态
        currentStateID = id;                                    //赋值给当前状态
        currentState.BeforeEnter();                             //播放状态进入之前
    }

	// Use this for initialization
	void Start () {
		
	}
	
	// Update is called once per frame
	public void Update (GameObject npc) {
        currentState.Act(npc);                                  //调用当前状态的ACT
        currentState.Reason(npc);
	}
}


定义巡逻类,追寻类;重写构造方法,状态进行时和条件判断

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PatrolState :  FSMState{

    public List<Transform> path=new List<Transform>() ;
    public int index = 0;
    public Transform player;
    public PatrolState(FSMSystem fsm) : base(fsm)                               //那么子类也要传一个FSM用于更新父类fsm
    {
        player = GameObject.Find("Play").transform;
        stateID = StateID.Patrol;                                              //继承并修改父类参数
        Transform Path1 = GameObject.Find("Path").transform;
        Transform[] children = Path1.GetComponentsInChildren<Transform>();    //这个位置组包含Path的位置
        foreach (Transform child in children)
        {
            if (child!=Path1)
            {
                path.Add(child);
            }
        }                
    }

    public override void Act(GameObject npc)                                //覆盖重写
    {
        npc.transform.LookAt(path[index].position);
        npc.transform.Translate(Vector3.forward*Time.deltaTime*3);
        if (Vector3.Distance(npc.transform.position,path[index].position)<1)
        {
            index++;
            index %= path.Count;
        }
    }

    public override void Reason(GameObject npc)                             
    {
        if (Vector3.Distance(player.position,npc.transform.position)<3)
        {
            fsm.PerformTransition(Transition.SeePlayer);
        }
        //throw new NotImplementedException();
    }

    // Use this for initialization
    void Start () {
		
	}
	
	// Update is called once per frame
	void Update () {
		
	}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Chase : FSMState {

    private Transform player;
    public Chase(FSMSystem fsm) : base(fsm)
    {
        stateID = StateID.Chase;
        player = GameObject.Find("Play").transform;
    }

    public override void Act(GameObject npc)
    {
        npc.transform.LookAt(player.position);
        npc.transform.Translate(Vector3.forward*Time.deltaTime*2);
    }

    public override void Reason(GameObject npc)
    {
        if (Vector3.Distance(player.position, npc.transform.position) > 6)
        {
            fsm.PerformTransition(Transition.LostPlayer);
        }
        //throw new NotImplementedException();
    }

    // Use this for initialization
    void Start () {
		
	}
	
	// Update is called once per frame
	void Update () {
		
	}
}



定义敌人类

  1. new一个状态机,初始化,新建一个状态机,见一个巡逻类,赋值状态机给构造函数往字典《看见什么条件,切换为什么状态》加“看见敌人条件”,“追寻状态”。调用状态机添加巡逻状态的函数。
  2. 新建一个状态机,见一个追寻类,赋值状态机给构造函数往字典《看见什么条件,切换为什么状态》加“丢失敌人条件”,“巡逻状态”。调用状态机添加追寻状态的函数。
  3. update给状态机update添加this.gameobject'

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class enemy : MonoBehaviour {

    public FSMSystem fsm;

	// Use this for initialization
	void Start () {
        InitFSM();
	}
    void InitFSM()
    {
        fsm = new FSMSystem();

        FSMState patrolState = new PatrolState(fsm);
        patrolState.AddTransition(Transition.SeePlayer,StateID.Chase);
        fsm.AddState(patrolState);

        FSMState ChaseState = new Chase(fsm);
        ChaseState.AddTransition(Transition.LostPlayer, StateID.Patrol);

        fsm.AddState(ChaseState);


    }

    // Update is called once per frame
    void Update () {
        fsm.Update(this.gameObject);
	}
}


您可能感兴趣的与本文相关的镜像

Stable-Diffusion-3.5

Stable-Diffusion-3.5

图片生成
Stable-Diffusion

Stable Diffusion 3.5 (SD 3.5) 是由 Stability AI 推出的新一代文本到图像生成模型,相比 3.0 版本,它提升了图像质量、运行速度和硬件效率

<think>我们正在讨论的是在无状态(stateless)环境下动态创建状态机的问题。根据引用[1]和[2],Stateless框架是一个轻量级的状态机框架,它允许我们以简单的方式定义状态机。但是,用户的问题是在无状态环境下动态创建状态机,这意味着我们可能需要在每次请求时重新创建状态机,而不依赖于之前的状态。 引用[2]中展示了一个使用Stateless框架创建状态机的例子,它通过配置状态和触发器的转换关系来定义状态机。但是,这个例子是在代码中静态配置的。如果我们想要动态创建状态机,我们需要在运行时根据某些条件来配置状态机。 引用[4]提到了一个方法:`PermitDynamic`,它允许我们动态选择下一个状态。这提供了一种动态性的方式,但还不够,因为用户需要的是整个状态机的动态创建,而不仅仅是动态选择下一个状态。 因此,我们需要考虑如何在运行时动态地定义状态机的状态和转换规则。我们可以将状态机的配置信息(如状态、触发器、转换规则)存储在外部(如数据库或配置文件),然后在每次创建状态机时从外部源加载配置。 步骤: 1. 定义状态和触发器的枚举(或者使用字符串等动态类型,但Stateless要求状态和触发器是同一类型,且通常使用枚举)。但是,如果我们希望完全动态,我们可以考虑使用字符串来表示状态和触发器。 2. 设计一个存储状态机配置的方式,例如,我们可以有一个配置列表,每个配置项包括:当前状态、触发器、目标状态(或者动态选择目标状态的方法)。 3. 在创建状态机实例时,从存储中读取配置,然后通过Stateless提供的API动态配置状态机。 但是,Stateless框架的状态机配置是在编译时类型安全的,它通常要求状态和触发器是枚举。然而,我们可以通过使用`StateMachine<string, string>`来使用字符串作为状态和触发器,从而实现动态配置。 示例代码(C#): 假设我们有一个状态机配置的列表,每个配置项包含:当前状态(CurrentState)、触发器(Trigger)、目标状态(NextState)以及是否使用动态选择(如果使用动态选择,则NextState可能是一个函数,但在配置列表中存储函数可能不太现实,所以我们可以考虑另一种方式:存储一个条件表达式或者委托的名称,然后通过反射调用。但这样会复杂,且可能不安全。因此,我们这里只考虑静态配置的转换,动态转换使用`PermitDynamic`,但需要预先注册委托)。 为了简化,我们可以这样设计: 1. 定义一个状态机配置类: public class StateTransitionConfig { public string CurrentState { get; set; } public string Trigger { get; set; } public string NextState { get; set; } // 如果使用静态转换 // 或者,我们可以有一个标志,指示是否动态选择状态,但动态选择需要提供委托,这需要我们在代码中预先定义好委托,然后通过配置触发器的名称来关联。 } 2. 从某个数据源(如数据库)读取配置列表。 3. 创建状态机并配置: var stateMachine = new StateMachine<string, string>("初始状态"); foreach (var config in configList) { // 获取当前状态的配置 var stateConfig = stateMachine.Configure(config.CurrentState); // 静态转换 stateConfig.Permit(config.Trigger, config.NextState); } 但是,如果我们想要动态选择目标状态(即根据运行时条件决定下一个状态),我们可以使用`PermitDynamic`,但需要提供一个委托。这个委托必须预先定义好,我们可以在配置中指定一个委托名称,然后通过一个字典映射到具体的委托方法。 例如: public delegate string DynamicStateSelector(); private Dictionary<string, DynamicStateSelector> _dynamicSelectors = new Dictionary<string, DynamicStateSelector>(); // 注册动态选择器 _dynamicSelectors.Add("Selector1", () => { // 根据一些条件返回状态字符串 return "State1"; }); // 在配置循环中,如果配置项标记为动态,则使用PermitDynamic if (config.IsDynamic) { var selector = _dynamicSelectors[config.SelectorName]; stateConfig.PermitDynamic(config.Trigger, selector); } 这样,我们就可以在运行时动态配置状态机了。 但是,请注意,无状态环境意味着我们不会保存状态机的状态,所以每次请求时,我们可能需要根据某个外部存储的状态值来初始化状态机,然后根据事件(触发器)进行状态转换,并保存新的状态。 总结:在无状态环境下动态创建状态机,我们可以通过以下步骤实现: 1. 将状态机的配置存储在外部(如数据库),包括状态转换规则(静态转换和动态转换的委托名称)。 2. 当需要处理一个事件时,从外部存储获取当前状态,然后根据配置动态构建状态机(使用字符串作为状态和触发器类型)。 3. 对于动态转换,预先定义一组委托(动态状态选择器),并在配置中通过名称引用。 4. 触发事件后,得到新状态,然后将新状态保存回外部存储。 这样,状态机的状态保存在外部存储中,而状态机的转换规则通过动态配置加载,从而实现了无状态环境下的状态机动态创建和使用。 注意:Stateless框架本身并不要求状态机实例是长期存在的,我们可以在每次需要时根据当前状态和配置重新创建状态机实例,然后触发事件,得到新状态,然后销毁状态机实例。这样,应用服务器本身就可以是无状态的。 示例代码: 假设我们有一个服务,它处理订单状态。订单状态存储在数据库中,而状态转换规则也存储在数据库中(或者配置中心)。 伪代码: public class OrderService { // 获取订单的当前状态(从数据库) string currentState = GetOrderState(orderId); // 从配置源加载该订单类型的状态机配置 var configs = LoadStateMachineConfigs(orderType); // 创建状态机 var sm = new StateMachine<string, string>(currentState); // 配置状态机 foreach (var config in configs) { var stateConfig = sm.Configure(config.CurrentState); if (config.IsDynamic) { // 获取预先注册的委托 var selector = GetDynamicSelector(config.SelectorName); stateConfig.PermitDynamic(config.Trigger, selector); } else { stateConfig.Permit(config.Trigger, config.NextState); } } // 触发事件(例如:用户支付) sm.Fire("Pay"); // 获取新状态 string newState = sm.State; // 保存新状态到数据库 SaveOrderState(orderId, newState); } 这样,每次处理事件时,我们都重新创建状态机,然后触发事件,保存新状态。由于状态机配置是从外部加载的,所以我们可以动态修改配置(比如添加新的状态转换规则)而无需重新部署应用。 但是,这种方法在频繁触发事件时可能会有性能问题,因为每次都要重新加载配置并构建状态机。我们可以通过缓存状态机配置来优化。 另外,引用[3]提到了一种动态生成状态机的方法,它使用声明式配置(通过Action类)来动态生成状态机。虽然这是另一个框架(MSM)的做法,但思路类似:将配置与代码分离,在运行时根据配置生成状态机。 因此,在Stateless框架中,我们可以通过上述方式实现动态创建状态机。 相关问题: 1. 动态状态机配置如何存储和管理? 2. 在无状态环境中,如何保证状态转换的并发安全? 3. 如何优化动态创建状态机的性能? 4. Stateless框架支持哪些动态配置方式? 5. 除了Stateless框架,还有哪些支持动态创建状态机的框架?</think>### 在无状态环境下动态创建状态机的方法 在无状态(Stateless)环境中动态创建状态机,核心思路是**将状态机配置与状态存储分离**,通过外部数据源驱动状态机的动态构建。以下是具体实现方法: --- #### 一、核心实现步骤 1. **状态配置外部化** 将状态转换规则存储在数据库或配置文件中,例如: | 当前状态 | 触发器 | 目标状态 | 动态条件 | |----------|--------|----------|----------| | `草稿` | `提交` | `审核中` | - | | `审核中` | `通过` | `已发布` | `IsAdmin`| | `审核中` | `驳回` | `草稿` | - | 2. **动态加载配置** 初始化时从外部源加载配置,动态构建状态机: ```csharp // 伪代码:从数据库加载配置 var transitions = db.GetTransitions(); var stateMachine = new StateMachine<string, string>("草稿"); foreach (var t in transitions) { stateMachine.Configure(t.CurrentState) .PermitIf(t.Trigger, t.NextState, () => CheckCondition(t.DynamicCondition)); } ``` 3. **使用动态触发器** 通过 `PermitDynamic()` 实现运行时状态决策(引用[4]): ```csharp // 根据业务数据动态选择目标状态 stateMachine.Configure("审核中") .PermitDynamic("分配", context => context.User.IsAdmin ? "高级处理" : "普通处理" ); ``` 4. **无状态设计** - **状态存储**:将当前状态保存在数据库/缓存中,而非内存 - **实例重建**:每次请求时根据存储的状态重建状态机: ```csharp var currentState = db.GetState(orderId); var sm = new StateMachine<string, string>(currentState); sm.Configure(...); // 动态加载配置 sm.Fire(trigger); // 触发事件 db.SaveState(orderId, sm.State); // 保存新状态 ``` --- #### 二、关键代码示例 ```csharp // 动态构建状态机(引用[2]扩展) public StateMachine<string, string> BuildStateMachine(string initialState) { var sm = new StateMachine<string, string>(initialState); var rules = LoadRulesFromDatabase(); // 从数据库加载配置 foreach (var rule in rules) { sm.Configure(rule.CurrentState) .OnEntry(() => LogEntry(rule.CurrentState)) .PermitDynamic(rule.Trigger, ctx => rule.UseDynamic ? CalculateNextState(ctx) : rule.NextState ); } return sm; } // 使用示例 var orderState = GetOrderStateFromDB(orderId); var sm = BuildStateMachine(orderState); sm.Fire("支付"); // 触发事件 SaveStateToDB(sm.State); // 保存新状态 ``` --- #### 三、解决无状态环境的关键点 1. **状态持久化** 每次操作后立即将新状态保存到外部存储(如数据库),确保下次请求可重建。 2. **配置热更新** 修改数据库中的转换规则即可动态调整状态机逻辑,无需重启服务。 3. **动态条件支持** 通过 `PermitIf` 和 `PermitDynamic` 实现运行时决策(引用[4]): ```csharp .PermitIf("提交", "审核中", () => DateTime.Now.Hour < 17) // 仅限工作时间提交 ``` 4. **上下文传递** 在触发事件时注入业务上下文: ```csharp sm.Fire("审核", new { User = currentUser }); // 上下文用于动态计算状态 ``` --- #### 四、适用场景 1. 需要运行时修改状态规则的业务(如工作流引擎) 2. 多租户系统中各租户拥有独立状态流 3. 状态转换依赖实时业务数据的场景(如风控系统) > **注意事项**:频繁重建状态机可能影响性能,建议通过缓存配置优化加载速度。对于超复杂状态机,可参考 MSM 的分层状态设计(引用[3])。 --- ### 相关问题 1. 如何保证动态状态机在高并发场景下的数据一致性? 2. Stateless 框架的 `PermitDynamic` 与 `PermitIf` 有何本质区别? 3. 在微服务架构中如何跨服务管理状态机? 4. 动态状态机配置变更时如何避免版本冲突? 5. 除了数据库,还有哪些存储方案适合保存状态机配置?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值