在战斗场景中,主角不能像主城系统中一样只需要移动就可以了,主角在移动时可能会受到伤害,受伤时就要播放受伤动画和音效;主角也有可能释放技能,释放技能时就可能不受控制,有一些技能可能会被中断。

总之,我们把复杂的逻辑封装到逻辑实体和状态机里来实现。

在战斗场景中,通过操作各种逻辑实体(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;//引用Controller
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//Player逻辑实体
{
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