195 lines
5.9 KiB
C#
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;
|
|||
|
|
}
|
|||
|
|
}
|