1. 程式人生 > >FSM有限狀態機架構

FSM有限狀態機架構

      首先我們應該定義一個狀態基類FSMBase,裡面有一些動畫狀態的基本行為,比如動畫狀態的進入,持續,離開等,然後因為人物並不是只有一個狀態,所以我們應該建立一個狀態管理器來管理狀態,裡面應該定義一些方法,比如我們應該定義一些狀態的共同邏輯,比如對狀態進行儲存,新增狀態AddState,狀態間跳轉的方法ChangeState(進入當前的動畫狀態時,應該離開上一個動畫狀態,從而保證每一幀處理的都是當前狀態的邏輯)。      然後抽象出相應的狀態類,即每一個狀態都是一個類,如果增添狀態,則需要再次定義相應的狀態類並繼承,每一個狀態類都只寫與自身相關的邏輯。     檔具體的呼叫時,我們可以通過列舉,將狀態新增到狀態管理類裡,然後如果需要跳轉,就應該在狀態類裡設定一個代理,將需要改變的狀態傳回至FSMManager管理類的ChangeState方法裡,進行跳轉,而不是直接進行跳轉,這樣就大大減少了各個狀態之間的耦合性和狀態的擴充套件性。

程式碼如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public abstract class FSMBase
{
    public Animator animator;
    public abstract void OnEnter();
    public abstract void OnLeave();
    public abstract void UpDate();

}

public class FSMManager
{
    FSMBase[] allState;
    byte curState;
    short stateIndex;
    public FSMManager (byte count)
    {
        allState = new FSMBase[count];
        curState = 0;
        stateIndex = -1;
    }
    /// <summary>
    /// 新增
    /// </summary>
    /// <param name="tmpBase">Tmp base.</param>
    public void AddState(FSMBase tmpBase)
    {
        if (curState<allState.Length)
        {
            allState[curState] = tmpBase;
            curState++;
        }
    }
    /// <summary>
    /// 狀態跳轉
    /// </summary>
    /// <param name="index">Index.</param>
    public void ChangeState(byte index)
    {
        if (stateIndex!=-1)
        {
            allState[curState].OnLeave();
        }
        stateIndex = curState;
        allState[index].OnEnter();
        curState = index;
    }

    public void Update()
    {
        if (stateIndex!=-1)
        {
            allState[curState].UpDate();
        }
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public enum AnimalState
{
    Idle,
    Attack,
    Walk,
    Jump,
    Max

}
public class PlayerController : MonoBehaviour {

    FSMManager fsmManger;
    private void Start()
    {
        fsmManger = new FSMManager((byte)AnimalState.Max);
        Animator ani = GetComponent<Animator>();
        PlayerIdle tmpIdle = new PlayerIdle(ani);
        fsmManger.AddState(tmpIdle);
        PlayerAttack tmpAttack = new PlayerAttack(ani);
        fsmManger.AddState(tmpAttack);
        PlayerWalk tmpWalk= new PlayerWalk(ani);
        fsmManger.AddState(tmpWalk);
        PlayerJump tmpJump = new PlayerJump(ani);
        fsmManger.AddState(tmpJump);
    }
    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.A))
        {
            fsmManger.ChangeState((byte)AnimalState.Walk);
        }
    }
}

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public delegate void ChangeDelegate(byte index);

public class PlayerIdle : FSMBase
{
    public PlayerIdle(Animator tmpAnimal)
    {
        this.animator = tmpAnimal;
    }
    public override void OnEnter()
    {
        this.animator.SetInteger("StateIndex",1);
    }

    public override void OnLeave()
    {
        
    }

    public override void UpDate()
    {
        
    }
}

public class PlayerAttack : FSMBase
{
	public PlayerAttack(Animator tmpAnimator)
	{
		this.animator = tmpAnimator;
	}
	public override void OnEnter()
	{
		this.animator.SetInteger("StateIndex", 2);
	}

	public override void OnLeave()
	{

	}

	public override void UpDate()
	{

	}
}
public class PlayerWalk : FSMBase
{
    public PlayerWalk(Animator tmpAnimator)
    {
        this.animator = tmpAnimator;
    }
    public override void OnEnter()
    {
        this.animator.SetInteger("StateIndex",3);
    }

    public override void OnLeave()
    {
        
    }

    public override void UpDate()
    {
        
    }
}

public class Jump : FSMBase
{
    ChangeDelegate changeIdle;
	public Skill1(Animator tmpAnimal,ChangeDelegate tmpDlegate)
	{
		this.animator = tmpAnimal;
        changeIdle = tmpDlegate;
	}
	float timer = 0;
	bool isPlayFinish;
	public override void OnEnter()
	{
		this.animator.SetInteger("StateIndex", 4);
		timer = 0;
		isPlayFinish = false;
	}

	public override void OnLeave()
	{
		timer = 0;
		isPlayFinish = false;
	}

	public override void UpDate()
	{
		timer += Time.deltaTime;
		if (timer > 0.28f && !isPlayFinish)
		{
			isPlayFinish = true;

			//do samething
		}
		if (timer > 1)
		{
			timer = 0;
			isPlayFinish = false;
			changeIdle((byte)AnimalStateSkill.Idle);
		}
	}
}

public void ChangeState(byte index)
	{
		fsmManger.ChangeState(index);
	}