在战斗场景中,主角不能像主城系统中一样只需要移动就可以了,主角在移动时可能会受到伤害,受伤时就要播放受伤动画和音效;主角也有可能释放技能,释放技能时就可能不受控制,有一些技能可能会被中断。
总之,我们把复杂的逻辑封装到逻辑实体和状态机里来实现。
在战斗场景中,通过操作各种逻辑实体(Entity)——逻辑实体调用它的状态机并将自己传入——状态机调用具体的状态类的生命周期方法——状态类通过各个生命周期方法使用传入逻辑实体(Entity)作为参数修改这个实体的状态和调用实体的API。
我们之所以创建各种逻辑实体,是为了减少状态机的数量,减少状态机的数量就能减少状态类的创建数量。在战斗场景中,我们只有一个状态机StateMgr
,它也只创建一次各种状态类,各种状态类具体的逻辑都丢给各个逻辑实体实现。
逻辑实体基类
逻辑实体需要持有状态机的引用,还需要持有表现层Controller的引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| namespace DarknessWarGodLearning { public abstract class EntityBase { public AniState currentAniState = AniState.None; public StateMgr stateMgr = null; public Controller controller = null; public void Move() { stateMgr.ChangeStates(this, AniState.Move); }
public void Idle() { stateMgr.ChangeStates(this,AniState.Idle); } } }
|
EntityPlayer逻辑实体
1 2 3 4 5 6 7
| namespace DarknessWarGodLearning { public class EntityPlayer : EntityBase {
} }
|
在BattleMgr
中新建EntityPlayer
并注入状态机和Controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| EntityPlayer entitySelfPlayer
private void LoadPlayer(MapCfg mapCfg) { GameObject player = resSvc.LoadPrefab(PathDefine.AssassinBattlePlayerPrefab); player.transform.position = mapCfg.playerBornPos; player.transform.localEulerAngles= mapCfg.playerBornRota; player.transform.localScale = Vector3.one;
entitySelfPlayer = new EntityPlayer { stateMgr = stateMgr }; PlayerController playerCtrl = player.GetComponent<PlayerController>(); playerCtrl.Init(); entitySelfPlayer.controller = playerCtrl; }
|
状态接口
每个状态生命周期方法都需要一个逻辑实体作为参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| namespace DarknessWarGodLearning { public interface IState { void Enter(EntityBase entity); void Process(EntityBase entity); void Exit(EntityBase entity); }
public enum AniState { None, Idle, Move, } }
|
StateIdle
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| namespace DarknessWarGodLearning { public class StateIdle : IState { public void Enter(EntityBase entity) { entity.currentAniState = AniState.Idle; }
public void Exit(EntityBase entity) { }
public void Process(EntityBase entity) { } } }
|
StateMove
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| namespace DarknessWarGodLearning { public class StateMove : IState { public void Enter(EntityBase entity) { entity.currentAniState = AniState.Move; }
public void Exit(EntityBase entity) { }
public void Process(EntityBase entity) { } } }
|
状态机StateMgr
状态机负责初始化和管理各个状态,比如状态的切换
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
| using System.Collections.Generic; using UnityEngine;
namespace DarknessWarGodLearning { public class StateMgr : MonoBehaviour { private Dictionary<AniState,IState> fsm = new Dictionary<AniState,IState>(); public void InitMgr() { fsm.Add(AniState.Idle,new StateIdle()); fsm.Add(AniState.Move,new StateMove());
PECommon.Log("Init StateMgr Done"); }
public void ChangeStates(EntityBase entity,AniState targetState) { if(entity.currentAniState == targetState) { return; }
if(fsm.ContainsKey(targetState)) { if(entity.currentAniState != AniState.None) { fsm[entity.currentAniState].Exit(entity); } fsm[targetState].Enter(entity); fsm[targetState].Process(entity); } } } }
|
注意
教程中所设置的状态机都是“一次性”的,没有在Update中调用的生命周期,也就是说每个状态类的Enter、Exit、Process在进入和退出当前状态时都只会调用一次
Controller基类
1 2 3 4 5 6 7 8 9
| using UnityEngine;
namespace DarknessWarGodLearning { public abstract class Controller : MonoBehaviour {
} }
|
将PlayerController
的父类改为Controller