欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

拥挤都市 中人物状态机

程序员文章站 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