220 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			C#
		
	
	
	
		
		
			
		
	
	
			220 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			C#
		
	
	
	
|  | using UnityEngine; | |||
|  | 
 | |||
|  | //Example implementation of the SuperStateMachine and SuperCharacterController | |||
|  | [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 | |||
|  |     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; | |||
|  | 
 | |||
|  | 	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; | |||
|  | 	} | |||
|  | 
 | |||
|  |     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 BEFORE the state's update function. | |||
|  |         // This is run regardless of what state you're in | |||
|  |     } | |||
|  | 
 | |||
|  |     protected override void LateGlobalSuperUpdate() | |||
|  |     { | |||
|  |         // 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 | |||
|  | 
 | |||
|  |         // 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() | |||
|  |     void Idle_EnterState() | |||
|  |     { | |||
|  |         controller.EnableSlopeLimit(); | |||
|  |         controller.EnableClamping(); | |||
|  |     } | |||
|  | 
 | |||
|  |     // Run every frame we are in the idle state | |||
|  |     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 | |||
|  |     void Idle_ExitState() | |||
|  |     { | |||
|  |     } | |||
|  | 
 | |||
|  |     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; | |||
|  |         } | |||
|  |     } | |||
|  | 
 | |||
|  |     void Jump_EnterState() | |||
|  |     { | |||
|  |         controller.DisableClamping(); | |||
|  |         controller.DisableSlopeLimit(); | |||
|  | 
 | |||
|  |         moveDirection += controller.up * CalculateJumpSpeed(JumpHeight, Gravity); | |||
|  |     } | |||
|  | 
 | |||
|  |     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; | |||
|  |     } | |||
|  | 
 | |||
|  |     void Fall_EnterState() | |||
|  |     { | |||
|  |         controller.DisableClamping(); | |||
|  |         controller.DisableSlopeLimit(); | |||
|  |     } | |||
|  | 
 | |||
|  |     void Fall_SuperUpdate() | |||
|  |     { | |||
|  |         if(AcquiringGround()) | |||
|  |         { | |||
|  |             moveDirection = Math3d.ProjectVectorOnPlane(controller.up, moveDirection); | |||
|  |             currentState = PlayerStates.Idle; | |||
|  |             return; | |||
|  |         } | |||
|  | 
 | |||
|  |         moveDirection -= controller.up * Gravity * controller.deltaTime; | |||
|  |     } | |||
|  | } |