Hey,
So I am making a C# version of the third person controller included in the Lerpz tutorial pack, and I am running into a slight problem where when moving forward/right/left, a small error is introduced into the movement vector, creating a curved movement, when all I want is a straight-ahead movement. Interestingly, no error is introduced when moving backwards. What would cause this slight error in the movement vectors? Any help would be appreciated!
Edit:
After some further testing, I believe the SpringFollowing Camera is introducing the error (passed as the cameraTransform in UpdateSmoothedMovementDirection in the ThirdPersonController), as whenever I hold down “fire2” to snap the camera (snapping every update), the character moves in the correct direction. This leads me to believe that the ApplyPositionDamping function is what is causing the error, as it is called only when ApplySnapping is not called. Script below
using UnityEngine;
using System.Collections;
[AddComponentMenu(“Third Person Camera/Spring Follow Camera”)]
public class SpringFollowCamera : MonoBehaviour
{
public Transform target;
public float distance = 4.0f;
public float height = 1.0f;
public float smoothLag = 0.2f;
public float maxSpeed = 10.0f;
public float snapLag = 0.3f;
public float clampHeadPositionScreenSpace = 0.75f;
public LayerMask lineOfSightMask = 0;
private bool isSnapping = false;
private Vector3 headOffset = Vector3.zero;
private Vector3 centerOffset = Vector3.zero;
private ThirdPersonController controller;
private Vector3 velocity = Vector3.zero;
private float targetHeight = 100000.0f;
void Awake()
{
CharacterController characterController = (CharacterController)target.collider;
if(characterController)
{
centerOffset = characterController.bounds.center - target.position;
headOffset = centerOffset;
headOffset.y = characterController.bounds.max.y - target.position.y;
}
if(target)
{
controller = target.GetComponent<ThirdPersonController>();
}
if(!controller)
{
Debug.Log("Assing a target to the camera with a Third person Controller");
}
}
void LateUpdate()
{
Vector3 targetCenter = target.position + centerOffset;
Vector3 targetHead = target.position + headOffset;
if(controller.IsJumping())
{
//Only move the camera upwards if the target is very high
float newTargetHeight = targetCenter.y + height;
if((newTargetHeight < targetHeight) || (newTargetHeight - targetHeight >5))
{
targetHeight = targetCenter.y + height;
}
}
//Always update target height while walking
else
{
targetHeight = targetCenter.y + height;
}
//Snap when user presses Fire2
if(Input.GetButton("Fire2") && !isSnapping)
{
velocity = Vector3.zero;
isSnapping = true;
}
if(isSnapping)
{
ApplySnapping(targetCenter);
}
else
{
ApplyPositionDamping(new Vector3(targetCenter.x, targetHeight, targetCenter.z));
}
SetUpRotation(targetCenter, targetHead);
}
void ApplySnapping(Vector3 targetCenter)
{
Vector3 position = transform.position;
Vector3 offset = position - targetCenter;
offset.y = 0;
float currentDistance = offset.magnitude;
float targetAngle = target.eulerAngles.y;
float currentAngle = transform.eulerAngles.y;
currentAngle = Mathf.SmoothDampAngle(currentAngle, targetAngle,ref velocity.x, snapLag);
currentDistance = Mathf.SmoothDamp(currentDistance, distance,ref velocity.z, snapLag);
Vector3 newPosition = targetCenter;
newPosition += Quaternion.Euler(0, currentAngle, 0) * Vector3.back * currentDistance;
newPosition.y = Mathf.SmoothDamp(position.y, targetCenter.y + height,ref velocity.y, smoothLag, maxSpeed);
newPosition = AdjustLineOfSight(newPosition, targetCenter);
transform.position = newPosition;
//Stop snapping, close to target
if(AngleDistance(currentAngle, targetAngle) < 3.0f)
{
isSnapping = false;
velocity = Vector3.zero;
}
}
public Vector3 AdjustLineOfSight(Vector3 newPosition, Vector3 target)
{
RaycastHit hit;
if(Physics.Linecast(target, newPosition,out hit, lineOfSightMask.value))
{
velocity = Vector3.zero;
return hit.point;
}
return newPosition;
}
void ApplyPositionDamping(Vector3 targetCenter)
{
//Attempt to maintain a constant distance on x-z plane
//Y position is handled with separate sping
Vector3 position = transform.position;
Vector3 offset = position - targetCenter;
offset.y = 0;
Vector3 newTargetPos = offset.normalized * distance + targetCenter;
Vector3 newPosition;
newPosition.x = Mathf.SmoothDamp(position.x, newTargetPos.x, ref velocity.x, smoothLag, maxSpeed);
newPosition.y = Mathf.SmoothDamp(position.y, targetCenter.y, ref velocity.y, smoothLag, maxSpeed);
newPosition.z = Mathf.SmoothDamp(position.z, newTargetPos.z, ref velocity.z, smoothLag, maxSpeed);
newPosition = AdjustLineOfSight(newPosition, targetCenter);
Debug.Log("New Position:" + newPosition);
transform.position = newPosition;
}
void SetUpRotation(Vector3 centerPos, Vector3 headPos)
{
//Find the rotation around the y axis.
//When grounded, center camera
//When jumping, keep the camera rotation, but rotate the camera if character out of view
//When landing, smoothly interpolate towards centering character
Vector3 cameraPos = transform.position;
Vector3 offsetToCenter = centerPos-cameraPos;
//Generate rotation around y axis
Quaternion yRotation = Quaternion.LookRotation(new Vector3(offsetToCenter.x, 0, offsetToCenter.z));
Vector3 relativeOffset = Vector3.forward * distance + Vector3.down * height;
transform.rotation = yRotation * Quaternion.LookRotation(relativeOffset);
//Calculate the projected center position and top position
Ray centerRay = camera.ViewportPointToRay(new Vector3(0.5f, 0.5f, 1));
Ray topRay = camera.ViewportPointToRay(new Vector3(0.5f, clampHeadPositionScreenSpace, 1));
Vector3 centerRayPos = centerRay.GetPoint(distance);
Vector3 topRayPos = topRay.GetPoint(distance);
float centerToTopAngle = Vector3.Angle(centerRay.direction, topRay.direction);
float heightToAngle = centerToTopAngle / (centerRayPos.y - topRayPos.y);
float extraLookAngle = heightToAngle * (centerRayPos.y - centerPos.y);
if(extraLookAngle < centerToTopAngle)
{
extraLookAngle = 0;
}
else
{
extraLookAngle = extraLookAngle - centerToTopAngle;
transform.rotation *= Quaternion.Euler(-extraLookAngle, 0, 0);
}
}
float AngleDistance(float a, float b)
{
a = Mathf.Repeat(a, 360f);
b = Mathf.Repeat(b, 360f);
return Mathf.Abs(b - a);
}
}