PS:之前完成的敌人状态机:状态机实现 。
一:旧版状态机
在之前的状态机实现中,我们主要做了状态类(StateActionSO ),转换器类(TransitionSO ),转换条件类(ConditionSO ),将它们解耦合并且进行了复用。
但是每一个敌人对象都要挂载一个StateMachine的脚本,这显然是不想看到的。
二:新版状态机
针对这个问题,通过采用接口 + 观察者模式一管理所有敌人的方式来改进。
(1)接口:主要提供 注册 和 注销 的方法
public interface IStateMachineEnemy
{
public void RegisterStateMachineEnemy();
public void UnRegisterStateMachineEnemy();
}
(2)新的状态机:抽象基类 + 泛型。将其设置为单例(方便注册敌人,也可以采取别的更好方式),并且将泛型限制为EnemyController的子类。我们可以为每种类型的敌人创建一个具体状态机的子类,并且挂载在一个长久存在的对象上,同时设置它的转换器以及初始状态。
进一步地,我们可以在子类的状态机中根据具体敌人的特殊性进行扩展和修改。此外,由于不是每个敌人都挂载StateMachine,因此要将StateActionSO ,TransitionSO ,ConditionSO 的所有函数参数直接变更为敌人EnemyController类型即可。
这样的话,在敌人的控制器中,只需要维护一个CurrentEnemyState的属性即可,在一定程度上进行了解耦合。同时将同种类型的敌人进行统一的处理,也符合局部性原则。
注:实际上将全部敌人不分种类统一管理也可以,只通过状态,转换器,转换条件来约束敌人。
public abstract class StateMachine<T> : Singleton<StateMachine<T>> where T : EnemyController
{
[SerializeField] private TransitionSO transition;
[SerializeField] private StateActionSO initState;
private List<T> enemies = new();
private void Update()
{
for (int i = 0; i < enemies.Count; i++)
{
StateMachineTick(enemies[i]);
}
}
private void StateMachineTick(EnemyController enemy)
{
if (transition != null) transition.TryGetApplyCondition(enemy); //每一帧都去找是否有成立的条件
if (CanEnemyAction(enemy))
if (enemy.CurrentEnemyState != null)
enemy.CurrentEnemyState.OnUpdate(enemy);
}
//敌人是否能行动的条件判断
protected virtual bool CanEnemyAction(EnemyController enemy)
{
if (enemy == null || enemy.IsDead || enemy.IsHurt || enemy.enemyCharacterStats.IsWeakState ||
enemy.IsExecuted) return false;
return true;
}
#region 添加删除敌人的接口
public void AddEnemy(T enemy)
{
enemies.Add(enemy);
StartCoroutine(InitEnemyState(enemy));
}
public void RemoveEnemy(T enemy)
{
enemies.Remove(enemy);
}
private IEnumerator InitEnemyState(T enemy)
{
yield return null;
if (enemy != null && CanEnemyAction(enemy))
{
enemy.CurrentEnemyState = initState;
enemy.CurrentEnemyState.OnEnter(enemy);
}
}
#endregion
}