Several reasons why I am abandoning Rigidbody Character Controller, but one of the main ones is I grew tired of fighting with step detection. What I need help with is minor but I can’t find any guidance to restrict some movement on the Character controller to keep it from completely turning around when using the WASD or Arrow Keys. I also need to be able to properly toggle on/off Run better than I currently have it.
Here is the script I am converting to. There is a note next to what I am trying to resolve:
using System;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
private Animator anim;
private CharacterController cc;
public Vector2 inputNormalized;
[Header("Movement")]
public bool steer;
public float gravity = -9.10f;
[Range(0, 10)] public float jH = 1f;
public float moveSpeed;
[Range(0, 10)] public float runSpeed;
public float rotSpeed;
public float jumpSpeed;
public float jumpButtonGracePeriod;
private float ySpeed;
private float originalStepOffset;
private float? lastGroundedTime;
private float? jumpButtonPressedTime;
public bool isRunning;
public void Start()
{
CameraControl cameraControl = FindObjectOfType<CameraControl>();
cameraControl.Init();
}
// Start is called before the first frame update
private void Awake()
{
cc = GetComponent<CharacterController>();
anim = GetComponent<Animator>();
originalStepOffset = cc.stepOffset;
}
// Update is called once per frame
private void Update()
{
Move();
}
private void Move()
{
float x = Input.GetAxis("Horizontal");
float z = Input.GetAxis("Vertical");// I belive this is where I need something for (< 0 ? -input.x : input.x ) as it is in a rigidbody setup but cannot sort out the equvilant in a Character Controller;
Vector3 movementDirection = new Vector3(x, 0, z);
float magnitude = Mathf.Clamp01(movementDirection.magnitude) * moveSpeed;
movementDirection.Normalize();
cc.SimpleMove(movementDirection * magnitude);
if (movementDirection != Vector3.zero)
{
Quaternion toRotation = Quaternion.LookRotation(movementDirection, Vector3.up);
transform.rotation = Quaternion.RotateTowards(transform.rotation, toRotation, rotSpeed * Time.deltaTime);
}
//for the running I have at present this, but am asking for some suggestions to make it transition better as I dont want to use the hold down shift key.
if (Input.GetButtonDown("Run"))
{
isRunning = !isRunning;
}
if (isRunning)
{
moveSpeed = runSpeed;
;
}
else
{
moveSpeed = 0.5f;
}
ySpeed += Physics.gravity.y * Time.deltaTime;
if (cc.isGrounded)
{
lastGroundedTime = Time.time;
}
if (Input.GetButtonDown("Jump"))
{
jumpButtonPressedTime = Time.time;
}
if (Time.time - lastGroundedTime <= jumpButtonGracePeriod)
{
cc.stepOffset = originalStepOffset;
ySpeed = -0.5f;
if (Time.time - jumpButtonPressedTime <= jumpButtonGracePeriod)
{
ySpeed = jumpSpeed;
jumpButtonPressedTime = null;
lastGroundedTime = null;
}
}
else
{
cc.stepOffset = 0;
}
Vector3 velocity = movementDirection * magnitude;
velocity.y = ySpeed;
cc.Move(velocity * Time.deltaTime);
if (movementDirection != Vector3.zero)
{
anim.SetBool("IsMoving", true);
Quaternion toRotation = Quaternion.LookRotation(movementDirection, Vector3.up);
transform.rotation = Quaternion.RotateTowards(transform.rotation, toRotation, rotSpeed * Time.deltaTime);
}
else
{
anim.SetBool("IsMoving", false);
}
}
}
The Camera that needs a little refinement is:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraControl : MonoBehaviour
{
// NOTE:Been also having issues with collission detection to prevent camera from passing through other mesh/objects such as ground
// and getting Smoothing to work properly when using camera tpo steer player.
//Inputs
KeyCode leftMouse = KeyCode.Mouse0;
KeyCode rightMouse = KeyCode.Mouse1;
KeyCode middleMouse = KeyCode.Mouse2;
public CameraState cameraState = CameraState.CameraNone;
public CameraMoveState camMoveState = CameraMoveState.OnlyWhileMoving;
public float cameraAdjustSpeed = 1;
[SerializeField] [Range(0, 25)] float camMaxDistance;
[SerializeField] [Range(0, 10)] float camHeight;
[SerializeField] [Range(-90, 90)] float camMaxTilt, camMinTilt;
[SerializeField] [Range(0, 100)] float camSpeed;
[SerializeField] [Range(0, 300)] float scrollSpeed;
[SerializeField] [Range(0, 10)] float scrollMax;
[SerializeField] [Range(0, 10)] float currentTilt, currentPan, currentDistance;
//Collision
public bool collisionDebug;
public float collisionCushion = 0.35f;
public float clipCushion = 1.5f;
public int rayGridX = 9, rayGridY = 5;
[SerializeField] float adjustedDistance;
Vector3[] camClip, clipDirection, playerClip, rayColOrigin, rayColPoint;
bool[] rayColHit;
Ray camRay;
RaycastHit camRayHit;
//CameraSmoothing //Doesn't work as of yet and haven't sorted out why.
[SerializeField] float panAngle, panOffset;
bool camXAdjust, camYAdjust;
[SerializeField] float rotationXCushion = 3, rotationXSpeed = 0;
[SerializeField] float yRotMin = 0, yRotMax = 20, rotationYSpeed = 0;
//Reference
PlayerController player;
public Transform tilt;
Camera mainCam;
// Update is called once per frame
public void Init()
{
player = FindObjectOfType<PlayerController>();
mainCam = Camera.main;
transform.position = player.transform.position + Vector3.up * camHeight;
transform.rotation = player.transform.rotation;
tilt.eulerAngles = new Vector3(currentTilt, transform.eulerAngles.y, transform.eulerAngles.z);
mainCam.transform.position += tilt.forward * -currentDistance;
}
void Update()
{
if (!Input.GetKey(leftMouse) && !Input.GetKey(rightMouse) && !Input.GetKey(middleMouse))
cameraState = CameraState.CameraNone;
else if (Input.GetKey(rightMouse))
cameraState = CameraState.CameraRotate;
else if (!Input.GetKey(leftMouse) && Input.GetKey(rightMouse) && !Input.GetKey(middleMouse)) //if left mouse button is pressed
cameraState = CameraState.CameraSteer;
CameraClipInfo();
CameraCollisions();
CameraInputs();
ScrollCam();
CameraTransform();
}
void LateUpdate()
{
panAngle = Vector3.SignedAngle(transform.forward, player.transform.forward, Vector3.up);
switch (camMoveState)
{
case CameraMoveState.OnlyWhileMoving:
if (player.inputNormalized.magnitude > 0 || player.rotSpeed != 0)
{
CameraXAdjust();
CameraYAdjust();
}
break;
case CameraMoveState.OnlyHorizontalWhileMoving:
if (player.inputNormalized.magnitude > 0 || player.rotSpeed != 0)
CameraXAdjust();
break;
case CameraMoveState.AlwaysAdjust:
CameraXAdjust();
CameraYAdjust();
break;
case CameraMoveState.NeverAdjust:
CameraNeverAdjust();
break;
}
}
void CameraInputs()
{
if (cameraState != CameraState.CameraNone)
{
if (!camYAdjust && (camMoveState == CameraMoveState.AlwaysAdjust || camMoveState == CameraMoveState.OnlyWhileMoving))
camYAdjust = true;
if (cameraState == CameraState.CameraRotate)
if(!camXAdjust && camMoveState != CameraMoveState.NeverAdjust)
camXAdjust = true;
if (player.steer)
player.steer = false;
currentPan += Input.GetAxis("Mouse X") * camSpeed;
currentTilt -= Input.GetAxis("Mouse Y") * camSpeed;
currentTilt = Mathf.Clamp(currentTilt, camMinTilt, camMaxTilt);
}
if (!player.steer)
{
Vector3 playerReset = player.transform.eulerAngles;
playerReset.y = transform.eulerAngles.y;
player.transform.eulerAngles = playerReset;
player.steer = true;
}
//Keyboard & Mouse
if (Input.GetKey(rightMouse))
{
Cursor.visible = false;
Cursor.lockState = CursorLockMode.Locked;
}
else if (Cursor.visible == false)
{
Cursor.visible = true;
Cursor.lockState = CursorLockMode.None;
}
}
public void ScrollCam()
{
currentDistance -= Input.GetAxis("Mouse ScrollWheel") * scrollSpeed * Time.deltaTime;
currentDistance = Mathf.Clamp(currentDistance, 0, scrollMax);
}
public void CameraTransform()
{
switch (cameraState)
{
case CameraState.CameraNone:
currentPan = currentPan = player.transform.eulerAngles.y;
currentTilt = 10;
break;
}
transform.position = player.transform.position + Vector3.up * camHeight;
transform.eulerAngles = new Vector3(transform.eulerAngles.x, currentPan, transform.eulerAngles.z);
tilt.eulerAngles = new Vector3(currentTilt, transform.eulerAngles.y, transform.eulerAngles.z);
mainCam.transform.position = transform.position + tilt.forward * -currentDistance;
}
void CameraClipInfo()
{
camClip = new Vector3[4];
mainCam.CalculateFrustumCorners(new Rect(0, 0, 1, 1), mainCam.nearClipPlane, Camera.MonoOrStereoscopicEye.Mono, camClip);
clipDirection = new Vector3[4];
playerClip = new Vector3[4];
int rays = rayGridX * rayGridY;
rayColOrigin = new Vector3[rays];
rayColPoint = new Vector3[rays];
rayColHit = new bool[rays];
}
void CameraCollisions()
{
float camDistance = currentDistance + collisionCushion;
for (int i = 0; i < camClip.Length; i++)
{
Vector3 clipPoint = mainCam.transform.up * camClip[i].y + mainCam.transform.right * camClip[i].x;
clipPoint *= clipCushion;
clipPoint += mainCam.transform.forward * camClip[i].z;
clipPoint += transform.position - (tilt.forward * camMaxDistance);
Vector3 playerPoint = mainCam.transform.up * camClip[i].y + mainCam.transform.right * camClip[i].x;
playerPoint += transform.position;
clipDirection[i] = (clipPoint - playerPoint).normalized;
playerClip[i] = playerPoint;
}
int currentRay = 0;
bool isColliding = false;
float rayX = rayGridX - 1;
float rayY = rayGridY - 1;
for (int x = 0; x < rayGridX; x++)
{
Vector3 CU_Point = Vector3.Lerp(clipDirection[1], clipDirection[2], x / rayX);
Vector3 CL_Point = Vector3.Lerp(clipDirection[0], clipDirection[3], x / rayX);
Vector3 PU_Point = Vector3.Lerp(playerClip[1], playerClip[2], x / rayX);
Vector3 PL_Point = Vector3.Lerp(playerClip[0], playerClip[3], x / rayX);
for (int y = 0; y < rayGridY; y++)
{
camRay.origin = Vector3.Lerp(PU_Point, PL_Point, y / rayY);
camRay.direction = Vector3.Lerp(CU_Point, CL_Point, y / rayY);
rayColOrigin[currentRay] = camRay.origin;
if (Physics.Raycast(camRay, out camRayHit, camDistance))
{
isColliding = true;
rayColHit[currentRay] = true;
rayColPoint[currentRay] = camRayHit.point;
if (collisionDebug)
{
Debug.DrawLine(camRay.origin, camRayHit.point, Color.cyan);
Debug.DrawLine(camRayHit.point, camRay.origin + camRay.direction * camDistance, Color.magenta);
}
}
else
{
if (collisionDebug)
Debug.DrawLine(camRay.origin, camRay.origin + camRay.direction * camDistance, Color.cyan);
}
currentRay++;
}
}
if (isColliding)
{
float minRayDistance = float.MaxValue;
currentRay = 0;
for (int i = 0; i < rayColHit.Length; i++)
{
if (rayColHit[i])
{
float colDistance = Vector3.Distance(rayColOrigin[i], rayColPoint[i]);
if (colDistance < minRayDistance)
{
minRayDistance = colDistance;
currentRay = i;
}
}
}
Vector3 clipCenter = transform.position - (tilt.forward * currentDistance);
adjustedDistance = Vector3.Dot(-mainCam.transform.forward, clipCenter - rayColPoint[currentRay]);
adjustedDistance = currentDistance - (adjustedDistance + collisionCushion);
adjustedDistance = Mathf.Clamp(adjustedDistance, 0, camMaxDistance);
}
else
adjustedDistance = currentDistance;
}
void CameraXAdjust()
{
if (cameraState != CameraState.CameraRotate)
{
if (camXAdjust)
{
rotationXSpeed += Time.deltaTime * cameraAdjustSpeed;
if (Mathf.Abs(panAngle) > rotationXCushion)
currentPan = Mathf.Lerp(currentPan, currentPan + panAngle, rotationXSpeed);
else
camXAdjust = false;
}
else
{
if (rotationXSpeed > 0)
rotationXSpeed = 0;
currentPan = player.transform.eulerAngles.y;
}
}
}
void CameraYAdjust()
{
if (cameraState == CameraState.CameraNone)
{
if (camYAdjust)
{
rotationYSpeed += (Time.deltaTime / 2) * cameraAdjustSpeed;
if (currentTilt >= yRotMax || currentTilt <= yRotMin)
currentTilt = Mathf.Lerp(currentTilt, yRotMax / 2, rotationYSpeed);
else if (currentTilt < yRotMax && currentTilt > yRotMin)
camYAdjust = false;
}
else
{
if (rotationYSpeed > 0)
rotationYSpeed = 0;
}
}
}
void CameraNeverAdjust()
{
switch (cameraState)
{
case CameraState.CameraSteer:
if (panOffset != 0)
panOffset = 0;
currentPan = player.transform.eulerAngles.y;
break;
case CameraState.CameraNone:
currentPan = player.transform.eulerAngles.y - panOffset;
break;
case CameraState.CameraRotate:
panOffset = panAngle;
break;
}
}
public enum CameraState
{
CameraNone,
CameraRotate,
CameraSteer
}
public enum CameraMoveState
{
OnlyWhileMoving,
OnlyHorizontalWhileMoving,
AlwaysAdjust,
NeverAdjust
}
public void OnDisabled()
{
Cursor.visible = true;
Cursor.lockState = CursorLockMode.None;
enabled = false;
}
}
As you can see the camera has a lot of features. I been working on it a very long time.