战斗框架主要由有限状态机(FSM),AI,角色信息(RoleInfo),以及控制他们的角色控制器四个部分组成。
整个加载过程为:角色控制器绑定在角色或者角色模型的父物体上,然后每次登录场景之后,创建角色或者怪物,然后通过角色或者怪物身上的初始化函数,初始化FSM,AI,RoleInfo,即通过实例化脚本,通过构造函数将角色控制器传递给脚本,达到降低耦合的作用
首先是动画状态机的控制,如果选择新动画机,只需要将各种状态通过连线进行连接,通过条件判断执行动画,就可以达到动画切换的目的,但是,这其中会有一些问题。第一:一旦动画个数达到一定程度,再添加动画,新增的动画间的连线也会剧增,不方便管理。第二:在每次判定动画切换的时候,需要的条件前提也会多很多,会增加很多不必要的判定。
所以这里我们选择有限状态机,通过将每种动画播放分为切换动画,播放动画,结束动画三个部分,然后通过有限状态机对他们进行总体的控制。好处在于可以减少动画之间线的复杂程度,以及不必要的判定,以及代码的复用性。
同样需要连线,以及动画切换判定,但是连线方式有所不同;
每种动画的切换方式也需要稍微修改一下,以保证他们动画平滑的切换。
第二:因为每种状态都有切开始播放,播放动画,结束动画三种状态,所以抽象出一个角色状态抽象类RoleStateAbstract。
/// <summary>
/// 角色状态的抽象基类
/// </summary>
public abstract class RoleStateAbstract
{
/// <summary>
/// 当前角色有限状态机管理器
/// </summary>
public RoleFSMMgr CurrRoleFSMMgr { get; private set; }
public AnimatorStateInfo CurrRoleAnimatorStateInfo { get; set; }
/// <summary>
/// 构造函数
/// </summary>
/// <param name="roleFSMMgr"></param>
public RoleStateAbstract(RoleFSMMgr roleFSMMgr)
{
CurrRoleFSMMgr = roleFSMMgr;
}
/// <summary>
/// 进入状态
/// </summary>
public virtual void OnEnter() { }
/// <summary>
/// 执行状态
/// </summary>
public virtual void OnUpdate() { }
/// <summary>
/// 离开状态
/// </summary>
public virtual void OnLeave() { }
}
然后其他的角色状态类继承抽象状态类,例如待机状态
/// <summary>
/// 待机状态
/// </summary>
public class RoleStateIdle : RoleStateAbstract
{
/// <summary>
/// 构造函数
/// </summary>
/// <param name="roleFSMMgr">有限状态机管理器</param>
public RoleStateIdle(RoleFSMMgr roleFSMMgr) : base(roleFSMMgr)
{
}
/// <summary>
/// 实现基类 进入状态
/// </summary>
public override void OnEnter()
{
base.OnEnter();
CurrRoleFSMMgr.CurrRoleCtrl.Animator.SetBool(ToAnimatorCondition.ToIdleFight.ToString(), true);
}
/// <summary>
/// 实现基类 执行状态
/// </summary>
public override void OnUpdate()
{
base.OnUpdate();
//得到动画机第一层的角色当前状态信息
CurrRoleAnimatorStateInfo = CurrRoleFSMMgr.CurrRoleCtrl.Animator.GetCurrentAnimatorStateInfo(0);
//如果状态是Idle_Fight
if (CurrRoleAnimatorStateInfo.IsName(RoleAnimatorName.Idle_Fight.ToString()))
{
CurrRoleFSMMgr.CurrRoleCtrl.Animator.SetInteger(ToAnimatorCondition.CurrentState.ToString(), (int)RoleState.Idle);
}
else
{
CurrRoleFSMMgr.CurrRoleCtrl.Animator.SetInteger(ToAnimatorCondition.CurrentState.ToString(), (int)RoleState.None);
}
}
/// <summary>
/// 实现基类 离开状态
/// </summary>
public override void OnLeave()
{
base.OnLeave();
CurrRoleFSMMgr.CurrRoleCtrl.Animator.SetBool(ToAnimatorCondition.ToIdleFight.ToString(), false);
}
}
其他状态因为不是循环动画,所以需要添加一个判断,在动画播放结束后,进入待机状态
///如果动画执行一遍就切换待机
if (CurrRoleAnimatorStateInfo.normalizedTime > 1)
{
CurrRoleFSMMgr.CurrRoleCtrl.ToIdle();
}
通过角色有限状态机执行所有动画之间的切换,并将它们封装成一个方法,供角色控制器调用。
/// <summary>
/// 角色有限状态机管理器
/// </summary>
public class RoleFSMMgr
{
/// <summary>
/// 当前角色控制器
/// </summary>
public RoleCtrl CurrRoleCtrl { get; private set; }
/// <summary>
/// 当前角色状态枚举
/// </summary>
public RoleState CurrRoleStateEnum { get; private set; }
/// <summary>
/// 当前角色状态
/// </summary>
private RoleStateAbstract m_CurrRoleState = null;
/// <summary>
/// 关于角色状态枚举和当前角色状态的字典
/// </summary>
private Dictionary<RoleState, RoleStateAbstract> m_RoleStateDic;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="currRoleCtrl"></param>
public RoleFSMMgr(RoleCtrl currRoleCtrl)
{
CurrRoleCtrl = currRoleCtrl;
m_RoleStateDic = new Dictionary<RoleState, RoleStateAbstract>();
m_RoleStateDic[RoleState.Idle] = new RoleStateIdle(this);
m_RoleStateDic[RoleState.Run] = new RoleStateRun(this);
m_RoleStateDic[RoleState.Attack] = new RoleStateAttack(this);
m_RoleStateDic[RoleState.Hurt] = new RoleStateHurt(this);
m_RoleStateDic[RoleState.Die] = new RoleStateDie(this);
if(m_RoleStateDic.ContainsKey(CurrRoleStateEnum))
{
m_CurrRoleState = m_RoleStateDic[CurrRoleStateEnum];
}
}
#region OnUpdate 每帧执行
/// <summary>
/// 每帧执行
/// </summary>
public void OnUpdate()
{
if(m_CurrRoleState!=null)
{
m_CurrRoleState.OnUpdate();
}
}
#endregion
/// <summary>
/// 切换状态
/// </summary>
/// <param name="newState">新状态</param>
public void ChangeState(RoleState newState)
{
if(CurrRoleStateEnum==newState)
{
return;
}
//调用以前状态的离开方法
if(m_CurrRoleState!=null)
{
m_CurrRoleState.OnLeave();
}
//更改当前状态枚举
CurrRoleStateEnum = newState;
//更改当前状态
m_CurrRoleState = m_RoleStateDic[newState];
//调用新状态的进入方法
m_CurrRoleState.OnEnter();
}
}