- Home /
How do I fix this jitter problem?
I have been following some tutorials on 3D Buzz to create a 3rd person camera & control system. Everything works perfectly...almost. There is functionality that is meant to make a player slide down slopes when it is too steep, but it just creates a hug amount of jitter while doing so. Here's a video example: link text. There is also jitter when going up the steep slope, but it doesn't show on the 30fps video, which has lead me to believe that it has something to do with frame-rate. The system is made of 3 .cs files so I will post them below. Any help is appreciated.
TP_Camera:
using UnityEngine;
using System.Collections;
public class TP_Camera : MonoBehaviour
{
public static TP_Camera Instance;
public Transform TargetLookAt;
public float Distance = 5.0f;
public float DistanceMin = 1.0f;
public float DistanceMax = 15.0f;
public float DistanceSmooth = 0.05f;
public float DistanceResumeSmooth = 1f;
public float X_MouseSensitivity = 5.0f;
public float Y_MouseSensitivity = 5.0f;
public float MouseWheelSensitivity = 5.0f;
public float X_Smooth = 0.05f;
public float Y_Smooth = 0.1f;
public float Y_MinLimit = -40f;
public float Y_MaxLimit = 80f;
public float OcclusionDistanceStep = 0.5f;
public int MaxOcclusionChecks = 10;
private bool isMouseVisible = true;
private bool useRightMouseToOrbitCamera = true;
private float mouseX = 0.0f;
private float mouseY = 0.0f;
private float velX = 0.0f;
private float velY = 0.0f;
private float velZ = 0.0f;
private float velDistance = 0.0f;
private float startDistance = 0.0f;
private Vector3 position = Vector3.zero;
private Vector3 desiredPosition = Vector3.zero;
private float desiredDistance = 0.0f;
private float distanceSmooth = 0f;
private float preOccludedDistance = 0;
void Awake()
{
Instance = this;
}
void Start()
{
Distance = Mathf.Clamp(Distance, DistanceMin, DistanceMax);
startDistance = Distance;
Reset();
}
void Update()
{
if (Input.GetButtonUp("ToggleMouseVisibility"))
ToggleMouseVisibility();
}
void LateUpdate()
{
if (TargetLookAt == null)
return;
HandlePlayerInput();
var count = 0;
do
{
CalculateDesiredPosition();
count++;
} while (CheckIfOccluded(count));
UpdatePosition();
}
void ToggleMouseVisibility()
{
isMouseVisible = !isMouseVisible;
if (isMouseVisible)
{
Screen.showCursor = true;
Screen.lockCursor = false;
}
else
{
Screen.showCursor = false;
Screen.lockCursor = true;
}
useRightMouseToOrbitCamera = isMouseVisible;
}
void HandlePlayerInput()
{
float deadZone = 0.01f;
if (Input.GetMouseButton(1) || useRightMouseToOrbitCamera == false)
{
// The RMB is down get mouse axis input.
mouseX += Input.GetAxis("Mouse X") * X_MouseSensitivity;
mouseY -= Input.GetAxis("Mouse Y") * Y_MouseSensitivity;
}
// This is where we will limit mouseY
mouseY = Helper.ClampAngle(mouseY, Y_MinLimit, Y_MaxLimit);
if (Input.GetAxis("Mouse ScrollWheel") < -deadZone || Input.GetAxis("Mouse ScrollWheel") > deadZone)
{
desiredDistance = Mathf.Clamp(Distance - Input.GetAxis("Mouse ScrollWheel") * MouseWheelSensitivity, DistanceMin, DistanceMax);
preOccludedDistance = desiredDistance;
distanceSmooth = DistanceSmooth;
}
}
void CalculateDesiredPosition()
{
// Evaluate distance
ResetDesiredDistance();
Distance = Mathf.SmoothDamp(Distance, desiredDistance, ref velDistance, distanceSmooth);
// Calculate desired position
desiredPosition = CalculatePosition(mouseY, mouseX, Distance);
}
Vector3 CalculatePosition(float rotationX, float rotationY, float distance)
{
Vector3 direction = new Vector3(0, 0, -distance);
Quaternion rotation = Quaternion.Euler(rotationX, rotationY, 0);
return TargetLookAt.position + rotation * direction;
}
bool CheckIfOccluded(int count)
{
var isOccluded = false;
var nearestDistance = CheckCameraPoints(TargetLookAt.position, desiredPosition);
if (nearestDistance != -1)
{
if (count < MaxOcclusionChecks)
{
isOccluded = true;
Distance -= OcclusionDistanceStep;
if (Distance < 0.25f)
Distance = 0.25f;
}
else
Distance = nearestDistance - Camera.main.nearClipPlane;
desiredDistance = Distance;
distanceSmooth = DistanceResumeSmooth;
}
return isOccluded;
}
float CheckCameraPoints(Vector3 from, Vector3 to)
{
var nearestDistance = -1f;
RaycastHit hitInfo;
Helper.ClipPlanePoints clipPlanePoints = Helper.ClipPlaneAtNear(to);
// Draw lines in the editor to make it easier to visualise
Debug.DrawLine(from, to + transform.forward * -camera.nearClipPlane, Color.red);
Debug.DrawLine(from, clipPlanePoints.UpperLeft);
Debug.DrawLine(from, clipPlanePoints.LowerLeft);
Debug.DrawLine(from, clipPlanePoints.UpperRight);
Debug.DrawLine(from, clipPlanePoints.LowerRight);
Debug.DrawLine(clipPlanePoints.UpperLeft, clipPlanePoints.UpperRight);
Debug.DrawLine(clipPlanePoints.UpperRight, clipPlanePoints.LowerRight);
Debug.DrawLine(clipPlanePoints.LowerRight, clipPlanePoints.LowerLeft);
Debug.DrawLine(clipPlanePoints.LowerLeft, clipPlanePoints.UpperLeft);
if (Physics.Linecast(from, clipPlanePoints.UpperLeft, out hitInfo) && hitInfo.collider.tag != "Player")
nearestDistance = hitInfo.distance;
if (Physics.Linecast(from, clipPlanePoints.LowerLeft, out hitInfo) && hitInfo.collider.tag != "Player")
if (hitInfo.distance < nearestDistance || nearestDistance == -1)
nearestDistance = hitInfo.distance;
if (Physics.Linecast(from, clipPlanePoints.UpperRight, out hitInfo) && hitInfo.collider.tag != "Player")
if (hitInfo.distance < nearestDistance || nearestDistance == -1)
nearestDistance = hitInfo.distance;
if (Physics.Linecast(from, clipPlanePoints.LowerRight, out hitInfo) && hitInfo.collider.tag != "Player")
if (hitInfo.distance < nearestDistance || nearestDistance == -1)
nearestDistance = hitInfo.distance;
if (Physics.Linecast(from, to + transform.forward * -camera.nearClipPlane, out hitInfo) && hitInfo.collider.tag != "Player")
if (hitInfo.distance < nearestDistance || nearestDistance == -1)
nearestDistance = hitInfo.distance;
return nearestDistance;
}
void ResetDesiredDistance()
{
if (desiredDistance < preOccludedDistance)
{
var pos = CalculatePosition(mouseY, mouseX, preOccludedDistance);
var nearestDistance = CheckCameraPoints(TargetLookAt.position, pos);
if (nearestDistance == -1 || nearestDistance > preOccludedDistance)
{
desiredDistance = preOccludedDistance;
}
}
}
void UpdatePosition()
{
float posX = Mathf.SmoothDamp(position.x, desiredPosition.x, ref velX, X_Smooth);
float posY = Mathf.SmoothDamp(position.y, desiredPosition.y, ref velY, Y_Smooth);
float posZ = Mathf.SmoothDamp(position.z, desiredPosition.z, ref velZ, X_Smooth);
position = new Vector3(posX, posY, posZ);
transform.position = position;
transform.LookAt(TargetLookAt);
}
public void Reset()
{
mouseX = 0.0f;
mouseY = 10.0f;
Distance = startDistance;
desiredDistance = Distance;
preOccludedDistance = Distance;
}
public static void UseExistingOrCreateNewMainCamera()
{
GameObject tempCamera;
GameObject targetLookat;
TP_Camera myCamera;
if (Camera.main != null)
{
tempCamera = Camera.main.gameObject;
}
else
{
tempCamera = new GameObject("Main Camera");
tempCamera.AddComponent<Camera>();
tempCamera.tag = "MainCamera";
}
tempCamera.AddComponent<TP_Camera>();
myCamera = tempCamera.GetComponent<TP_Camera>() as TP_Camera;
targetLookat = GameObject.Find("targetLookAt") as GameObject;
if (targetLookat == null)
{
targetLookat = new GameObject("targetLookAt");
targetLookat.transform.position = Vector3.zero;
}
myCamera.TargetLookAt = targetLookat.transform;
}
}
TP_Controller:
using UnityEngine;
using System.Collections;
public class TP_Controller : MonoBehaviour
{
public static CharacterController CharacterController;
public static TP_Controller Instance;
void Awake()
{
CharacterController = GetComponent<CharacterController>() as CharacterController;
Instance = this;
TP_Camera.UseExistingOrCreateNewMainCamera();
}
void Update()
{
if(Camera.main == null)
return;
GetLocomotionInput();
HandleActionInput();
TP_Motor.Instance.UpdateMotor();
}
void GetLocomotionInput()
{
float deadzone = 0.1f;
TP_Motor.Instance.VerticalVelocity = TP_Motor.Instance.MoveVector.y;
TP_Motor.Instance.MoveVector = Vector3.zero;
if(Input.GetAxis("Vertical") > deadzone || Input.GetAxis("Vertical") < -deadzone)
TP_Motor.Instance.MoveVector += new Vector3(0, 0, Input.GetAxis("Vertical"));
if(Input.GetAxis("Horizontal") > deadzone || Input.GetAxis("Horizontal") < -deadzone)
TP_Motor.Instance.MoveVector += new Vector3(Input.GetAxis("Horizontal"), 0, 0);
TP_Animator.Instance.DetermineCurrentMoveDirection();
}
void HandleActionInput()
{
if (Input.GetButton("Jump"))
{
Jump();
}
}
void Jump()
{
TP_Motor.Instance.Jump();
}
}
TP_Motor:
using UnityEngine;
using System.Collections;
public class TP_Motor : MonoBehaviour
{
public static TP_Motor Instance;
public float ForwardSpeed = 10.0f;
public float BackwardSpeed = 2.0f;
public float StrafingSpeed = 5.0f;
public float SlideSpeed = 10f;
public float JumpSpeed = 6.0f;
public float Gravity = 21.0f;
public float TerminalVelocity = 20.0f;
public float SlideThreshold = 0.6f;
public float MaxControllableSlideMagnitude = 0.4f;
private Vector3 slideDirection;
public Vector3 MoveVector { get; set; }
public float VerticalVelocity { get; set; }
void Awake()
{
Instance = this;
}
public void UpdateMotor()
{
SnapAlignCharacterWithCamera();
ProcessMotion();
}
void ProcessMotion()
{
// Transform moveVector to world space
MoveVector = transform.TransformDirection(MoveVector);
// Normalize MoveVector if magnitude > 1
if(MoveVector.magnitude > 1)
MoveVector = Vector3.Normalize(MoveVector);
// Apply sliding if applicable
ApplySlide();
// Multiply MoveVector by moveSpeed
MoveVector *= MoveSpeed();
// Reapply Vertical velocity to MoveVector.y
MoveVector = new Vector3(MoveVector.x, VerticalVelocity, MoveVector.z);
// Apply gravity
ApplyGravity();
// Move the character in world space
TP_Controller.CharacterController.Move(MoveVector * Time.deltaTime);
}
void ApplyGravity()
{
if (MoveVector.y > -TerminalVelocity)
MoveVector = new Vector3(MoveVector.x, MoveVector.y - Gravity * Time.deltaTime, MoveVector.z);
if (TP_Controller.CharacterController.isGrounded && MoveVector.y < -1)
MoveVector = new Vector3(MoveVector.x, -1, MoveVector.z);
}
void ApplySlide()
{
if (!TP_Controller.CharacterController.isGrounded)
return;
slideDirection = Vector3.zero;
RaycastHit hitInfo;
if (Physics.Raycast(transform.position + Vector3.up, Vector3.down, out hitInfo))
{
if (hitInfo.normal.y < SlideThreshold)
slideDirection = new Vector3(hitInfo.normal.x, -hitInfo.normal.y, hitInfo.normal.z);
}
if (slideDirection.magnitude < MaxControllableSlideMagnitude)
MoveVector += slideDirection;
else
{
MoveVector = slideDirection;
}
}
public void Jump()
{
if (TP_Controller.CharacterController.isGrounded)
VerticalVelocity = JumpSpeed;
}
void SnapAlignCharacterWithCamera()
{
if(MoveVector.x != 0 || MoveVector.z != 0)
{
transform.rotation = Quaternion.Euler(transform.eulerAngles.x, Camera.main.transform.eulerAngles.y, transform.eulerAngles.z);
}
}
float MoveSpeed()
{
float moveSpeed = 0.0f;
switch (TP_Animator.Instance.MoveDirection)
{
case TP_Animator.Direction.Stationary:
moveSpeed = 0.0f;
break;
case TP_Animator.Direction.Forward:
moveSpeed = ForwardSpeed;
break;
case TP_Animator.Direction.Backward:
moveSpeed = BackwardSpeed;
break;
case TP_Animator.Direction.Left:
moveSpeed = StrafingSpeed;
break;
case TP_Animator.Direction.Right:
moveSpeed = StrafingSpeed;
break;
case TP_Animator.Direction.LeftForward:
moveSpeed = ForwardSpeed;
break;
case TP_Animator.Direction.RightForward:
moveSpeed = ForwardSpeed;
break;
case TP_Animator.Direction.LeftBackward:
moveSpeed = BackwardSpeed;
break;
case TP_Animator.Direction.RightBackward:
moveSpeed = BackwardSpeed;
break;
}
if (slideDirection.magnitude > 0)
moveSpeed = SlideSpeed;
return moveSpeed;
}
}
Your answer
Follow this Question
Related Questions
Help with a simple 3rd person camera? 1 Answer
3rd person camera doesn't rotate as intended 0 Answers
Camera Help (WoW-like) 1 Answer
3rd Person Camera Problem 1 Answer
Camera collision 2 Answers