Projext/Assets/Animation_human/SuperCharacterController/Code/PlayerMachine.cs
2026-02-20 03:14:55 +09:00

195 lines
5.9 KiB
C#

/*
* Example implementation of the SuperStateMachine and SuperCharacterController.
*/
using UnityEngine;
[RequireComponent(typeof(SuperCharacterController))]
[RequireComponent(typeof(PlayerInputController))]
public class PlayerMachine:SuperStateMachine
{
public Transform AnimatedMesh;
public float WalkSpeed = 4.0f;
public float WalkAcceleration = 30.0f;
public float JumpAcceleration = 5.0f;
public float JumpHeight = 3.0f;
public float Gravity = 25.0f;
// Add more states by comma separating them.
private enum PlayerStates { Idle, Walk, Jump, Fall }
private SuperCharacterController controller;
// Current velocity.
private Vector3 moveDirection;
// Current direction our character's art is facing.
public Vector3 lookDirection { get; private set; }
private PlayerInputController input;
private void Start()
{
input = gameObject.GetComponent<PlayerInputController>();
// Grab the controller object from our object.
controller = gameObject.GetComponent<SuperCharacterController>();
// Our character's current facing direction, planar to the ground.
lookDirection = transform.forward;
// Set our currentState to idle on startup.
currentState = PlayerStates.Idle;
}
// Put any code in here you want to run BEFORE the state's update function.
// This is run regardless of what state you're in.
protected override void EarlyGlobalSuperUpdate()
{
// Rotate out facing direction horizontally based on mouse input.
// (Taking into account that this method may be called multiple times per frame)
lookDirection = Quaternion.AngleAxis(input.Current.MouseInput.x * (controller.deltaTime / Time.deltaTime), controller.up) * lookDirection;
}
// Put any code in here you want to run AFTER the state's update function.
// This is run regardless of what state you're in.
protected override void LateGlobalSuperUpdate()
{
// Move the player by our velocity every frame.
transform.position += moveDirection * controller.deltaTime;
// Rotate our mesh to face where we are "looking".
AnimatedMesh.rotation = Quaternion.LookRotation(lookDirection, controller.up);
}
private bool AcquiringGround()
{ return controller.currentGround.IsGrounded(false, 0.01f); }
private bool MaintainingGround()
{ return controller.currentGround.IsGrounded(true, 0.5f); }
public void RotateGravity(Vector3 up)
{ lookDirection = Quaternion.FromToRotation(transform.up, up) * lookDirection; }
/// <summary>
/// Constructs a vector representing our movement local to our lookDirection, which is
/// controlled by the camera.
/// </summary>
private Vector3 LocalMovement()
{
Vector3 right = Vector3.Cross(controller.up, lookDirection);
Vector3 local = Vector3.zero;
if (input.Current.MoveInput.x != 0) { local += right * input.Current.MoveInput.x; }
if (input.Current.MoveInput.z != 0) { local += lookDirection * input.Current.MoveInput.z; }
return local.normalized;
}
// Calculate the initial velocity of a jump based off gravity and desired maximum height attained.
private float CalculateJumpSpeed(float jumpHeight, float gravity)
{ return Mathf.Sqrt(2 * jumpHeight * gravity); }
/*void Update () {
* Update is normally run once on every frame update. We won't be using it
* in this case, since the SuperCharacterController component sends a callback Update
* called SuperUpdate. SuperUpdate is recieved by the SuperStateMachine, and then fires
* further callbacks depending on the state.
}*/
// Below are the three state functions. Each one is called based on the name of the state,
// so when currentState = Idle, we call Idle_EnterState. If currentState = Jump, we call
// Jump_SuperUpdate().
private void Idle_EnterState()
{
controller.EnableSlopeLimit();
controller.EnableClamping();
}
// Run every frame we are in the idle state.
private void Idle_SuperUpdate()
{
if (input.Current.JumpInput) {
currentState = PlayerStates.Jump;
return;
}
if (!MaintainingGround()) {
currentState = PlayerStates.Fall;
return;
}
if (input.Current.MoveInput != Vector3.zero) {
currentState = PlayerStates.Walk;
return;
}
// Apply friction to slow us to a halt.
moveDirection = Vector3.MoveTowards(moveDirection, Vector3.zero, 10.0f * controller.deltaTime);
}
// Run once when we exit the idle state.
private void Idle_ExitState()
{
}
private void Walk_SuperUpdate()
{
if (input.Current.JumpInput) {
currentState = PlayerStates.Jump;
return;
}
if (!MaintainingGround()) {
currentState = PlayerStates.Fall;
return;
}
if (input.Current.MoveInput != Vector3.zero) {
moveDirection = Vector3.MoveTowards(moveDirection, LocalMovement() * WalkSpeed, WalkAcceleration * controller.deltaTime);
}
else {
currentState = PlayerStates.Idle;
return;
}
}
private void Jump_EnterState()
{
controller.DisableClamping();
controller.DisableSlopeLimit();
moveDirection += controller.up * CalculateJumpSpeed(JumpHeight, Gravity);
}
private void Jump_SuperUpdate()
{
Vector3 planarMoveDirection = Math3d.ProjectVectorOnPlane(controller.up, moveDirection);
Vector3 verticalMoveDirection = moveDirection - planarMoveDirection;
if (Vector3.Angle(verticalMoveDirection, controller.up) > 90 && AcquiringGround()) {
moveDirection = planarMoveDirection;
currentState = PlayerStates.Idle;
return;
}
planarMoveDirection = Vector3.MoveTowards(planarMoveDirection, LocalMovement() * WalkSpeed, JumpAcceleration * controller.deltaTime);
verticalMoveDirection -= controller.up * Gravity * controller.deltaTime;
moveDirection = planarMoveDirection + verticalMoveDirection;
}
private void Fall_EnterState()
{
controller.DisableClamping();
controller.DisableSlopeLimit();
}
private void Fall_SuperUpdate()
{
if (AcquiringGround()) {
moveDirection = Math3d.ProjectVectorOnPlane(controller.up, moveDirection);
currentState = PlayerStates.Idle;
return;
}
moveDirection -= controller.up * Gravity * controller.deltaTime;
}
}