本Demo基础来自于:
【阿严】[Unity]平台跳跃游戏 角色控制器 教程合集 | 状态机架构 | Platformer Controller Tutorial Collection_哔哩哔哩_bilibili
阿严的Github地址:https://github.com/AtCloudStudio/PlatformerControllerTutorial
我的实现:通过网盘分享的文件:3D下的2D控制器精髓.7z
链接: https://pan.baidu.com/s/1B-XHXM0a9VzVTrJbL3uctw?pwd=1234 提取码: 1234本Demo的重点:
目录
思维导图
状态机及玩家基本组件部分
玩家具体状态实现
视频介绍:
Unity 一个3D下的2D平台跳跃控制器
因为实现起来还是比较简单无痛的 所以具体请查看源码 我只是将代码放在这里 并不会做更多的解释
1.新输入系统
using System.Collections;
using Unity.VisualScripting;
using UnityEngine;
public class PlayerInput : MonoBehaviour
{
private InputSystem_Actions inputActions;
[SerializeField, InspectorLabel("跳跃缓冲自动无效时间")] private float jumpInputBufferTime = 0.5f;
public Vector2 Axis => inputActions.Player.Move.ReadValue<Vector2>();
public float MoveX_input => Axis.x;
public bool isMove_input => MoveX_input != 0;
public bool isJump_input => inputActions.Player.Jump.WasPerformedThisFrame();
public bool StopJump_input => inputActions.Player.Jump.WasReleasedThisFrame();
//土狼时间
public bool hasJumpInputBuffer { get; set; }
private void Awake()
{
inputActions = new InputSystem_Actions();
}
public void EnableInputSystem()
{
inputActions.Player.Enable();
Cursor.lockState = CursorLockMode.Locked;
// 注册 当玩家松开跳跃键时,就会触发canceled这个回调
inputActions.Player.Jump.canceled += (de) =>
{
hasJumpInputBuffer = false;
};
}
void OnGUI()
{
Rect rect = new Rect(200, 200, 200, 200);
string message = "Has Jump Input Buffer: " + hasJumpInputBuffer;
GUIStyle style = new GUIStyle();
style.fontSize = 20;
style.fontStyle = FontStyle.Bold;
GUI.Label(rect, message, style);
}
public void ResetJumpBuffer()
{
StopCoroutine(DoRestJumpBuffer());
StartCoroutine(DoRestJumpBuffer());
}
public IEnumerator DoRestJumpBuffer()
{
hasJumpInputBuffer = true;
yield return new WaitForSeconds(jumpInputBufferTime);
hasJumpInputBuffer = false;
}
public void DisableInputSystem()
{
inputActions.Player.Disable();
}
}
2. FSM的so文件化
using UnityEngine;
public class PlayerState : ScriptableObject, IState
{
protected Animator animator;
protected PlayerInput playerInput;
protected PlyaerStateMachine playerStateMachine;
protected PlayerController playerController;
#region 动画参数
[SerializeField] private string animationClipName; //动画名称
[SerializeField, Range(0, 1f)] private float crossFadeTime = 0.1f;
private int stateHash;
protected float startTime;
protected float StateDurtion => Time.time - startTime;
protected bool IsAnimationFinished => StateDurtion >= animator.GetCurrentAnimatorStateInfo(0).length;
#endregion
#region 继承参数
protected float currentSpeed;
#endregion
/// <summary>
/// 初始化
/// </summary>
/// <param name="stateMachine">状态机对象</param>
/// <param name="animator">动画组件</param>
/// <param name="playerInput">玩家输入类对象</param>
/// <param name="playerController">玩家控制类对象</param>
public void Init(PlyaerStateMachine stateMachine, Animator animator, PlayerInput playerInput,
PlayerController playerController)
{
this.animator = animator;
this.playerStateMachine = stateMachine;
this.playerInput = playerInput;
this.playerController = playerController;
}
private void OnEnable()
{
stateHash = Animator.StringToHash(animationClipName);
}
public virtual void Enter()
{
animator.CrossFadeInFixedTime(animationClipName, crossFadeTime);
startTime = Time.time;
}
public virtual void Exit() { }
public virtual void FixedUpdate() { }
public virtual void Update() { }
}
3.土狼时间
using UnityEngine;
[CreateAssetMenu(menuName = "Player/States/Player_CoyoteTime")]
public class Player_CoyoteTime : PlayerState
{
public float runSpeed = 5f;
public float coyoteTime = 0.2f;
public override void Enter()
{
base.Enter();
playerController.SetUesGravity(false);
}
public override void Update()
{
//土狼时间:让玩家在空中悬浮一小段时间 还是可以跳跃
if (playerInput.isJump_input)
{
playerStateMachine.ChangeState(typeof(Player_JumpUp));
}
//只有在超过过土狼时间以后 或者不输入以后才会下落
if (StateDurtion >= coyoteTime || !playerInput.isMove_input)
{
playerStateMachine.ChangeState(typeof(Player_JumpFall));
}
}
public override void FixedUpdate()
{
//currentSpeed同步减速
playerController.Move2Clip(runSpeed);
}
public override void Exit()
{
playerController.SetUesGravity(true);
}
}
4.输入预处理(跳跃)
在PlayerInput里
public void ResetJumpBuffer()
{
StopCoroutine(DoRestJumpBuffer());
StartCoroutine(DoRestJumpBuffer());
}
public IEnumerator DoRestJumpBuffer()
{
hasJumpInputBuffer = true;
yield return new WaitForSeconds(jumpInputBufferTime);
hasJumpInputBuffer = false;
}