拥挤都市 中人物状态机
程序员文章站
2022-03-23 11:36:13
转变ID:enum CharacterTransitionID{ NullTransition, Init, Start, CanEatObj, NotEatObj, InAttackArea, AttackTimeOut, AttackGoalDeath, InRunAwayArea, AwayEscape, AwayCanEat, AwayGoalDeath, BeEaten, Reviv...
转变ID:
enum CharacterTransitionID
{
NullTransition,
Init,
Start,
CanEatObj,
NotEatObj,
InAttackArea,
AttackTimeOut,
AttackGoalDeath,
InRunAwayArea,
AwayEscape,
AwayCanEat,
AwayGoalDeath,
BeEaten,
Revival
}
状态ID:
enum CharacterStateID
{
NullState,
Patrol,
EatObj,
Attack,
RunAway,
Death,
Stay
}
FSMState:状态
map:<转变id,状态id>
stateID:当前状态的id
addTransition(转变id,状态id);添加一组转变id和状态id
deleteTransition(trans: number):删除一组转变id和状态id
getOutputState(trans: number):通过一个转变id,获得一个状态id
public abstract doBeforeEntering();进入动作之前
public abstract doBeforeLeaving();动作离开以后
public abstract reason();确定下一个状态
public abstract act();行动
状态实例
巡逻,停留,死亡,逃跑,攻击,吃
//巡逻状态
class PatrolState extends FSMState
{
private aiController: AICharacterController;
private transform: Transform3D;
private patrolPoint: Vector3;
private currentPatrolTime: number;
private patrolTime: number;
constructor(aiController: AICharacterController)
{
super();
this.stateID = CharacterStateID.Patrol;
this.aiController = aiController;
this.transform = this.aiController.obj.transform;
}
public doBeforeEntering()
{
this.setPatrolPoint();
this.patrolTime = Maths.randomFloat(CharacterConfig.aiPatrolTime[0], CharacterConfig.aiPatrolTime[1]);
this.currentPatrolTime = 0;
}
public doBeforeLeaving()
{
}
public reason()
{
if (this.aiController.isDeath)
{
this.aiController.setTransition(CharacterTransitionID.BeEaten);
return;
}
if (this.aiController.aiType == AIType.Bottom)
{
return;
}
let targetCharacter: CharacterController[] = this.aiController.getTargetCharacter();
if (targetCharacter[0] != null)
{
// Debug.log("patrol runaway:", targetCharacter[0].name);
this.aiController.runAwayCharacter = targetCharacter[0];
this.aiController.setTransition(CharacterTransitionID.InRunAwayArea);
return;
}
if (this.aiController.aiType == AIType.Lower)
{
return;
}
if (this.aiController.aiType == AIType.Senior || this.aiController.aiType == AIType.Middle)
{
if (targetCharacter[1] != null)
{
this.aiController.attackCharacter = targetCharacter[1];
this.aiController.setTransition(CharacterTransitionID.InAttackArea);
return;
}
}
let eatPeople: PeopleController = this.aiController.getInEatAreaPeople();
if (eatPeople != null && !eatPeople.hasFollow)
{
this.aiController.setTransition(CharacterTransitionID.CanEatObj);
}
}
public act()
{
if (this.getPatrolPointDistanceSquared() <= 0.3)
{
this.currentPatrolTime = 0;
this.setPatrolPoint();
}
if (this.currentPatrolTime >= this.patrolTime)
{
this.currentPatrolTime = 0;
this.setPatrolPoint();
}
else
{
this.currentPatrolTime += CharacterConfig.aiActionTimestep;
}
this.aiController.actionTargetPoint = this.patrolPoint;
}
private setPatrolPoint()
{
this.patrolPoint = ActiveAreaManager.instance.getCenterSpawnAndRoutePoints()[0];
// Gizmos.drawXYZ(this.patrolPoint);
}
private getPatrolPointDistanceSquared()
{
return Vector3.distanceSquared(this.transform.position, this.patrolPoint);
}
}
class EatObjState extends FSMState
{
private aiController: AICharacterController;
private transform: Transform3D;
private eatPeople: PeopleController;
private currentEatTime: number;
constructor(aiController: AICharacterController)
{
super();
this.stateID = CharacterStateID.EatObj;
this.aiController = aiController;
this.transform = this.aiController.obj.transform;
}
public doBeforeEntering()
{
this.eatPeople = this.aiController.getInEatAreaPeople();
// Debug.log(this.aiController.name, " enter eat obj:", this.eatPeople.peopleId);
this.currentEatTime = 0;
}
public doBeforeLeaving()
{
}
public reason()
{
if (this.aiController.isDeath)
{
this.aiController.setTransition(CharacterTransitionID.BeEaten);
return;
}
let targetCharacter: CharacterController[] = this.aiController.getTargetCharacter();
if (targetCharacter[0] != null)
{
this.aiController.runAwayCharacter = targetCharacter[0];
this.aiController.setTransition(CharacterTransitionID.InRunAwayArea);
return;
}
if (targetCharacter[1] != null)
{
this.aiController.attackCharacter = targetCharacter[1];
this.aiController.setTransition(CharacterTransitionID.InAttackArea);
return;
}
if (this.eatPeople == null)
{
this.aiController.setTransition(CharacterTransitionID.NotEatObj);
}
}
public act()
{
if (this.eatPeople != null && this.eatPeople.hasFollow)
{
this.eatPeople = this.aiController.getInEatAreaPeople();
// Debug.log("eat other:", this.eatPeople.peopleId);
}
if (this.eatPeople != null)
{
this.aiController.actionTargetPoint = this.eatPeople.obj.transform.position;
}
}
}
class AttackState extends FSMState
{
private aiController: AICharacterController;
private transform: Transform3D;
private attackCharacter: CharacterController;
private attackTime: number;
private attackDiffTime: number = 2;
private currentAttackDiffTime: number;
private targetPos: Vector3;
private isAttack: boolean;
constructor(aiController: AICharacterController)
{
super();
this.stateID = CharacterStateID.Attack;
this.aiController = aiController;
this.transform = this.aiController.obj.transform;
}
public doBeforeEntering()
{
this.attackCharacter = this.aiController.attackCharacter;
this.attackTime = 0;
this.currentAttackDiffTime = 0;
this.isAttack = true;
this.targetPos = this.attackCharacter.transform.position;
}
public doBeforeLeaving()
{
}
public reason()
{
if (this.aiController.isDeath)
{
this.aiController.setTransition(CharacterTransitionID.BeEaten);
return;
}
if (this.attackCharacter.isDeath)
{
this.aiController.setTransition(CharacterTransitionID.AttackGoalDeath);
return;
}
if (this.attackTime >= CharacterConfig.aiAttackTime && this.currentAttackDiffTime >= this.attackDiffTime)
{
this.aiController.setTransition(CharacterTransitionID.AttackTimeOut);
return;
}
}
public act()
{
this.attackTime += CharacterConfig.aiActionTimestep;
if (this.attackTime >= CharacterConfig.aiAttackTime)
{
if (this.isAttack)
{
if (Maths.randomBoolean())
{
this.attackTime = 0;
this.currentAttackDiffTime = 0;
this.isAttack = true;
this.targetPos = this.attackCharacter.transform.position;
}
else
{
if (this.aiController.getInEatAreaPeople() == null)
{
this.targetPos = new Vector3(-this.attackCharacter.transform.position.x, 0, -this.attackCharacter.transform.position.z);
}
else
{
this.targetPos = this.aiController.getInEatAreaPeople().obj.transform.position;
this.isAttack = false;
}
}
}
else
{
this.currentAttackDiffTime += CharacterConfig.aiActionTimestep;
}
}
else
{
this.targetPos = this.attackCharacter.transform.position;
}
this.aiController.actionTargetPoint = this.targetPos;
}
}
class RunAwayState extends FSMState
{
private aiController: AICharacterController;
private transform: Transform3D;
private runAwayCharacter: CharacterController;
private currentRunAwayTime: number;
private awayPoint: Vector3;
private isRandomRunAway: boolean = false;
private tempV: Vector3;
constructor(aiController: AICharacterController)
{
super();
this.stateID = CharacterStateID.RunAway;
this.aiController = aiController;
this.transform = this.aiController.obj.transform;
this.awayPoint = Vector3.ZERO.clone();
this.tempV = Vector3.ZERO.clone();
}
public doBeforeEntering()
{
// Debug.log(this.aiController.name, " enter run away");
this.runAwayCharacter = this.aiController.runAwayCharacter;
this.currentRunAwayTime = 0;
this.isRandomRunAway = false;
// Gizmos.drawXYZ(this.awayPoint, 3);
}
public doBeforeLeaving()
{
}
public reason()
{
if (this.aiController.isDeath)
{
this.aiController.setTransition(CharacterTransitionID.BeEaten);
return;
}
if (this.runAwayCharacter == null || this.runAwayCharacter.isDeath)
{
this.aiController.setTransition(CharacterTransitionID.AwayGoalDeath);
return;
}
let targetCharacter: CharacterController[] = this.aiController.getTargetCharacter();
if (targetCharacter[1] != null && targetCharacter[1] == this.runAwayCharacter)
{
this.aiController.attackCharacter = targetCharacter[1];
this.aiController.setTransition(CharacterTransitionID.AwayCanEat);
return;
}
if (this.canNotRunAway())
{
this.aiController.setTransition(CharacterTransitionID.AwayEscape);
return;
}
}
public act()
{
let targetCharacter: CharacterController[] = this.aiController.getTargetCharacter();
if (targetCharacter[0] != null && targetCharacter[0] != this.runAwayCharacter)
{
this.runAwayCharacter = targetCharacter[0];
this.currentRunAwayTime = 0;
}
if (this.currentRunAwayTime > CharacterConfig.aiRunAwayTime)
{
this.awayPoint.x = Maths.randomFloat(-1, 1);
this.awayPoint.z = Maths.randomFloat(-1, 1);
Vector3.normalize(this.awayPoint, this.awayPoint);
this.currentRunAwayTime = 0;
if (!this.isRandomRunAway)
{
this.isRandomRunAway = true;
}
}
else
{
this.currentRunAwayTime += CharacterConfig.aiActionTimestep;
if (!this.isRandomRunAway)
{
Vector3.subtract(this.aiController.transform.position, this.runAwayCharacter.transform.position, this.awayPoint);
Vector3.normalize(this.awayPoint, this.awayPoint);
}
}
// Debug.log(this.awayPoint);
Vector3.add(this.awayPoint, this.aiController.transform.position, this.tempV);
this.aiController.actionTargetPoint = this.tempV;
}
private canNotRunAway()
{
if (this.runAwayCharacter == null)
{
Debug.log("not runAwayCharacter");
return true;
}
if (Vector3.distanceSquared(this.aiController.transform.position, this.runAwayCharacter.transform.position) >= CharacterConfig.aiNotRunAwayDis * CharacterConfig.aiNotRunAwayDis)
{
// Debug.log("runaway patrol:", Vector3.distanceSquared(this.aiController.getXZPos(), this.runAwayCharacter.getXZPos()));
return true;
}
return false;
}
}
class DeathState extends FSMState
{
private aiController: AICharacterController;
constructor(aiController: AICharacterController)
{
super();
this.stateID = CharacterStateID.Death;
this.aiController = aiController;
}
public doBeforeEntering()
{
// Debug.log(this.aiController.name, " enter death");
}
public doBeforeLeaving()
{
}
public reason()
{
if (!this.aiController.isDeath)
{
this.aiController.setTransition(CharacterTransitionID.Revival);
}
}
public act()
{
}
}
class StayState extends FSMState
{
private aiController: AICharacterController;
constructor(aiController: AICharacterController)
{
super();
this.stateID = CharacterStateID.Stay;
this.aiController = aiController;
}
public doBeforeEntering()
{
Debug.log(this.aiController.name, " enter stay");
}
public doBeforeLeaving()
{
}
public reason()
{
if (this.aiController.getIsStart())
{
this.aiController.setTransition(CharacterTransitionID.Start);
}
}
public act()
{
}
}
FSMSystem:状态机
states:存储所有状态
_currentStateID:当前状态id
addState(s: FSMState):添加状态
deleteState(id: number):删除状态
performTransition(trans: number):修改当前的状态为这个转变id对应的状态
AICharacterController:角色控制类
//注册状态机
private registerFSM()
{
this.fsm = new FSMSystem();
let patrolState: PatrolState = new PatrolState(this);
patrolState.addTransition(CharacterTransitionID.InAttackArea, CharacterStateID.Attack);
patrolState.addTransition(CharacterTransitionID.InRunAwayArea, CharacterStateID.RunAway);
patrolState.addTransition(CharacterTransitionID.CanEatObj, CharacterStateID.EatObj);
patrolState.addTransition(CharacterTransitionID.BeEaten, CharacterStateID.Death);
patrolState.addTransition(CharacterTransitionID.Init, CharacterStateID.Stay);
let eatObjState: EatObjState = new EatObjState(this);
eatObjState.addTransition(CharacterTransitionID.InAttackArea, CharacterStateID.Attack);
eatObjState.addTransition(CharacterTransitionID.InRunAwayArea, CharacterStateID.RunAway);
eatObjState.addTransition(CharacterTransitionID.BeEaten, CharacterStateID.Death);
eatObjState.addTransition(CharacterTransitionID.NotEatObj, CharacterStateID.Patrol);
eatObjState.addTransition(CharacterTransitionID.Init, CharacterStateID.Stay);
let attackState: AttackState = new AttackState(this);
attackState.addTransition(CharacterTransitionID.AttackGoalDeath, CharacterStateID.Patrol);
attackState.addTransition(CharacterTransitionID.AttackTimeOut, CharacterStateID.Patrol);
attackState.addTransition(CharacterTransitionID.BeEaten, CharacterStateID.Death);
attackState.addTransition(CharacterTransitionID.Init, CharacterStateID.Stay);
let runAwayState: RunAwayState = new RunAwayState(this);
runAwayState.addTransition(CharacterTransitionID.AwayEscape, CharacterStateID.Patrol);
runAwayState.addTransition(CharacterTransitionID.AwayCanEat, CharacterStateID.Attack);
runAwayState.addTransition(CharacterTransitionID.BeEaten, CharacterStateID.Death);
runAwayState.addTransition(CharacterTransitionID.AwayGoalDeath, CharacterStateID.Patrol);
runAwayState.addTransition(CharacterTransitionID.Init, CharacterStateID.Stay);
let deathState: DeathState = new DeathState(this);
deathState.addTransition(CharacterTransitionID.Init, CharacterStateID.Stay);
deathState.addTransition(CharacterTransitionID.Revival, CharacterStateID.Patrol);
let stayState: StayState = new StayState(this);
stayState.addTransition(CharacterTransitionID.Start, CharacterStateID.Patrol);
this.fsm.addState(stayState);
this.fsm.addState(patrolState);
this.fsm.addState(eatObjState);
this.fsm.addState(attackState);
this.fsm.addState(runAwayState);
this.fsm.addState(deathState);
}
自己内部更新
public update()
{
super.update();
if (!this.isStart)
{
return;
}
this.currentActionTime += Laya.timer.delta / 1000;
if (this.currentActionTime >= CharacterConfig.aiActionTimestep)
{
if (this.fsm != null)
{
this.fsm.currentState.reason();
this.fsm.currentState.act();
}
this.currentActionTime = 0;
}
this.moveCharacter();
}
//移动角色
public moveCharacter()
{
if (!this.isStart || this.isDeath)
{
return;
}
if (this._actionTargetPoint == null)
{
return;
}
this.tempV.x = this._transform.position.x;
this.tempV.y = 0;
this.tempV.z = this._transform.position.z;
Vector3.subtract(this._actionTargetPoint, this.tempV, this.tempV);
Vector3.normalize(this.tempV, this.tempV);
this.rotateForward = this.tempV;
this.move();
this.rotate();
}
设置转变id
```public setTransition(t: number)
{
this.fsm.performTransition(t);
}
本文地址:https://blog.csdn.net/HzjCsdn/article/details/107348912
上一篇: vue中在父组件点击按钮触发子组件的事件
下一篇: STL
推荐阅读