当前位置: 移动技术网 > IT编程>开发语言>JavaScript > 拥挤都市 中人物状态机

拥挤都市 中人物状态机

2020年07月15日  | 移动技术网IT编程  | 我要评论

转变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

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网