- Home /
Potential broad discussion topic
Can someone help me understand this code.
I have been reading up on the Scripting Reference and using a calculator to see why some of these equations are useful once they are run through. I still can't fully understand how it is working. I know basic C# Syntax and I understand some of what it all does. I feel more like I am putting together code that I have previously seen do what I want, without actually understanding the code. Below is a some camera code I nabbed from a forum thread and have been trying to reverse engineer it to be able to write my own variant. Much Appreciated if you can help me out.
EDIT: I understand what the variables are doing, I understand what the different updates are accomplishing and so forth. I don't understand how the equations and if statements inside LateUpdate are accomplishing an orbiting "MMO Like" camera. I have a broad idea as to what each bit of code does, but what is actually doing escapes me. Vectors, Quaternions, Mathf functions and so forth are a tad abstract to me. I included the entire script so that if anyone wants to explain they can use the original variable names and so forth. Thank You
using UnityEngine;
using System.Collections;
public class WowCamera : MonoBehaviour
{
public Transform target;
public float targetHeight = 1.7f;
public float distance = 5.0f;
public float offsetFromWall = 0.1f;
public float maxDistance = 20;
public float minDistance = .6f;
public float xSpeed = 200.0f;
public float ySpeed = 200.0f;
public int yMinLimit = -80;
public int yMaxLimit = 80;
public int zoomRate = 40;
public float rotationDampening = 3.0f;
public float zoomDampening = 5.0f;
public LayerMask collisionLayers = -1;
private float xDeg = 0.0f;
private float yDeg = 0.0f;
private float currentDistance;
private float desiredDistance;
private float correctedDistance;
void Start ()
{
Vector3 angles = transform.eulerAngles;
xDeg = angles.x;
yDeg = angles.y;
currentDistance = distance;
desiredDistance = distance;
correctedDistance = distance;
// Make the rigid body not change rotation
if (rigidbody)
rigidbody.freezeRotation = true;
}
/**
* Camera logic on LateUpdate to only update after all character movement logic has been handled.
*/
void LateUpdate ()
{
Vector3 vTargetOffset;
// Don't do anything if target is not defined
if (!target)
return;
// If either mouse buttons are down, let the mouse govern camera position
if (Input.GetMouseButton(0) || Input.GetMouseButton(1))
{
xDeg += Input.GetAxis ("Mouse X") * xSpeed * 0.02f;
yDeg -= Input.GetAxis ("Mouse Y") * ySpeed * 0.02f;
}
// otherwise, ease behind the target if any of the directional keys are pressed
else if (Input.GetAxis("Vertical") != 0 || Input.GetAxis("Horizontal") != 0)
{
float targetRotationAngle = target.eulerAngles.y;
float currentRotationAngle = transform.eulerAngles.y;
xDeg = Mathf.LerpAngle (currentRotationAngle, targetRotationAngle, rotationDampening * Time.deltaTime);
}
yDeg = ClampAngle (yDeg, yMinLimit, yMaxLimit);
// set camera rotation
Quaternion rotation = Quaternion.Euler (yDeg, xDeg, 0);
// calculate the desired distance
desiredDistance -= Input.GetAxis ("Mouse ScrollWheel") * Time.deltaTime * zoomRate * Mathf.Abs (desiredDistance);
desiredDistance = Mathf.Clamp (desiredDistance, minDistance, maxDistance);
correctedDistance = desiredDistance;
// calculate desired camera position
vTargetOffset = new Vector3 (0, -targetHeight, 0);
Vector3 position = target.position - (rotation * Vector3.forward * desiredDistance + vTargetOffset);
// check for collision using the true target's desired registration point as set by user using height
RaycastHit collisionHit;
Vector3 trueTargetPosition = new Vector3 (target.position.x, target.position.y + targetHeight, target.position.z);
// if there was a collision, correct the camera position and calculate the corrected distance
bool isCorrected = false;
if (Physics.Linecast (trueTargetPosition, position, out collisionHit, collisionLayers.value))
{
// calculate the distance from the original estimated position to the collision location,
// subtracting out a safety "offset" distance from the object we hit. The offset will help
// keep the camera from being right on top of the surface we hit, which usually shows up as
// the surface geometry getting partially clipped by the camera's front clipping plane.
correctedDistance = Vector3.Distance (trueTargetPosition, collisionHit.point) - offsetFromWall;
isCorrected = true;
}
// For smoothing, lerp distance only if either distance wasn't corrected, or correctedDistance is more than currentDistance
currentDistance = !isCorrected || correctedDistance > currentDistance ? Mathf.Lerp (currentDistance, correctedDistance, Time.deltaTime * zoomDampening) : correctedDistance;
// keep within legal limits
currentDistance = Mathf.Clamp (currentDistance, minDistance, maxDistance);
// recalculate position based on the new currentDistance
position = target.position - (rotation * Vector3.forward * currentDistance + vTargetOffset);
transform.rotation = rotation;
transform.position = position;
}
private static float ClampAngle (float angle, float min, float max)
{
if (angle < -360)
angle += 360;
if (angle > 360)
angle -= 360;
return Mathf.Clamp (angle, min, max);
}
}
Is there a specific question in there? You already have a ton of comments. You need to isolate exactly where are you confused and rephrase the question that specific part of the script.
Updated my post to further narrow in what my concerns are. Thanks
Answer by robertbu · May 26, 2013 at 05:28 AM
I am heartened that someone wants to understand some code rather than have someone solve their problem for them (as so often happens on UA). On the other hand, UA is not setup for a discussion, which this question could turn into. In general discussions and broad how-too-like-topics similar this one belong on Unity Forums. So I'm going to take middle ground and add some addition comments to the LateUpdate() portion of the code and then close this answer.
void LateUpdate ()
{
Vector3 vTargetOffset;
// Don't do anything if target is not defined because if we do, we will generate null reference
// exceptions
if (!target)
return;
// If either mouse buttons are down, let the mouse govern camera position
if (Input.GetMouseButton(0) || Input.GetMouseButton(1))
{
// Get Input.GetAxis() will return values in the range of -1 to 1. xDeg is the current
// 'x' rotation based on the incremental 'X' mouse movemment. This is a bit confusing
// since our 'x' mouse movement will control the rotation around the 'y' axis, and the 'y'
// movement will control rotation around the 'x' axis, just as in the real world if
// we were to spipe a cylinder from left to right we would cause it to rotate on the
// 'y' axis. I probably would have swapped the names of xDeg and yDeg.
xDeg += Input.GetAxis ("Mouse X") * xSpeed * 0.02f;
yDeg -= Input.GetAxis ("Mouse Y") * ySpeed * 0.02f;
}
// otherwise, ease behind the target if any of the directional keys are pressed
else if (Input.GetAxis("Vertical") != 0 || Input.GetAxis("Horizontal") != 0)
{
// We want to match the rotation of the camera to the rotation of target.
// The desired position is behind the target, so when the camera
// is in the desired rotation and position, it will be looking from the
// back of the character facing the character.
float targetRotationAngle = target.eulerAngles.y;
float currentRotationAngle = transform.eulerAngles.y;
// This is a common but unintuitive and unconventional use of Lerp(). It takes approximately the
// same small percentage bite towards the target rotation each frame. As the current and and
// target become closer together, the same percentage is a smaller angle. The result is an
// eased movement. This line helps ease the camera from the current rotation to the
// target/desired rotation over time.
xDeg = Mathf.LerpAngle (currentRotationAngle, targetRotationAngle, rotationDampening * Time.deltaTime);
}
// Keeps the rotation around the 'x' axis within specified limits.
yDeg = ClampAngle (yDeg, yMinLimit, yMaxLimit);
// Set camera rotation. Note the use of 'yDeg' for the 'x' component, and 'xDeg' for the 'y' component
// as mentioned in the mouse movement code above.
Quaternion rotation = Quaternion.Euler (yDeg, xDeg, 0);
// calculate the desired distance - The mouse scroll wheel will increase or decrease the desired distance
// of the camera from the character. But that distance is clamped to the specified limits
desiredDistance -= Input.GetAxis ("Mouse ScrollWheel") * Time.deltaTime * zoomRate * Mathf.Abs (desiredDistance);
desiredDistance = Mathf.Clamp (desiredDistance, minDistance, maxDistance);
correctedDistance = desiredDistance;
// Note the desired position of the camera is at the top of the head of the character.
// The value is negated because the next line of code negates it again.
vTargetOffset = new Vector3 (0, -targetHeight, 0);
// This calculates a vector from the back of the target the desired distance behind the
// character and shifted up to the head. Actually it calculates a vector from the front
// down toward the feet, and then turns that vector around so that it is going out the
// back and up.
Vector3 position = target.position - (rotation * Vector3.forward * desiredDistance + vTargetOffset);
// check for collision using the true target's desired registration point as set by user using height
RaycastHit collisionHit;
Vector3 trueTargetPosition = new Vector3 (target.position.x, target.position.y + targetHeight, target.position.z);
// The Linecast() is to detect anything like walls between the desired position of the camera and the
// target position. If it find an object, it brings the camera closer to the target so that the camera
// does not end up inside walls and (therefore wold clip out sections of the walls).
// if there was a collision, correct the camera position and calculate the corrected distance
bool isCorrected = false;
if (Physics.Linecast (trueTargetPosition, position, out collisionHit, collisionLayers.value))
{
// calculate the distance from the original estimated position to the collision location,
// subtracting out a safety "offset" distance from the object we hit. The offset will help
// keep the camera from being right on top of the surface we hit, which usually shows up as
// the surface geometry getting partially clipped by the camera's front clipping plane.
correctedDistance = Vector3.Distance (trueTargetPosition, collisionHit.point) - offsetFromWall;
isCorrected = true;
}
// For smoothing, lerp distance only if either distance wasn't corrected, or correctedDistance is more than currentDistance
currentDistance = !isCorrected || correctedDistance > currentDistance ? Mathf.Lerp (currentDistance, correctedDistance, Time.deltaTime * zoomDampening) : correctedDistance;
// The preceeding line of code can be rewritten as follows (which should be easier to understand):
// if (!isCorrected || correctedDistance > currentDistance)
// currentDistance = Mathf.Lerp (currentDistance, correctedDistance, Time.deltaTime * zoomDampening);
// else
// currentDistance = correctedDistance;
// Note the Lerp() here brings the camera to the correctedDistance over time and is similer to the
// LerpAngle() above in that it is an eased movement.
// keep the distance within specified limits
currentDistance = Mathf.Clamp (currentDistance, minDistance, maxDistance);
// recalculate position based on the new currentDistance
// This is the same calculation we saw just before the Linecast() only using
// currentDistance instead of desiredDistance
position = target.position - (rotation * Vector3.forward * currentDistance + vTargetOffset);
transform.rotation = rotation;
transform.position = position;
}
Wow, thanks. I think I am starting to grasp a bit more of what it's actually doing. If I have any more $$anonymous$$d boggling code ($$anonymous$$d boggling to me) I will be sure to be very specific and try not to present it like this. Thank You!
Follow this Question
Related Questions
Multiple Cars not working 1 Answer
Implementing Scoring System 2 Answers
Why is my camera switching angles? 0 Answers
Functions via camera rotations... 1 Answer
UDP implementation in Unity, unable to send to two different machines 0 Answers