这篇直接接着上一篇开始写啦。
目录
四、开始撸状态机代码!
1、写状态类
IdleState
AttackingState
ChasingState
ReturningState
2、Enemy类
五、测试运行
使用GF的状态机的话,我们要做的主要是实现一下各个状态类。状态机的创建使用这个模块的Create函数即可,然后就可以用了。
所以实际上是开始撸状态代码!
GF里的状态机需要继承FsmState这个类......嗯不多说了直接开始干就知道了!
有个注意点这里先提一下,就是状态类里面使用ChangeState<>()方法并不是立即切换状态,下面的代码不执行。并且执行这个方法后就会执行该状态类的OnLeave()。所以要注意如果ChangeState<>()后面的内容不想执行的话要加上return语句。(否则的话比如某个引用在OnLeave()的时候释放了,那在ChangeState<>()后面执行就会报错。)
注:因为状态机中要调用Enemy实体类的内容,所以写状态的时候实体类里面也做了点工作,写完状态类后会贴出来代码。
类继承GameFramework.Fsm.FsmState
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityGameFramework.Runtime;
using GameFramework.Fsm;
namespace ShadowU
{
public class IdleState : FsmState
{
protected override void OnDestroy(IFsm fsm)
{
base.OnDestroy(fsm);
}
protected override void OnEnter(IFsm fsm)
{
base.OnEnter(fsm);
}
protected override void OnInit(IFsm fsm)
{
base.OnInit(fsm);
}
protected override void OnLeave(IFsm fsm, bool isShutdown)
{
base.OnLeave(fsm, isShutdown);
}
protected override void OnUpdate(IFsm fsm, float elapseSeconds, float realElapseSeconds)
{
base.OnUpdate(fsm, elapseSeconds, realElapseSeconds);
}
}
}
然后就是很简单的根据上一篇的设计填代码就行了:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityGameFramework.Runtime;
using GameFramework.Fsm;
namespace ShadowU
{
public class IdleState : FsmState
{
private Enemy owner; //状态机所控制的Enemy实体
private float moveTimer; //移动计时器,归零的时候移动一次
private Vector3 targetPos; //Idle移动的时候的随目标点
private float speed; //速度
protected override void OnDestroy(IFsm fsm)
{
base.OnDestroy(fsm);
}
protected override void OnEnter(IFsm fsm)
{
base.OnEnter(fsm);
owner = fsm.Owner;
speed = owner.enemyData.Speed;
SetMoveTimer();
SetRandomTargetPos();
}
protected override void OnInit(IFsm fsm)
{
base.OnInit(fsm);
}
protected override void OnLeave(IFsm fsm, bool isShutdown)
{
base.OnLeave(fsm, isShutdown);
owner = null;
}
protected override void OnUpdate(IFsm fsm, float elapseSeconds, float realElapseSeconds)
{
base.OnUpdate(fsm, elapseSeconds, realElapseSeconds);
if (moveTimer > 0)
{
if (Vector3.Distance(targetPos, owner.transform.position) > 0.1)
{
//如果还没有到达目标位置
//向目标位置移动
Vector3 direction = Vector3.Normalize(targetPos - owner.transform.position);
owner.transform.Translate(direction * speed * elapseSeconds);
}
else
{
//已经到了目标位置
//Enemy就停在原地啥也不做
}
}
else
{
//重新选择一个目标点移动
SetMoveTimer();
SetRandomTargetPos();
}
moveTimer -= elapseSeconds;
//如果玩家进入了发现范围,则进入追逐状态
if (owner.IsPlayerFound())
{
ChangeState(fsm);//状态机的状态切换
}
}
///
/// 随机设置一个0.5-3秒的Timer
///
private void SetMoveTimer()
{
moveTimer = Random.Range(0.5f,3.0f);
}
///
/// 随机设置一个IdleRange内的目标点
///
private void SetRandomTargetPos()
{
float idleRange = -owner.enemyData.IdleRange;
targetPos = new Vector3(Random.Range(-idleRange,idleRange),owner.transform.position.y,owner.transform.position.z);
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using GameFramework.Fsm;
using UnityGameFramework.Runtime;
namespace ShadowU
{
public class AttackingState : FsmState
{
private Enemy owner;
private Transform targetTran;
private float speed = 0.7f; //速度 因为即便进入了攻击范围还要靠近走走
private Color preCol; //之前Enemy的颜色
protected override void OnDestroy(IFsm fsm)
{
base.OnDestroy(fsm);
}
protected override void OnEnter(IFsm fsm)
{
base.OnEnter(fsm);
owner = fsm.Owner;
targetTran = owner.targetTran;
//进入攻击状态 Enemy变为红色
preCol = owner.GetComponent().material.color;
owner.GetComponent().material.color = Color.red;
Log.Info("变红色!");
}
protected override void OnInit(IFsm fsm)
{
base.OnInit(fsm);
}
protected override void OnLeave(IFsm fsm, bool isShutdown)
{
base.OnLeave(fsm, isShutdown);
//退出攻击状态 颜色变回去
owner.GetComponent().material.color = preCol;
owner = null;
targetTran = null;
}
protected override void OnUpdate(IFsm fsm, float elapseSeconds, float realElapseSeconds)
{
base.OnUpdate(fsm, elapseSeconds, realElapseSeconds);
//慢慢靠近目标
Vector3 direction = Vector3.Normalize(targetTran.position - owner.transform.position);
owner.transform.Translate(direction * speed * elapseSeconds);
//如果超出了攻击范围 则进入追逐状态
if (!owner.IsInAttackRange())
{
ChangeState(fsm);
}
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using GameFramework.Fsm;
namespace ShadowU
{
public class ChasingState : FsmState
{
private Enemy owner;
private Transform targetTran;
private float speed; //速度
protected override void OnDestroy(IFsm fsm)
{
base.OnDestroy(fsm);
}
protected override void OnEnter(IFsm fsm)
{
base.OnEnter(fsm);
owner = fsm.Owner;
targetTran = owner.targetTran;
speed = owner.enemyData.Speed;
}
protected override void OnInit(IFsm fsm)
{
base.OnInit(fsm);
}
protected override void OnLeave(IFsm fsm, bool isShutdown)
{
base.OnLeave(fsm, isShutdown);
owner = null;
targetTran = null;
}
protected override void OnUpdate(IFsm fsm, float elapseSeconds, float realElapseSeconds)
{
base.OnUpdate(fsm, elapseSeconds, realElapseSeconds);
//追逐玩家
Vector3 direction = Vector3.Normalize(targetTran.position - owner.transform.position);
owner.transform.Translate(direction * speed * elapseSeconds);
//如果超出了追逐的活动范围则进入返回状态
if (owner.IsOutOfChasingRange())
{
ChangeState(fsm);
return;
}
//如果进入了攻击范围则进入攻击状态
if (owner.IsInAttackRange())
{
ChangeState(fsm);
}
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using GameFramework.Fsm;
namespace ShadowU
{
public class ReturningState : FsmState
{
private Enemy owner;
private float speed;
private float timer; //敌人返回两秒后才会检测玩家是否进入发现范围,否则如果玩家一直在发现范围内Enemy会卡在追逐范围边缘。
protected override void OnDestroy(IFsm fsm)
{
base.OnDestroy(fsm);
}
protected override void OnEnter(IFsm fsm)
{
base.OnEnter(fsm);
owner = fsm.Owner;
speed = owner.enemyData.Speed;
timer = 2.0f;
}
protected override void OnInit(IFsm fsm)
{
base.OnInit(fsm);
}
protected override void OnLeave(IFsm fsm, bool isShutdown)
{
base.OnLeave(fsm, isShutdown);
owner = null;
}
protected override void OnUpdate(IFsm fsm, float elapseSeconds, float realElapseSeconds)
{
base.OnUpdate(fsm, elapseSeconds, realElapseSeconds);
//向Enemy的原点移动
Vector3 direction = Vector3.Normalize(owner.enemyData.Origin- owner.transform.position);
owner.transform.Translate(direction * speed * elapseSeconds);
if(timer > 0)
{
timer -= elapseSeconds;
}
else
{
//玩家进入发现范围 转换到 追逐状态
if (owner.IsPlayerFound())
{
ChangeState(fsm);
return;
}
}
//Enemy进入了巡逻范围 则转到巡视状态
if (owner.IsInIdleRange())
{
ChangeState(fsm);
}
}
}
}
Enemy类也进行了一点修改,主要就是加入了四个检测:IsPlayerFound(),IsInAttackRange(),IsOutOfChasingRange()和IsInIdleRange();以及创建、启动和销毁状态机的内容。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using GameFramework.Fsm;
using UnityGameFramework.Runtime;
namespace ShadowU
{
public class Enemy : EntityLogic
{
public EnemyData enemyData;//敌人实体数据类
public Transform targetTran; //玩家Transform 即Enemy追逐的目标
private IFsm fsm; //状态机
protected override void OnInit(object userData)
{
base.OnInit(userData);
CachedTransform.position = ((EnemyData)userData).Position;
}
protected override void OnShow(object userData)
{
base.OnShow(userData);
enemyData = (EnemyData)userData;
/*
Log.Debug("敌人加载成功");
Log.Info(enemyData.Origin);
Log.Info(enemyData.IdleRange);
Log.Info(enemyData.ChasingRange);
Log.Info(enemyData.AlarmRange);
Log.Info(enemyData.AttackRange);
Log.Info(enemyData.Speed);
*/
targetTran = GameObject.FindGameObjectWithTag("Player").transform;
//状态类的列表
List> states = new List>(){ new IdleState() ,new ChasingState(),new ReturningState(),new AttackingState()};
//创建状态机
fsm = GameEntry.Fsm.CreateFsm("EnemyFsm",this,states);
//启动状态机
fsm.Start();
}
protected override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
base.OnUpdate(elapseSeconds, realElapseSeconds);
}
///
/// 是否已经发现了玩家(检测玩家是否在Alarm范围内)
///
///
public bool IsPlayerFound()
{
return Vector3.Distance(transform.position,targetTran.position)
/// 玩家是否处于Enemy的攻击范围
///
///
public bool IsInAttackRange()
{
return Vector3.Distance(transform.position, targetTran.position) < enemyData.AttackRange;
}
///
/// Enemy是否超出了追逐范围
///
///
public bool IsOutOfChasingRange()
{
return Vector3.Distance(transform.position, enemyData.Origin) > enemyData.ChasingRange;
}
public bool IsInIdleRange()
{
return Vector3.Distance(transform.position, enemyData.Origin) < enemyData.IdleRange;
}
protected override void OnHide(bool isShutdown, object userData)
{
base.OnHide(isShutdown, userData);
GameEntry.Fsm.DestroyFsm(fsm);
fsm = null;
}
}
}
看看效果!
搞定!