FSM有限狀態機架構
阿新 • • 發佈:2019-02-06
首先我們應該定義一個狀態基類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);
}