用接口,抽象出每个类的共同点
状态机的基本属性
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public interface IState//interface 状态机接口
{
void Enter();//进入时
void Exit();//退出时
void LogicUpdata();//根据电脑帧率来运行 一般用于接受按键
void PhysicUpdata();//物理更新 一般用于计算物体运动
}
管理状态机切换
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//用于管理状态机切换
public class StateMachina : MonoBehaviour
{
IState currentState;
protected Dictionary<System.Type, IState> stateTable;// 类名 对应 文件
private void Update()
{
currentState.LogicUpdata();
}
private void FixedUpdate()
{
currentState.PhysicUpdata();
}
//切换状态 初始化的状态 1
protected void SwitchOn(IState newState)
{
currentState = newState;
currentState.Enter();
}
//切换状态 可以被访问 2
public void SwitchState(IState newState)
{
currentState.Exit();
SwitchOn(newState);
}
//切换状态 可以被访问 3 用于字典 多个对象时
public void SwitchState(System.Type newStateType)
{
SwitchState(stateTable[newStateType]);//stateTable[newStateType] 返回的是 IState 的值 用的切换状态2
}
}
人物的状态
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerState : ScriptableObject, IState
{
protected Animator animator;
protected PlayerStateMachine stateMachine;
protected PlayerInput input;
protected PlayerController player;
protected float currentSpeed;//记录当前速度
[SerializeField,Range(0,1)] float transitionDuration = 0.1f;//动画转换时间
[SerializeField] string stateName;
int stateHash;
public float stateStartTime;
protected bool isAnimatorFinished => stateDuration>animator.GetCurrentAnimatorStateInfo(0).length;//GetCurrentAnimatorStateInfo(0).length动画第0层的时间长度
protected float stateDuration => Time.time-stateStartTime;//动画持续时间
private void OnEnable()
{
stateHash=Animator.StringToHash(stateName);
}
//初始化状态 避免报空
public void Initialize(Animator animator,PlayerStateMachine stateMachine,PlayerInput input,PlayerController player)
{
this.animator = animator;
this.input= input;
this.stateMachine=stateMachine;
this.player=player;
}
public virtual void Enter()
{
animator.CrossFade(stateHash, transitionDuration);//切换动画 animator.CrossFade stateHash状态哈希值 transitionDuration切换的时间
stateStartTime=Time.time; //Time.time游戏运行的时间
}
public virtual void Exit()
{
}
public virtual void LogicUpdata()
{
}
public virtual void PhysicUpdata()
{
}
}
控制人物切换状态
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerStateMachine : StateMachina
{
[SerializeField] PlayerState[] states;
Animator animator;
PlayerInput input;
PlayerController player;
private void Awake()
{
animator = GetComponentInChildren<Animator>();//获得子物体的animator的组件
stateTable=new Dictionary<System.Type, IState>(states.Length);// 初始化字典 (states.Length)初始化的个数
input=GetComponent<PlayerInput>();
player=GetComponent<PlayerController>();
foreach (var state in states)
{
state.Initialize(animator, this,input,player);
stateTable.Add(state.GetType(), state);//state.GetType() 字典键 state字典值 字典用键来访问值
}
}
private void Start()
{
SwitchOn(stateTable[typeof(PlayerStateIdle)]);
}
}
用inputSystem来控制人物
玩家输入
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerInput : MonoBehaviour
{
[SerializeField] float jumpInputBufferTime = 0.5f;//跳跃缓冲时间
WaitForSeconds waitJumpInputBufferTime;
PlayerInputAction playInputAction;
Vector2 axes=>playInputAction.GamePlay.Axex.ReadValue<Vector2>();//取得按键时的值 值为 -1,0,1
public bool isMove => axisX!=0;
public bool isJump => playInputAction.GamePlay.Jump.WasPressedThisFrame();//WasPressedThisFrame 按下空格时返回true
public bool isStopJump=> playInputAction.GamePlay.Jump.WasReleasedThisFrame();//WasReleasedThisFrame 松开按键时
public bool isDash => playInputAction.GamePlay.Dash.WasPressedThisFrame();//WasPressedThisFrame 按下空格时返回true
public bool isHasJumpInputBuffer { get; set; }//跳跃缓冲
public float axisX=>axes.x;//a 为-1 d 为 1
private void Awake()
{
playInputAction= new PlayerInputAction();
waitJumpInputBufferTime=new WaitForSeconds(jumpInputBufferTime);
}
private void OnEnable()
{
playInputAction.GamePlay.Jump.canceled+=delegate//增加委托用法 每一个状态下都会运行这个事件 canceled按键松开
{
isHasJumpInputBuffer=false;
};
}
//启用玩家动作表
public void EnableGamePlayInput()
{
playInputAction.GamePlay.Enable();// 启用玩家动作表
Cursor.lockState = CursorLockMode.Locked;//光标锁定
}
//禁用玩家动作表
public void DisableGamePlayInput()
{
playInputAction.GamePlay.Disable();
}
//private void OnGUI()//可以在游戏画面显示
//{
// Rect rect=new Rect(200,200,200,200);//显示方框的各个位置
// string message = "Has jump Input Buffer "+isHasJumpInputBuffer;
// GUIStyle style = new GUIStyle();
// style.fontSize=20;
// style.fontStyle= FontStyle.Bold;// 字体为黑体
// GUI.Label(rect, message, style);
//}
//跳跃缓冲
public void SetJumpBufferTiem()
{
StopCoroutine(nameof(JumpInputButterCoroutine));//避免多个协程开始
StartCoroutine(nameof(JumpInputButterCoroutine));
}
//跳跃缓冲协程
IEnumerator JumpInputButterCoroutine()
{
isHasJumpInputBuffer=true;
yield return waitJumpInputBufferTime;
isHasJumpInputBuffer= false;
}
}
控制角色移动
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
[SerializeField] VoidEnentChannel leveCrearEnentChanel;
public AudioSource voicePlayer{ set; get; }
Animator animator;
PlayerInput input;
public Rigidbody rigidBody;
PlayerGroundDetector groundDetector;
public float moveSpeed => Mathf.Abs(rigidBody.velocity.x);//Mathf.Abs 取绝对值
public bool isGround => groundDetector.IsGround;
public bool isFall => rigidBody.velocity.y<0f&&!isGround;
public bool isIdle=>rigidBody.velocity.x<0.1f&&isGround;
public bool isAir=> rigidBody.velocity.y>0f&&!isGround;
public bool isAirJump { get; set; }//可以被访问和更改
public bool isVictory { get; set; }
private void Awake()
{
//获得实列 要不然调用就是调用全部的脚本
animator = GetComponentInChildren<Animator>();
input = GetComponent<PlayerInput>();
rigidBody=GetComponent<Rigidbody>();
groundDetector=GetComponentInChildren<PlayerGroundDetector>();
voicePlayer=GetComponentInChildren<AudioSource>();
}
private void OnEnable()
{
leveCrearEnentChanel.AddListener(OnLevelCleared);
}
private void OnDisable()
{
leveCrearEnentChanel.RemoveListener(OnLevelCleared);
}
private void Start()
{
input.EnableGamePlayInput();
}
//玩家移动 翻转
public void Move(float speed)
{
if(input.isMove)
{
transform.localScale=new Vector3(input.axisX, 1f, 1f);//玩家翻转
}
SetVelocityX(speed*input.axisX);
}
//改变物体位置
public void SetVelocity(Vector3 velocity)
{
rigidBody.velocity = velocity;
}
//改变物体x的位置
public void SetVelocityX(float velocityX)
{
rigidBody.velocity=new Vector3(velocityX,rigidBody.velocity.y);
}
//改变物体y的位置
public void SetVelocityY(float velocityY)
{
rigidBody.velocity=new Vector3( rigidBody.velocity.x,velocityY);
}
//突然增加力量 与质量有关系
public void SetDashJupm(float jumpForce)
{
rigidBody.AddForce(Vector2.up * jumpForce, ForceMode.Impulse);
}
//设置重力
public void SetUseGravtity(bool isValue)
{
rigidBody.useGravity = isValue;
}
//委托 切换到胜利状态
public void OnLevelCleared()
{
isVictory=true;
}
//玩家死亡
public void OnDefeated()
{
input.DisableGamePlayInput();
rigidBody.velocity = Vector3.zero;
rigidBody.useGravity=false;//重力为零
rigidBody.detectCollisions= false;//碰撞体清零
GetComponent<StateMachina>().SwitchState(typeof(PlayerStateDefeated));
}
}
然后创建各个状态实例,每个状态只要考虑自己可能出现的情况,列如
我的休闲状态
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
[CreateAssetMenu(menuName ="Data/StateMachine/PlayerState/Idle",fileName ="Player_Idle")]
public class PlayerStateIdle : PlayerState
{
[SerializeField] float deceleration = 5f;
public override void Enter()
{
// animator.Play("Idle");
// player.SetVelocityX(0);
base.Enter();
currentSpeed=player.moveSpeed;
}
public override void LogicUpdata()
{
if (!player.isGround)
{
stateMachine.SwitchState(typeof(PlayerStateFall));
}
if (input.isJump)
{
stateMachine.SwitchState(typeof(PlayerStateJump));
}
//一旦移动就切换Run状态
// inputSystem的用法 按下a 或者d
// if(Keyboard.current.aKey.isPressed||Keyboard.current.dKey.isPressed)
if (input.isMove)
{
// stateMachine.SwitchState(stateMachine.runState);
stateMachine.SwitchState(typeof(PlayerStateRun));//用的切换状态3
}
currentSpeed=Mathf.MoveTowards(currentSpeed, 0f, deceleration);
}
public override void PhysicUpdata()
{
player.Move(currentSpeed);
}
}
跑步状态
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
[CreateAssetMenu(menuName = "Data/StateMachine/PlayerState/Run", fileName = "Player_Run")]
public class PlayerStateRun : PlayerState
{
[SerializeField] float runSpeed=5f;
[SerializeField] float acceration = 5f;//加速度
public override void Enter()
{
// animator.Play("Run");
base.Enter();
//player 用的是 playerController
currentSpeed=player.moveSpeed;//记录当前速度
}
public override void LogicUpdata()
{
if(!player.isGround)
{
stateMachine.SwitchState(typeof(PlayerStateCoyto));
}
if (input.isJump)
{
stateMachine.SwitchState(typeof(PlayerStateJump));
}
// 非 a或者d就运行
// if(!(Keyboard.current.aKey.isPressed||Keyboard.current.dKey.isPressed))
if (!input.isMove)
{
//切换到等待状态
stateMachine.SwitchState(typeof(PlayerStateIdle));
}
//MoveTowards 让a点变化到b点 以c的速度 MoveTowards (a,b,c)
currentSpeed=Mathf.MoveTowards(currentSpeed, runSpeed, acceration*Time.deltaTime);//有一种逐步变快的感觉
if(input.isDash)
{
stateMachine.SwitchState(typeof(PlayerStateDash));
}
}
public override void PhysicUpdata()
{
player.Move(currentSpeed);
}
}
详细看