Skip to content

Instantly share code, notes, and snippets.

@zalo
Last active April 5, 2023 06:38
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save zalo/d9d423680ca92fcca98b02605f885e12 to your computer and use it in GitHub Desktop.
Save zalo/d9d423680ca92fcca98b02605f885e12 to your computer and use it in GitHub Desktop.
Physics-Based Unity Player Controller
using UnityEngine;
public class MovingPlatform : MonoBehaviour {
public float timeInterval = 5f;
public AnimationCurve XMotion;
public AnimationCurve YMotion;
public AnimationCurve ZMotion;
public bool PingPong = true;
float platformTime;
bool ping = true;
Vector3 initialPosition;
Quaternion initialRotation;
Rigidbody body;
void Start () {
body = GetComponent<Rigidbody>();
initialPosition = transform.position;
initialRotation = transform.rotation;
}
void FixedUpdate () {
if (PingPong) {
if (ping) {
platformTime += Time.fixedDeltaTime;
if (platformTime > timeInterval) {
ping = false;
platformTime = platformTime - (platformTime - timeInterval);
}
} else {
platformTime -= Time.fixedDeltaTime;
if (platformTime < 0f) {
ping = true;
platformTime *= -1f;
}
}
}else{
ping = true;
platformTime = Mathf.Repeat(Time.fixedTime, timeInterval);
}
Vector3 curPos = evaluatePositionAtTime(platformTime);
Vector3 prevPos = evaluatePositionAtTime(platformTime + (Time.fixedDeltaTime * (ping ? -1f : 1f)));
body.MovePosition(curPos);
body.velocity = ((curPos - prevPos) / Time.fixedDeltaTime);
}
private void OnDrawGizmosSelected() {
if (!Application.isPlaying) {
initialPosition = transform.position;
initialRotation = transform.rotation;
}
float step = 0.01f;
for(float i = 0f; i<1f; i += step) {
Gizmos.color = Color.HSVToRGB(i, 1f, 1f);
Gizmos.DrawLine(evaluatePositionAtTime(i*timeInterval), evaluatePositionAtTime((i + step) * timeInterval));
}
}
Vector3 evaluatePositionAtTime(float time) {
return initialPosition + (initialRotation * new Vector3(XMotion.Evaluate(time / timeInterval), YMotion.Evaluate(time / timeInterval), ZMotion.Evaluate(time / timeInterval)));
}
}
//See this image for set up information: http://i.imgur.com/pzfuzLn.png
//Don't forget to freeze the rotation on your rigidbody!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour {
Camera cam;
Rigidbody body;
bool jumpPressed;
//Collision State
List<Collider> colliding = new List<Collider>();
Collider groundCollider = new Collider();
Rigidbody groundRigidbody = new Rigidbody();
Vector3 groundNormal = Vector3.down;
Vector3 groundContactPoint = Vector3.zero;
Vector3 groundVelocity = Vector3.zero;
//Initialize variables
void Start() {
cam = GetComponentInChildren<Camera>();
body = GetComponent<Rigidbody>();
}
//Movement Handling
void FixedUpdate() {
//Record the world-space walking movement
Vector3 movement = transform.rotation * new Vector3(Input.GetAxisRaw("Horizontal"), 0f, Input.GetAxisRaw("Vertical")).normalized;
//If we're currently contacting a wall/ground like object
if (groundCollider != null && Vector3.Dot(Vector3.up, groundNormal) > -0.3f) {
//Subtract the ground's velocity
if (groundRigidbody != null && groundRigidbody.isKinematic) {
body.velocity -= groundVelocity;
}
//Walking along the ground movement
if (Vector3.Dot(Vector3.up, groundNormal) > 0.5f) {
if (movement != Vector3.zero) {
Vector2 XYVel = new Vector2(body.velocity.x, body.velocity.z);
XYVel = Mathf.Clamp(XYVel.magnitude, 0f, 3f) * XYVel.normalized;
body.velocity = new Vector3(XYVel.x, body.velocity.y, XYVel.y);
} else {
body.velocity = new Vector3(body.velocity.x * 0.8f, body.velocity.y, body.velocity.z * 0.8f);
}
body.velocity += movement;
}
//Handle jumping
if (jumpPressed && body.velocity.y <= 0.1f) { body.velocity += Vector3.Slerp(Vector3.up, groundNormal, 0.2f) * 6f; }
//Draw some debug info
Debug.DrawLine(groundContactPoint, groundContactPoint + groundNormal, Color.blue, 2f);
//Add back the ground's velocity
if (groundRigidbody != null && groundRigidbody.isKinematic) {
groundVelocity = groundRigidbody.GetPointVelocity(groundContactPoint);
body.velocity += groundVelocity;
}
} else {
body.velocity += movement * 0.1f;
groundVelocity = Vector3.zero;
}
groundNormal = Vector3.down;
groundCollider = null;
groundRigidbody = null;
groundContactPoint = (transform.position - Vector3.down * -0.5f);
jumpPressed = false;
}
//Per-Frame Updates
void Update() {
//Record whether the jump key was hit this frame
//NOTE: Must be done in Update, not FixedUpdate
jumpPressed = jumpPressed ? jumpPressed : Input.GetButtonDown("Jump");
//Rotate the player
transform.Rotate(0, (Input.GetAxis("Mouse X")) * 2f, 0);
//Rotate the camera rig and prevent it from penetrating the environment
RaycastHit hit;
cam.transform.parent.localRotation *= Quaternion.Euler(-Input.GetAxis("Mouse Y") * 2f, 0, 0);
cam.transform.localPosition = (Physics.SphereCast(cam.transform.parent.position, cam.nearClipPlane * 0.5f, -cam.transform.parent.forward, out hit, 3f) ? (Vector3.back * hit.distance) : Vector3.back * 3f);
}
//Ground Collision Handling
void OnCollisionEnter(Collision collision) {
colliding.Add(collision.collider);
}
void OnCollisionStay(Collision collision) {
if (collision.impulse.magnitude > float.Epsilon) {
if (!colliding.Contains(collision.collider)) {
colliding.Add(collision.collider);
}
//Record ground telemetry
for (int i = 0; i < collision.contacts.Length; i++) {
if (Vector3.Dot(Vector3.up, collision.contacts[i].normal) > Vector3.Dot(Vector3.up, groundNormal)) {
groundNormal = collision.contacts[i].normal;
groundCollider = collision.collider;
groundContactPoint = collision.contacts[i].point;
groundRigidbody = collision.rigidbody;
if (groundRigidbody != null && groundVelocity == Vector3.zero) {
groundVelocity = groundRigidbody.GetPointVelocity(groundContactPoint);
}
}
}
}
}
void OnCollisionExit(Collision collision) {
colliding.Remove(collision.collider);
if (colliding.Count == 0) {
groundVelocity = Vector3.zero;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment