I’m working on a character motor that includes drifting, acceleration, and deceleration and at some point I basically botched the entire thing as far as extensibility is concerned. Here’s the old script, which is a bit of a mess, but I think I’ve named everything well enough that it should be readable.
using UnityEngine;
using System.Collections;
using Rewired;
[RequireComponent(typeof(CharacterController))]
public class DrifterMotor : MonoBehaviour {
public int playerID = 0;
public float topSpeed = 10f;
public float driftLength = 3f;
public float turnSpeed = 60f;
public float driftTurnSpeed = 120f;
public float acceleration = 10f;
public MovementStatus movementStatus;
private Vector3 driftVelocity;
private Vector3 driftVelocityInternal;
private Vector3 velocity;
private float verticalSpeed;
private float driftLengthInternal;
private float currentSpeed;
private bool cancelDrift = false;
private Player player;
private CharacterController controller;
private float moveVertical;
private float moveHorizontal;
public enum MovementStatus {
driving,
drifting,
driftLock
}
void Start () {
driftLengthInternal = driftLength;
driftVelocityInternal = new Vector3 (0, 0, 0);
player = ReInput.players.GetPlayer(playerID);
controller = GetComponent<CharacterController> ();
}
float remap(float value, float low1, float high1, float low2, float high2) {
return low2 + (value - low1) * (high2 - low2) / (high1 - low1);
}
void Countdown() {
driftLength -= Time.deltaTime;
if (driftLength < 0f) {
cancelDrift = true;
driftLength = driftLengthInternal;
}
}
void DriftMovement() {
float value = remap (driftLength, 0f, driftLengthInternal, 1f, 0f);
Vector3 newDirection = transform.forward * topSpeed * moveVertical;
driftVelocity = Vector3.Lerp (driftVelocityInternal, newDirection, value);
Countdown ();
controller.Move (driftVelocity * Time.deltaTime);
Vector2 velocityMagnitude = new Vector2 (driftVelocity.x, driftVelocity.z);
currentSpeed = velocityMagnitude.magnitude;
}
void NormalMovement () {
if (player.GetAxis ("MoveVertical") > 0) {
currentSpeed += acceleration * Time.deltaTime * player.GetAxis ("MoveVertical");
} else if (player.GetAxis ("MoveVertical") < 0) {
currentSpeed -= acceleration * Time.deltaTime * player.GetAxis ("MoveVertical");
} else if (player.GetAxis ("MoveVertical") == 0) {
currentSpeed = 0f;
}
if (currentSpeed > topSpeed) {
currentSpeed = topSpeed;
}
driftVelocityInternal = transform.forward * currentSpeed * player.GetAxis ("MoveVertical");
controller.Move (transform.forward * currentSpeed * player.GetAxis ("MoveVertical") * Time.deltaTime);
}
void ApplyGravity() {
verticalSpeed += Physics.gravity.y * Time.deltaTime;
velocity.y = verticalSpeed;
driftVelocity.y = verticalSpeed;
}
void Update () {
moveVertical = player.GetAxis ("MoveVertical");
moveHorizontal = player.GetAxis ("MoveHorizontal");
if (movementStatus == MovementStatus.driving) {
driftLength = driftLengthInternal;
}
if (player.GetButton ("Drift") && cancelDrift == false) {
transform.Rotate(0, moveHorizontal * driftTurnSpeed * Time.deltaTime, 0);
movementStatus = MovementStatus.drifting;
} else {
cancelDrift = false;
transform.Rotate(0, moveHorizontal * turnSpeed * Time.deltaTime, 0);
movementStatus = MovementStatus.driving;
}
ApplyGravity ();
if (movementStatus == MovementStatus.driving) {
NormalMovement ();
} else {
DriftMovement ();
}
controller.Move (velocity * Time.deltaTime);
}
}
As you can probably see, when the player holds down the drift button, they enter drift mode, which allows them to turn faster for a limited amount of time (3 seconds) but also causes them to drift in the velocity they were previously travelling. However, I’m encountering a few problems here.
- The player doesn’t stop drifting when the countdown timer reaches zero. Instead, the timer just resets. I should be able to figure out how to fix this, but I can’t.
- When coming out of the drift at certain angles (basically anything where they haven’t come to a complete stop or facing roughly the same or opposite direction as they’re drifting in), the movement is sharp and sudden. I’d prefer having acceleration applied to this, but I can’t figure out how to do that, even though I have acceleration code in there. I think I need to find the velocity they’re traveling relative to the forward direction, but I have no idea how to do that. Vector math confuses the hell out of me, unfortunately.
- I’d like it if it took some time to come to a complete stop, instead of stopping on a dime like it does now. I have no idea how to do this even though it should be the simplest thing in the world.
Here’s what I have now for version 2 of the script, though it’s woefully incomplete (although better commented):
using UnityEngine;
using System.Collections;
using Rewired;
[RequireComponent(typeof(CharacterController))]
public class DrifterMotor2 : MonoBehaviour {
//Rewired Input Manager Variables
public int playerID = 0;
private Player player;
private CharacterController controller;
private float moveVertical;
private float moveHorizontal;
//Movement Variables
[Tooltip("Player maximum speed")]
public float maxSpeed = 10f;
[Tooltip("How long the player can drift for in seconds")]
public float driftLength = 3f;
[Tooltip("How fast the player turns while driving")]
public float turnSpeed = 60f;
[Tooltip("How fast the player turns while drifting")]
public float driftTurnSpeed = 120f;
[Tooltip("How fast the player accelerates")]
public float acceleration = 10f;
[Tooltip("How fast the player decelerates")]
public float deceleration = 3f;
[Tooltip("Current movement state")]
public MovementState movementState;
public enum MovementState {
driving,
drifting,
}
//Private Movement Variables
private Vector3 driftVelocity;
private Vector3 driftVelocityInternal;
private Vector3 velocity;
private float verticalSpeed;
private float driftLengthInternal;
private float currentSpeed;
//Used To Handle Ending Drift Status
private bool disableDrift = false;
//Simple Value Remap
float remap(float value, float low1, float high1, float low2, float high2) {
return low2 + (value - low1) * (high2 - low2) / (high1 - low1);
}
//Initialisation
void Start () {
player = ReInput.players.GetPlayer(playerID);
controller = GetComponent<CharacterController> ();
driftLengthInternal = driftLength;
}
//Apply Gravity To Character Controller
void ApplyGravity() {
verticalSpeed += Physics.gravity.y * Time.deltaTime;
velocity.y = verticalSpeed;
driftVelocity.y = verticalSpeed;
}
//Countdown Timer
void Countdown() {
driftLength -= Time.deltaTime;
if (driftLength < 0f) {
disableDrift = true;
driftLength = driftLengthInternal;
}
}
//Calculate Movement While Drifting
void DriftMovement() {
//Remap Timer To 0 - 1
float value = remap (driftLength, 0f, driftLengthInternal, 1f, 0f);
Vector3 newDirection = transform.forward * maxSpeed * moveVertical;
driftVelocity = Vector3.Lerp (driftVelocityInternal, newDirection, value);
Countdown ();
controller.Move (driftVelocity * Time.deltaTime);
Vector2 velocityMagnitude = new Vector2 (driftVelocity.x, driftVelocity.z);
currentSpeed = velocityMagnitude.magnitude;
}
//Calculate Normal Movement
void NormalMovement() {
//Set The Internal Drift Velocity
driftVelocityInternal = transform.forward * currentSpeed * player.GetAxis ("MoveVertical");
}
void Update () {
moveVertical = player.GetAxis ("MoveVertical");
moveHorizontal = player.GetAxis ("MoveHorizontal");
if (controller.isGrounded) {
verticalSpeed = 0f;
NormalMovement ();
}
ApplyGravity ();
controller.Move (velocity * Time.deltaTime);
}
}
Any help pointing me in the right direction to implement these features would be greatly appreciated.