Last active
March 15, 2022 09:24
-
-
Save MadLittleMods/10935622 to your computer and use it in GitHub Desktop.
Unity Character Driver/Controller for Movement, Jumping, and Gravity
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using System.Collections; | |
[RequireComponent (typeof(CharacterController))] | |
public class CharacterDriver : MonoBehaviour { | |
public class CharacterState | |
{ | |
public Vector3 position = new Vector3(); | |
public Vector3 velocity = new Vector3(); | |
public CharacterState() | |
{ | |
} | |
public CharacterState(CharacterState s) | |
{ | |
this.position = s.position; | |
this.velocity = s.velocity; | |
} | |
public CharacterState(Vector3 position,Vector3 velocity) | |
{ | |
this.position = position; | |
this.velocity = velocity; | |
} | |
public static CharacterState Lerp(CharacterState from, CharacterState to, float t) | |
{ | |
return new CharacterState(Vector3.Lerp(from.position, to.position, t), Vector3.Lerp(from.velocity, to.velocity, t)); | |
} | |
public static implicit operator string(CharacterState s) | |
{ | |
return s.ToString(); | |
} | |
public string ToString() | |
{ | |
return "p: " + this.position + " v: " + this.velocity; | |
} | |
} | |
public float maxSurgeSpeed = 5f; // Forward, backward | |
public float maxSwaySpeed = 5f; // Side to side | |
public float jumpHeight = 4f; | |
CharacterController characterController; | |
// We use these to smooth between values in certain framerate situations in the `Update()` loop | |
CharacterState currentState = new CharacterState(); | |
CharacterState previousState = new CharacterState(); | |
// Use this for initialization | |
void Start () { | |
this.characterController = GetComponent<CharacterController>(); | |
this.ResetCharacterDriver(); | |
} | |
// Use this to reset any built up forces | |
[ContextMenu("Reset Character State")] | |
void ResetCharacterDriver() | |
{ | |
// Set the transition state | |
this.currentState = new CharacterState(transform.position, Vector3.zero); | |
this.previousState = this.currentState; | |
} | |
float t = 0f; | |
float dt = 0.01f; | |
float currentTime = 0f; | |
float accumulator = 0f; | |
bool isFirstPhysicsFrame = true; | |
// Update is called once per frame | |
void Update () | |
{ | |
// Fixed deltaTime rendering at any speed with smoothing | |
// Technique: http://gafferongames.com/game-physics/fix-your-timestep/ | |
float frameTime = Time.time - currentTime; | |
this.currentTime = Time.time; | |
this.accumulator += frameTime; | |
while (this.accumulator >= this.dt) | |
{ | |
this.previousState = this.currentState; | |
this.currentState = this.MoveUpdate(this.currentState, this.dt); | |
//integrate(state, this.t, this.dt); | |
Vector3 movementDelta = currentState.position - transform.position; | |
this.characterController.Move(movementDelta); | |
this.currentState = new CharacterState(transform.position, this.currentState.velocity); | |
accumulator -= this.dt; | |
this.t += this.dt; | |
} | |
// Reset it | |
this.isFirstPhysicsFrame = true; | |
} | |
CharacterState MoveUpdate(CharacterState state, float deltaTime) | |
{ | |
CharacterState currentState = new CharacterState(state); | |
// Add the gravity | |
if(this.characterController.isGrounded) | |
{ | |
// Remove the gravity in the direction we hit (ground) | |
currentState.velocity -= Vector3.Project(currentState.velocity, Physics.gravity.normalized); | |
} | |
currentState.velocity += Physics.gravity * deltaTime; | |
// Jumping | |
if(this.characterController.isGrounded && Input.GetButtonDown("Jump") && this.isFirstPhysicsFrame) | |
{ | |
// Play jump sound effect here | |
// Jump the player | |
currentState.velocity += -1f * Physics.gravity.normalized * this.CalculateJumpVerticalSpeed(this.jumpHeight); | |
} | |
// Movement Surge and Sway | |
// Elliptical player/character movement. See diagram for more info: | |
// http://i.imgur.com/am2OYj1.png | |
float angle = Mathf.Atan2(Input.GetAxis("Vertical"), Input.GetAxis("Horizontal")); | |
float surgeSpeed = Mathf.Abs(Input.GetAxis("Vertical")) * this.maxSurgeSpeed * Mathf.Sin(angle); // Forward and Backward | |
float swaySpeed = Mathf.Abs(Input.GetAxis("Horizontal")) * this.maxSwaySpeed * Mathf.Cos(angle); // Left and Right | |
// Get the camera directions without the Y component | |
Vector3 cameraForwardNoY = Vector3.Scale(Camera.main.transform.forward, new Vector3(1, 0, 1)).normalized; | |
Vector3 cameraRightNoY = Vector3.Scale(Camera.main.transform.right, new Vector3(1, 0, 1)).normalized; | |
Vector3 movementDelta = (cameraForwardNoY*surgeSpeed + cameraRightNoY*swaySpeed) * deltaTime; | |
movementDelta += currentState.velocity * deltaTime; | |
currentState.position += movementDelta; | |
// Set this so we don't get confused later on | |
this.isFirstPhysicsFrame = false; | |
return currentState; | |
} | |
float CalculateJumpVerticalSpeed(float targetJumpHeight) | |
{ | |
// From the jump height and gravity we deduce the upwards speed | |
// for the character to reach at the apex. | |
// See for formula: http://math.stackexchange.com/a/222585/60008 | |
return Mathf.Sqrt(2f * targetJumpHeight * Physics.gravity.magnitude); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
cool,thanks for sharing.