unity中状态机控制人物

用接口,抽象出每个类的共同点

状态机的基本属性

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);
    }
    

}

详细看

[Unity] 平台游戏控制器 教程 Ep.02 基础状态机_哔哩哔哩_bilibili

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值