GameFrameWork框架(Unity3D)使用笔记(七)游戏主流程ProcedureMain——虽然游戏本来没有怪物但是为了使用状态机模块写一个简单怪物AI吧(下)
2023-09-11 14:20:51 时间
这篇直接接着上一篇开始写啦。
目录
四、开始撸状态机代码!
使用GF的状态机的话,我们要做的主要是实现一下各个状态类。状态机的创建使用这个模块的Create函数即可,然后就可以用了。
所以实际上是开始撸状态代码!
1、写状态类
GF里的状态机需要继承FsmState这个类......嗯不多说了直接开始干就知道了!
有个注意点这里先提一下,就是状态类里面使用ChangeState<>()方法并不是立即切换状态,下面的代码不执行。并且执行这个方法后就会执行该状态类的OnLeave()。所以要注意如果ChangeState<>()后面的内容不想执行的话要加上return语句。(否则的话比如某个引用在OnLeave()的时候释放了,那在ChangeState<>()后面执行就会报错。)
注:因为状态机中要调用Enemy实体类的内容,所以写状态的时候实体类里面也做了点工作,写完状态类后会贴出来代码。
-
IdleState
创建一个IdleState.cs:
类继承GameFramework.Fsm.FsmState<T> ,生成一下重写:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityGameFramework.Runtime;
using GameFramework.Fsm;
namespace ShadowU
{
public class IdleState : FsmState<Enemy>
{
protected override void OnDestroy(IFsm<Enemy> fsm)
{
base.OnDestroy(fsm);
}
protected override void OnEnter(IFsm<Enemy> fsm)
{
base.OnEnter(fsm);
}
protected override void OnInit(IFsm<Enemy> fsm)
{
base.OnInit(fsm);
}
protected override void OnLeave(IFsm<Enemy> fsm, bool isShutdown)
{
base.OnLeave(fsm, isShutdown);
}
protected override void OnUpdate(IFsm<Enemy> 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<Enemy>
{
private Enemy owner; //状态机所控制的Enemy实体
private float moveTimer; //移动计时器,归零的时候移动一次
private Vector3 targetPos; //Idle移动的时候的随目标点
private float speed; //速度
protected override void OnDestroy(IFsm<Enemy> fsm)
{
base.OnDestroy(fsm);
}
protected override void OnEnter(IFsm<Enemy> fsm)
{
base.OnEnter(fsm);
owner = fsm.Owner;
speed = owner.enemyData.Speed;
SetMoveTimer();
SetRandomTargetPos();
}
protected override void OnInit(IFsm<Enemy> fsm)
{
base.OnInit(fsm);
}
protected override void OnLeave(IFsm<Enemy> fsm, bool isShutdown)
{
base.OnLeave(fsm, isShutdown);
owner = null;
}
protected override void OnUpdate(IFsm<Enemy> 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<ChasingState>(fsm);//状态机的状态切换
}
}
/// <summary>
/// 随机设置一个0.5-3秒的Timer
/// </summary>
private void SetMoveTimer()
{
moveTimer = Random.Range(0.5f,3.0f);
}
/// <summary>
/// 随机设置一个IdleRange内的目标点
/// </summary>
private void SetRandomTargetPos()
{
float idleRange = -owner.enemyData.IdleRange;
targetPos = new Vector3(Random.Range(-idleRange,idleRange),owner.transform.position.y,owner.transform.position.z);
}
}
}
-
AttackingState
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using GameFramework.Fsm;
using UnityGameFramework.Runtime;
namespace ShadowU
{
public class AttackingState : FsmState<Enemy>
{
private Enemy owner;
private Transform targetTran;
private float speed = 0.7f; //速度 因为即便进入了攻击范围还要靠近走走
private Color preCol; //之前Enemy的颜色
protected override void OnDestroy(IFsm<Enemy> fsm)
{
base.OnDestroy(fsm);
}
protected override void OnEnter(IFsm<Enemy> fsm)
{
base.OnEnter(fsm);
owner = fsm.Owner;
targetTran = owner.targetTran;
//进入攻击状态 Enemy变为红色
preCol = owner.GetComponent<SpriteRenderer>().material.color;
owner.GetComponent<SpriteRenderer>().material.color = Color.red;
Log.Info("变红色!");
}
protected override void OnInit(IFsm<Enemy> fsm)
{
base.OnInit(fsm);
}
protected override void OnLeave(IFsm<Enemy> fsm, bool isShutdown)
{
base.OnLeave(fsm, isShutdown);
//退出攻击状态 颜色变回去
owner.GetComponent<SpriteRenderer>().material.color = preCol;
owner = null;
targetTran = null;
}
protected override void OnUpdate(IFsm<Enemy> 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<ChasingState>(fsm);
}
}
}
}
-
ChasingState
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using GameFramework.Fsm;
namespace ShadowU
{
public class ChasingState : FsmState<Enemy>
{
private Enemy owner;
private Transform targetTran;
private float speed; //速度
protected override void OnDestroy(IFsm<Enemy> fsm)
{
base.OnDestroy(fsm);
}
protected override void OnEnter(IFsm<Enemy> fsm)
{
base.OnEnter(fsm);
owner = fsm.Owner;
targetTran = owner.targetTran;
speed = owner.enemyData.Speed;
}
protected override void OnInit(IFsm<Enemy> fsm)
{
base.OnInit(fsm);
}
protected override void OnLeave(IFsm<Enemy> fsm, bool isShutdown)
{
base.OnLeave(fsm, isShutdown);
owner = null;
targetTran = null;
}
protected override void OnUpdate(IFsm<Enemy> 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<ReturningState>(fsm);
return;
}
//如果进入了攻击范围则进入攻击状态
if (owner.IsInAttackRange())
{
ChangeState<AttackingState>(fsm);
}
}
}
}
-
ReturningState
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using GameFramework.Fsm;
namespace ShadowU
{
public class ReturningState : FsmState<Enemy>
{
private Enemy owner;
private float speed;
private float timer; //敌人返回两秒后才会检测玩家是否进入发现范围,否则如果玩家一直在发现范围内Enemy会卡在追逐范围边缘。
protected override void OnDestroy(IFsm<Enemy> fsm)
{
base.OnDestroy(fsm);
}
protected override void OnEnter(IFsm<Enemy> fsm)
{
base.OnEnter(fsm);
owner = fsm.Owner;
speed = owner.enemyData.Speed;
timer = 2.0f;
}
protected override void OnInit(IFsm<Enemy> fsm)
{
base.OnInit(fsm);
}
protected override void OnLeave(IFsm<Enemy> fsm, bool isShutdown)
{
base.OnLeave(fsm, isShutdown);
owner = null;
}
protected override void OnUpdate(IFsm<Enemy> 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<ChasingState>(fsm);
return;
}
}
//Enemy进入了巡逻范围 则转到巡视状态
if (owner.IsInIdleRange())
{
ChangeState<IdleState>(fsm);
}
}
}
}
2、Enemy类
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<Enemy> 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<FsmState<Enemy>> states = new List<FsmState<Enemy>>(){ new IdleState() ,new ChasingState(),new ReturningState(),new AttackingState()};
//创建状态机
fsm = GameEntry.Fsm.CreateFsm<Enemy>("EnemyFsm",this,states);
//启动状态机
fsm.Start<IdleState>();
}
protected override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
base.OnUpdate(elapseSeconds, realElapseSeconds);
}
/// <summary>
/// 是否已经发现了玩家(检测玩家是否在Alarm范围内)
/// </summary>
/// <returns></returns>
public bool IsPlayerFound()
{
return Vector3.Distance(transform.position,targetTran.position)<enemyData.AlarmRange;
}
/// <summary>
/// 玩家是否处于Enemy的攻击范围
/// </summary>
/// <returns></returns>
public bool IsInAttackRange()
{
return Vector3.Distance(transform.position, targetTran.position) < enemyData.AttackRange;
}
/// <summary>
/// Enemy是否超出了追逐范围
/// </summary>
/// <returns></returns>
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;
}
}
}
五、测试运行
看看效果!
搞定!
相关文章
- 【洛谷1640】[SCOI2010]连续攻击游戏
- cocos2d-x 3.0游戏实例学习笔记 《跑酷》 第六步--金币&岩石加入而且管理
- Unity 2D游戏开发教程之2D游戏的运行效果
- 接入正式广告之后游戏黑屏无法进入该如何解决?
- 游戏启动后提示安装HMS Core,点击取消,未再次提示安装HMS Core(初始化失败返回907135003)
- 华为游戏初始化init失败,返回错误码907135000
- 《游戏大师Chris Crawford谈互动叙事》一6.4 本章小结
- 《Python游戏编程快速上手》一2.3 在变量中存储值
- 《游戏编程模式》一1.5 寻求平衡
- 《Unity 3.x游戏开发实例》——1.6节先走后跑(或双脚跳)
- ChatGPT 深度解析您真正需要知道的一切(特点和游戏开发中的用途)
- Unity 游戏实例开发集合 之 FlappyBird (像素鸟) 休闲小游戏快速实现
- [CareerCup] 17.5 Game of Master Mind 猜字游戏
- 从0开发游戏引擎之碰撞检测模块底层实现