/* * 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(); // Grab the controller object from our object. controller = gameObject.GetComponent(); // 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; } /// /// Constructs a vector representing our movement local to our lookDirection, which is /// controlled by the camera. /// 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; } }