PageRenderTime 21ms CodeModel.GetById 11ms app.highlight 7ms RepoModel.GetById 1ms app.codeStats 0ms

/Scripts/SmoothFollowCamera.js

http://acid-and-base.googlecode.com/
JavaScript | 236 lines | 140 code | 52 blank | 44 comment | 14 complexity | 55d73773fdc784e581b39a6c0210a4a8 MD5 | raw file
  1// This camera is similar to the one used in Super Mario 64
  2// Edited from SmoothFollowCamera script from the 3DPlatformer Tutorial
  3
  4/*
  5
  6"Fire2" snaps the camera
  7
  8*/
  9
 10// The target we are following
 11var target : Transform;
 12
 13// The distance in the x-z plane to the target
 14var distance = 10.0;
 15
 16// the height we want the camera to be above the target
 17var height = 10.0;
 18
 19var angularSmoothLag = 0.3;
 20var angularMaxSpeed = 150.0;
 21
 22var heightSmoothLag = 0.3;
 23
 24var snapSmoothLag = 0.2;
 25var snapMaxSpeed = 7200.0;
 26
 27var clampHeadPositionScreenSpace = 0.75;
 28
 29var lockCameraTimeout = 0.0;
 30
 31private var headOffset = Vector3.zero;
 32private var centerOffset = Vector3.zero;
 33
 34private var heightVelocity = 0.0;
 35private var angleVelocity = 0.0;
 36private var snap = false;
 37private var controller : ThirdPersonController;
 38private var targetHeight = 100000.0;
 39
 40//This rotates the camera so that it's always behind the player, default off to keep that isometric feel
 41private var followRotate = false;
 42
 43function Awake ()
 44{
 45	if (target)
 46	{
 47		controller = target.GetComponent(ThirdPersonController);
 48	}
 49	
 50	if (controller)
 51	{
 52		var characterController : CharacterController = target.collider;
 53		centerOffset = characterController.bounds.center - target.position;
 54		headOffset = centerOffset;
 55		headOffset.y = characterController.bounds.max.y - target.position.y;
 56	}
 57	else
 58		Debug.Log("Please assign a target to the camera that has a ThirdPersonController script attached.");
 59
 60	
 61	Cut(target, centerOffset);
 62}
 63
 64function DebugDrawStuff ()
 65{
 66	Debug.DrawLine(target.position, target.position + headOffset);
 67
 68}
 69
 70function AngleDistance (a : float, b : float)
 71{
 72	a = Mathf.Repeat(a, 360);
 73	b = Mathf.Repeat(b, 360);
 74	
 75	return Mathf.Abs(b - a);
 76}
 77
 78function Apply (dummyTarget : Transform, dummyCenter : Vector3)
 79{
 80	// Early out if we don't have a target
 81	if (!controller)
 82		return;
 83	
 84	var targetCenter = target.position + centerOffset;
 85	var targetHead = target.position + headOffset;
 86
 87//	DebugDrawStuff();
 88
 89	// Calculate the current & target rotation angles
 90	var originalTargetAngle = target.eulerAngles.y;
 91	var currentAngle = transform.eulerAngles.y;
 92
 93	// Adjust real target angle when camera is locked
 94	var targetAngle = originalTargetAngle; 
 95	
 96	// When pressing Fire2 (alt) the camera will snap to the target direction real quick.
 97	// It will stop snapping when it reaches the target
 98	if(followRotate)
 99	{
100		if (Input.GetButton("Fire2"))
101			snap = true;
102		
103		if (snap)
104		{
105			// We are close to the target, so we can stop snapping now!
106			if (AngleDistance (currentAngle, originalTargetAngle) < 3.0)
107				snap = false;
108			
109			currentAngle = Mathf.SmoothDampAngle(currentAngle, targetAngle, angleVelocity, snapSmoothLag, snapMaxSpeed);
110		}
111		// Normal camera motion
112		else
113		{
114			if (controller.GetLockCameraTimer () < lockCameraTimeout)
115			{
116				targetAngle = currentAngle;
117			}
118
119			// Lock the camera when moving backwards!
120			// * It is really confusing to do 180 degree spins when turning around.
121			if (AngleDistance (currentAngle, targetAngle) > 160 && controller.IsMovingBackwards ())
122				targetAngle += 180;
123
124			currentAngle = Mathf.SmoothDampAngle(currentAngle, targetAngle, angleVelocity, angularSmoothLag, angularMaxSpeed);
125		}
126
127
128	}
129	// When jumping don't move camera upwards but only down!
130	if (controller.IsJumping ())
131	{
132		// We'd be moving the camera upwards, do that only if it's really high
133		var newTargetHeight = targetCenter.y + height;
134		if (newTargetHeight < targetHeight || newTargetHeight - targetHeight > 5)
135			targetHeight = targetCenter.y + height;
136	}
137	// When walking always update the target height
138	else
139	{
140		targetHeight = targetCenter.y + height;
141	}
142
143	// Damp the height
144	currentHeight = transform.position.y;
145	currentHeight = Mathf.SmoothDamp (currentHeight, targetHeight, heightVelocity, heightSmoothLag);
146
147	// Convert the angle into a rotation, by which we then reposition the camera
148	currentRotation = Quaternion.Euler (0, currentAngle, 0);
149	
150	// Set the position of the camera on the x-z plane to:
151	// distance meters behind the target
152	transform.position = targetCenter;
153	transform.position += currentRotation * Vector3.back * distance;
154
155	// Set the height of the camera
156	transform.position.y = currentHeight;
157	
158	// Always look at the target	
159	SetUpRotation(targetCenter, targetHead);
160}
161
162function LateUpdate () {
163	Apply (transform, Vector3.zero);
164}
165
166function Cut (dummyTarget : Transform, dummyCenter : Vector3)
167{
168	var oldHeightSmooth = heightSmoothLag;
169	var oldSnapMaxSpeed = snapMaxSpeed;
170	var oldSnapSmooth = snapSmoothLag;
171	
172	snapMaxSpeed = 10000;
173	snapSmoothLag = 0.001;
174	heightSmoothLag = 0.001;
175	
176	snap = true;
177	Apply (transform, Vector3.zero);
178	
179	heightSmoothLag = oldHeightSmooth;
180	snapMaxSpeed = oldSnapMaxSpeed;
181	snapSmoothLag = oldSnapSmooth;
182}
183
184function SetUpRotation (centerPos : Vector3, headPos : Vector3)
185{
186	// Now it's getting hairy. The devil is in the details here, the big issue is jumping of course.
187	// * When jumping up and down we don't want to center the guy in screen space.
188	//  This is important to give a feel for how high you jump and avoiding large camera movements.
189	//   
190	// * At the same time we dont want him to ever go out of screen and we want all rotations to be totally smooth.
191	//
192	// So here is what we will do:
193	//
194	// 1. We first find the rotation around the y axis. Thus he is always centered on the y-axis
195	// 2. When grounded we make him be centered
196	// 3. When jumping we keep the camera rotation but rotate the camera to get him back into view if his head is above some threshold
197	// 4. When landing we smoothly interpolate towards centering him on screen
198	var cameraPos = transform.position;
199	var offsetToCenter = centerPos - cameraPos;
200	
201	// Generate base rotation only around y-axis
202	var yRotation = Quaternion.LookRotation(Vector3(offsetToCenter.x, 0, offsetToCenter.z));
203
204	var relativeOffset = Vector3.forward * distance + Vector3.down * height;
205	transform.rotation = yRotation * Quaternion.LookRotation(relativeOffset);
206
207	// Calculate the projected center position and top position in world space
208	var centerRay = camera.ViewportPointToRay(Vector3(.5, 0.5, 1));
209	var topRay = camera.ViewportPointToRay(Vector3(.5, clampHeadPositionScreenSpace, 1));
210
211	var centerRayPos = centerRay.GetPoint(distance);
212	var topRayPos = topRay.GetPoint(distance);
213	
214	var centerToTopAngle = Vector3.Angle(centerRay.direction, topRay.direction);
215	
216	var heightToAngle = centerToTopAngle / (centerRayPos.y - topRayPos.y);
217
218	var extraLookAngle = heightToAngle * (centerRayPos.y - centerPos.y);
219	if (extraLookAngle < centerToTopAngle)
220	{
221		extraLookAngle = 0;
222	}
223	else
224	{
225		extraLookAngle = extraLookAngle - centerToTopAngle;
226		transform.rotation *= Quaternion.Euler(-extraLookAngle, 0, 0);
227	}
228}
229
230function GetCenterOffset ()
231{
232	return centerOffset;
233}
234
235@script AddComponentMenu ("Third Person Camera/Smooth Follow Camera")
236@script RequireComponent (Camera)