Easing (Lerp) is just… awful.
With that out of the way, here’s my problem.
I’m (pathetically) trying to make a first-person character controller, and am having unbelievable amounts of trouble actually getting anything even remotely related to the camera working.
Part of the problem is I want this dynamic, otherwise, I’d just use animations and forget the concept of math ever existed.
I’m trying to get the camera to bob down and then back up when the player lands after jumping (or falling), which is proving to be substantially more difficult than I ever thought possible for such a simple thing.
I’m trying to replicate exponential ease-out, to very little success. This is probably the wrong approach to this but I’m not even able to achieve the wrong thing after several hours of staring at strange websites and trying to get anything at all working.
The coroutine supposedly meant to achieve the easing is at 238, and the call for it is at 163 (not even sure this is the best way to check if the player lands either).
I’m completely out of ideas and have absolutely no idea where to go from here.
Please excuse my trash code, I’m not a programmer.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Security.Principal;
using System.Threading;
using UnityEngine;
[RequireComponent(typeof(CharacterController))]
[AddComponentMenu("Arcane/Player/Character Controller")]
public class ArcaneCharacterController : MonoBehaviour
{
//Public Reference Access
public static ArcaneCharacterController arcaneCharacter;
//Variables and defaults
public float playerLookSensitivity = 8f;
public float playerWalkingSpeed = 5f;
public float playerRunningSpeed = 10f;
public float playerJumpStrength = 4f;
public float vertRotLimit = 80f;
float gravityModifier = 1.0f;
//Backend
float forwardMovt;
float sidewaysMovt;
float verticalVelocity;
bool airControl;
bool hasJumped;
float vertRot = 0;
//CharacterController Reference
CharacterController cc;
//Camera head bob
float bobSpeedWalk = 0.18f;
float bobSpeedRun = 0.32f;
float bobHeight = 0.2f;
[HideInInspector]
public float bobMid = 0.7f;
bool isHeadBobbing = true;
float bobTimer = 0f;
void Awake()
{
//If player already exists, destroy. If not, set reference to this and set player to DDOL.
if (arcaneCharacter != null)
{
Destroy(gameObject);
}
else
{
arcaneCharacter = this;
DontDestroyOnLoad(gameObject);
}
cc = GetComponent<CharacterController>();
Cursor.visible = false;
Cursor.lockState = CursorLockMode.Locked;
}
void Update()
{
#region Movement
//Set variables to preferences (obtained through Arcane_GameManager static reference)
playerLookSensitivity = float.Parse(Arcane_GameManager.manager.GetPref("playerLookSpeed"));
playerWalkingSpeed = float.Parse(Arcane_GameManager.manager.GetPref("playerWalkingSpeed"));
playerRunningSpeed = float.Parse(Arcane_GameManager.manager.GetPref("playerRunningSpeed"));
playerJumpStrength = float.Parse(Arcane_GameManager.manager.GetPref("playerJumpHeight"));
vertRotLimit = float.Parse(Arcane_GameManager.manager.GetPref("playerVertLimit"));
gravityModifier = float.Parse(Arcane_GameManager.manager.GetPref("playerGravMod"));
airControl = bool.Parse(Arcane_GameManager.manager.GetPref("playerAirCtrl"));
//Horizontal Look
float horiRot = Input.GetAxis("Mouse X") * (playerLookSensitivity / 4);
transform.Rotate(0, horiRot, 0);
//Vertical Look
vertRot -= Input.GetAxis("Mouse Y") * (playerLookSensitivity / 4);
//Clamp (Limit) view angles to +vertRotLimit and -vertRotLimit
vertRot = Mathf.Clamp(vertRot, -vertRotLimit, vertRotLimit);
//Set camera rotation
Camera.main.transform.localRotation = Quaternion.Euler(vertRot, 0, 0);
//Look With Keys
//Horizontal
if (Input.GetKey(Arcane_GameManager.manager.GetCont("contRotLeft")))
{
transform.Rotate(0, -1.25f, 0);
}
if (Input.GetKey(Arcane_GameManager.manager.GetCont("contRotRight")))
{
transform.Rotate(0, 1.25f, 0);
}
//Vertical
if (Input.GetKey(Arcane_GameManager.manager.GetCont("contLookUp")))
{
vertRot += -1f;
}
if (Input.GetKey(Arcane_GameManager.manager.GetCont("contLookDown")))
{
vertRot += 1f;
}
//Movement
if (!airControl && cc.isGrounded)
{
forwardMovt = CustomAxis(0f, Arcane_GameManager.manager.GetCont("contForward"), Arcane_GameManager.manager.GetCont("contBackward")) * playerWalkingSpeed;
sidewaysMovt = CustomAxis(0f, Arcane_GameManager.manager.GetCont("contRight"), Arcane_GameManager.manager.GetCont("contLeft")) * playerWalkingSpeed;
//Sprint toggle
if (Input.GetKeyDown(Arcane_GameManager.manager.GetCont("contSprintToggle")))
{
Arcane_GameManager.manager.sprintToggle = !Arcane_GameManager.manager.sprintToggle;
}
//If Running
if (Input.GetKey(Arcane_GameManager.manager.GetCont("contSprintHold")) || Arcane_GameManager.manager.sprintToggle)
{
forwardMovt = Input.GetAxis("Vertical") * playerRunningSpeed;
sidewaysMovt = Input.GetAxis("Horizontal") * playerRunningSpeed;
}
}
else if (airControl)
{
forwardMovt = CustomAxis(0f, Arcane_GameManager.manager.GetCont("contForward"), Arcane_GameManager.manager.GetCont("contBackward")) * playerWalkingSpeed;
sidewaysMovt = CustomAxis(0f, Arcane_GameManager.manager.GetCont("contRight"), Arcane_GameManager.manager.GetCont("contLeft")) * playerWalkingSpeed;
//Sprint toggle
if (Input.GetKeyDown(Arcane_GameManager.manager.GetCont("contSprintToggle")))
{
Arcane_GameManager.manager.sprintToggle = !Arcane_GameManager.manager.sprintToggle;
}
//If Running
if (Input.GetKey(Arcane_GameManager.manager.GetCont("contSprintHold")) || Arcane_GameManager.manager.sprintToggle)
{
forwardMovt = Input.GetAxis("Vertical") * playerRunningSpeed;
sidewaysMovt = Input.GetAxis("Horizontal") * playerRunningSpeed;
}
}
//Add Gravity
verticalVelocity += (Physics.gravity.y * gravityModifier) * Time.deltaTime;
//Jump
if (Input.GetKey(Arcane_GameManager.manager.GetCont("contJump")) && cc.isGrounded)
{
verticalVelocity = playerJumpStrength;
}
//Player Lands
if(Input.GetKey(Arcane_GameManager.manager.GetCont("contJump")) && !cc.isGrounded)
{
hasJumped = true;
}
//Head bob
if(hasJumped && cc.isGrounded)
{
StartCoroutine(LandHeadBob(Camera.main.transform.localPosition, bobHeight, 1.0f));
hasJumped = false;
}
//Create movement vector
Vector3 playerMovement = new Vector3(sidewaysMovt, verticalVelocity, forwardMovt);
//Apply movement vector to CharacterController
cc.Move(transform.rotation * playerMovement * Time.deltaTime);
#endregion
#region Camera Bob
bobSpeedWalk = float.Parse(Arcane_GameManager.manager.GetPref("playerHBobWlkSpd"));
bobSpeedRun = float.Parse(Arcane_GameManager.manager.GetPref("playerHBobRunSpd"));
bobHeight = float.Parse(Arcane_GameManager.manager.GetPref("playerHBobHgt"));
bobMid = float.Parse(Arcane_GameManager.manager.GetPref("playerHeight")) - (float)(Math.Truncate(float.Parse(Arcane_GameManager.manager.GetPref("playerHeight"))));
float waveslice = 0f;
Vector3 posConvert = Camera.main.transform.localPosition;
if (Mathf.Abs(sidewaysMovt) == 0 && Mathf.Abs(forwardMovt) == 0)
{
bobTimer = 0f;
}
else
{
waveslice = Mathf.Sin(bobTimer);
//If Running
if (Input.GetKey(Arcane_GameManager.manager.GetCont("contSprintHold")) || Arcane_GameManager.manager.sprintToggle)
{
bobTimer = bobTimer + bobSpeedRun;
}
else
{
//Walking
bobTimer = bobTimer + bobSpeedWalk;
}
if (bobTimer > Mathf.PI * 2)
{
bobTimer = bobTimer - (Mathf.PI * 2);
}
}
if (waveslice != 0)
{
float translateChange = waveslice * bobHeight;
float totalAxes = Mathf.Abs(sidewaysMovt) + Mathf.Abs(forwardMovt);
totalAxes = Mathf.Clamp(totalAxes, 0.0f, 1.0f);
translateChange = totalAxes * translateChange;
if (isHeadBobbing)
{
posConvert.y = bobMid + translateChange;
}
else if (!isHeadBobbing)
{
posConvert.x = translateChange;
}
}
else
{
if (isHeadBobbing)
{
posConvert.y = bobMid;
}
else if (!isHeadBobbing)
{
posConvert.x = 0;
}
}
Camera.main.transform.localPosition = posConvert;
#endregion
}
IEnumerator LandHeadBob(Vector3 start, float y_target, float duration)
{
float elapsed_time = 0;
Vector3 pos = start;
float y_start = pos.y;
while(elapsed_time < duration)
{
pos.y = EaseOutExpo(y_start, y_target, elapsed_time / duration);
start = pos;
yield return null;
elapsed_time += Time.deltaTime;
}
}
public static float EaseOutExpo(float start, float end, float value)
{
end -= start;
return end * (-Mathf.Pow(2, -10 * value) + 1) + start;
}
//Smooth movement output
float CustomAxis(float value, KeyCode positive, KeyCode negative)
{
float value2 = value;
if (Input.GetKey(positive) && !Input.GetKey(negative))
{
value2 = Mathf.SmoothStep(value, 1f, 1f);
}
if (Input.GetKey(negative) && !Input.GetKey(positive))
{
value2 = Mathf.SmoothStep(value, -1f, 1f);
}
if (!Input.GetKey(positive) && !Input.GetKey(negative))
{
value2 = Mathf.SmoothStep(value, 0f, 1f);
}
return value2;
}
public void SetPlayerHeight(float totalHeight)
{
//Set CharacterController Height
cc.height = (float)(Math.Truncate(totalHeight) + 1);
//Set Camera Height
Vector3 camPos = new Vector3(0, (cc.height - totalHeight), 0);
Camera.main.transform.localPosition = camPos;
}
}