18 [Range(0.0f, 1.0f), Tooltip(
"The minimum horizontal percentage visible before the object starts tagging along.")]
19 public float MinimumHorizontalOverlap = 0.1f;
20 [Range(0.0f, 1.0f), Tooltip(
"The target horizontal percentage the Tagalong attempts to achieve.")]
21 public float TargetHorizontalOverlap = 1.0f;
22 [Range(0.0f, 1.0f), Tooltip(
"The minimum vertical percentage visible before the object starts tagging along.")]
23 public float MinimumVerticalOverlap = 0.1f;
24 [Range(0.0f, 1.0f), Tooltip(
"The target vertical percentage the Tagalong attempts to achieve.")]
25 public float TargetVerticalOverlap = 1.0f;
29 [Range(3, 11), Tooltip(
"The number of rays to cast horizontally across the Tagalong.")]
30 public int HorizontalRayCount = 3;
31 [Range(3, 11), Tooltip(
"The number of rays to cast vertically across the Tagalong.")]
32 public int VerticalRayCount = 3;
34 [Tooltip(
"Don't allow the Tagalong to come closer than this distance.")]
35 public float MinimumTagalongDistance = 1.0f;
36 [Tooltip(
"When true, the Tagalong object maintains a fixed angular size.")]
37 public bool MaintainFixedSize =
true;
39 [Tooltip(
"The speed to update the Tagalong's distance when compensating for depth (meters/second).")]
40 public float DepthUpdateSpeed = 4.0f;
42 private float defaultTagalongDistance;
46 [Tooltip(
"Set to true to draw lines of interest in Unity's scene view during play-mode.")]
47 public bool DebugDrawLines =
false;
48 [Tooltip(
"Useful for visualizing the Raycasts used for determining the depth to place the Tagalong. Set to 'None' to disable.")]
56 defaultTagalongDistance = TagalongDistance;
69 EnforceDistance =
false;
72 if (MaintainFixedSize)
82 if (!interpolator.AnimatingPosition)
88 if (AdjustTagalongDistance(
CameraCache.
Main.transform.position, out newPosition))
90 interpolator.PositionPerSecond = DepthUpdateSpeed;
91 interpolator.SetTargetPosition(newPosition);
92 TagalongDistance = Mathf.Min(defaultTagalongDistance, Vector3.Distance(
CameraCache.
Main.transform.position, newPosition));
99 bool needsToMoveX =
false;
100 bool needsToMoveY =
false;
101 toPosition = fromPosition;
105 Vector3 cameraPosition = cameraTransform.position;
108 Bounds colliderBounds = tagalongCollider.bounds;
111 Vector3 newToPosition = tagalongCollider.bounds.center;
116 Ray rayTemp =
new Ray(cameraPosition, colliderBounds.center - cameraPosition);
117 colliderBounds.center = rayTemp.GetPoint(TagalongDistance);
120 DebugDrawColliderBox(DebugDrawLines, colliderBounds);
121 #endif // UNITY_EDITOR 124 float width = tagalongCollider.size.x * transform.lossyScale.x;
125 float height = tagalongCollider.size.y * transform.lossyScale.y;
129 Plane verticalCenterPlane =
new Plane(cameraTransform.right, cameraPosition + cameraTransform.forward);
130 bool tagalongIsRightOfCenter = verticalCenterPlane.GetDistanceToPoint(colliderBounds.center) > 0;
134 Vector3 horizontalTowardCenter = tagalongIsRightOfCenter ? -transform.right : transform.right;
135 Plane verticalFrustumPlane = tagalongIsRightOfCenter ? frustumPlanes[frustumRight] : frustumPlanes[frustumLeft];
138 Vector3 centermostHorizontalEdge = colliderBounds.center + (horizontalTowardCenter * (width / 2f));
142 Vector3 targetPoint = centermostHorizontalEdge + (-horizontalTowardCenter * (width * MinimumHorizontalOverlap));
147 needsToMoveX = verticalFrustumPlane.GetDistanceToPoint(targetPoint) < 0;
148 if (needsToMoveX || DebugDrawLines)
151 Vector3 newCalculatedTargetPosition =
152 CalculateTargetPosition(
true, centermostHorizontalEdge, horizontalTowardCenter, width,
153 colliderBounds.center, verticalFrustumPlane, tagalongIsRightOfCenter);
157 newToPosition.x = newCalculatedTargetPosition.x;
158 newToPosition.z = newCalculatedTargetPosition.z;
165 colliderBounds = tagalongCollider.bounds;
166 rayTemp =
new Ray(cameraPosition, colliderBounds.center - cameraPosition);
167 colliderBounds.center = rayTemp.GetPoint(TagalongDistance);
168 Plane horizontalCenterPlane =
new Plane(cameraTransform.up, cameraPosition + cameraTransform.forward);
169 bool tagalongIsAboveCenter = horizontalCenterPlane.GetDistanceToPoint(colliderBounds.center) > 0;
170 Vector3 verticalTowardCenter = tagalongIsAboveCenter ? -transform.up : transform.up;
171 Plane horizontalFrustumPlane = tagalongIsAboveCenter ? frustumPlanes[frustumTop] : frustumPlanes[frustumBottom];
172 Vector3 centermostVerticalEdge = colliderBounds.center + (verticalTowardCenter * (height / 2f));
173 targetPoint = centermostVerticalEdge + (-verticalTowardCenter * (height * MinimumVerticalOverlap));
175 needsToMoveY = horizontalFrustumPlane.GetDistanceToPoint(targetPoint) < 0;
176 if (needsToMoveY || DebugDrawLines)
179 Vector3 newCalculatedTargetPosition =
180 CalculateTargetPosition(
false, centermostVerticalEdge, verticalTowardCenter, height,
181 colliderBounds.center, horizontalFrustumPlane, !tagalongIsAboveCenter);
184 newToPosition.y = newCalculatedTargetPosition.y;
185 newToPosition.z = newCalculatedTargetPosition.z;
189 if (needsToMoveX || needsToMoveY)
191 Ray ray =
new Ray(cameraPosition, newToPosition - cameraPosition);
192 toPosition = ray.GetPoint(TagalongDistance);
195 return needsToMoveX || needsToMoveY;
209 private Vector3 CalculateTargetPosition(
bool isHorizontal, Vector3 centermostEdge, Vector3 vectorTowardCenter,
float width,
210 Vector3 center, Plane frustumPlane,
bool invertAngle)
213 Vector3 cameraPosition = cameraTransform.position;
217 float desiredOverlap = isHorizontal
218 ? Mathf.Max(MinimumHorizontalOverlap, TargetHorizontalOverlap)
219 : Mathf.Max(MinimumVerticalOverlap, TargetVerticalOverlap);
222 Vector3 targetPoint = centermostEdge + (-vectorTowardCenter * (width * desiredOverlap));
226 Vector3 centeredPoint = cameraPosition + cameraTransform.forward * TagalongDistance;
227 Ray rayTemp =
new Ray(centeredPoint, (invertAngle ? 1 : -1) * (isHorizontal ? cameraTransform.right : cameraTransform.up));
228 float distToFrustum = 0.0f;
229 frustumPlane.Raycast(rayTemp, out distToFrustum);
230 Vector3 pointOnFrustum = rayTemp.GetPoint(distToFrustum);
235 rayTemp =
new Ray(cameraPosition, pointOnFrustum - cameraPosition);
236 float distanceToTarget = Vector3.Distance(cameraPosition, targetPoint);
237 Vector3 recalculatedPointOnFrustum = rayTemp.GetPoint(distanceToTarget);
241 Quaternion rotQuat = Quaternion.FromToRotation(targetPoint - cameraPosition, center - cameraPosition);
243 Vector3 vectorToRotate = recalculatedPointOnFrustum - cameraPosition;
245 Vector3 newCalculatedTargetPosition = cameraPosition + rotQuat * vectorToRotate;
248 DebugDrawDebuggingLines(DebugDrawLines, center, cameraPosition,
249 cameraPosition + (targetPoint - cameraPosition),
250 centeredPoint, pointOnFrustum, recalculatedPointOnFrustum,
251 newCalculatedTargetPosition);
252 #endif // UNITY_EDITOR 254 return newCalculatedTargetPosition;
257 private bool AdjustTagalongDistance(Vector3 cameraPosition, out Vector3 newPosition)
259 bool needsUpdating =
false;
262 float width = tagalongCollider.size.x * transform.lossyScale.x;
263 float height = tagalongCollider.size.y * transform.lossyScale.y;
266 Vector3 lowerLeftCorner = transform.position - (transform.right * (width / 2)) - (transform.up * (height / 2));
271 RaycastHit closestHit =
new RaycastHit();
272 float closestHitDistance =
float.PositiveInfinity;
273 RaycastHit[] allHits;
274 for (
int x = 0; x < HorizontalRayCount; x++)
276 Vector3 xCoord = lowerLeftCorner + transform.right * (x * width / (HorizontalRayCount - 1));
277 for (
int y = 0; y < VerticalRayCount; y++)
279 Vector3 targetCoord = xCoord + transform.up * (y * height / (VerticalRayCount - 1));
281 allHits = Physics.RaycastAll(cameraPosition, targetCoord - cameraPosition, defaultTagalongDistance * 1.5f);
282 for (
int h = 0; h < allHits.Length; h++)
284 if (allHits[h].distance >= MinimumTagalongDistance &&
285 allHits[h].distance < closestHitDistance &&
286 !allHits[h].transform.IsChildOf(transform))
288 closestHit = allHits[h];
289 closestHitDistance = closestHit.distance;
290 if (DebugPointLight != null)
292 Light clonedLight = Instantiate(DebugPointLight, closestHit.point, Quaternion.identity) as Light;
293 clonedLight.color = Color.red;
294 Destroy(clonedLight, 1.0f);
297 DebugDrawLine(DebugDrawLines, cameraPosition, targetCoord, Color.red);
298 #endif // UNITY_EDITOR 305 needsUpdating = closestHitDistance <
float.PositiveInfinity;
314 Vector3 cameraToTransformPosition = transform.position - cameraPosition;
315 Vector3 cameraToClosestHitPoint = closestHit.point - cameraPosition;
316 float angleBetween = Vector3.Angle(cameraToTransformPosition, cameraToClosestHitPoint);
317 closestHitDistance = closestHitDistance * Mathf.Cos(angleBetween * Mathf.Deg2Rad);
320 closestHitDistance = Mathf.Max(closestHitDistance, MinimumTagalongDistance);
322 else if (TagalongDistance != defaultTagalongDistance)
326 needsUpdating =
true;
327 closestHitDistance = defaultTagalongDistance;
330 newPosition = cameraPosition + (transform.position - cameraPosition).normalized * closestHitDistance;
331 return needsUpdating;
335 protected void DebugDrawLine(
bool draw, Vector3 start, Vector3 end)
337 DebugDrawLine(draw, start, end, Color.white);
340 protected void DebugDrawLine(
bool draw, Vector3 start, Vector3 end, Color color)
344 Debug.DrawLine(start, end, color);
353 void DebugDrawColliderBox(
bool draw, Bounds colliderBounds)
355 Vector3 extents = colliderBounds.extents;
357 Vector3 frontUpperLeft, backUpperLeft, backUpperRight, frontUpperRight;
358 frontUpperLeft = colliderBounds.center +
new Vector3(-extents.x, extents.y, -extents.z);
359 backUpperLeft = colliderBounds.center +
new Vector3(-extents.x, extents.y, extents.z);
360 backUpperRight = colliderBounds.center +
new Vector3(extents.x, extents.y, extents.z);
361 frontUpperRight = colliderBounds.center +
new Vector3(extents.x, extents.y, -extents.z);
363 DebugDrawLine(draw, frontUpperLeft, backUpperLeft, Color.blue);
364 DebugDrawLine(draw, backUpperLeft, backUpperRight, Color.red);
365 DebugDrawLine(draw, backUpperRight, frontUpperRight, Color.blue);
366 DebugDrawLine(draw, frontUpperRight, frontUpperLeft, Color.red);
368 Vector3 frontLowerLeft, backLowerLeft, backLowerRight, frontLowerRight;
369 frontLowerLeft = colliderBounds.center +
new Vector3(-extents.x, -extents.y, -extents.z);
370 backLowerLeft = colliderBounds.center +
new Vector3(-extents.x, -extents.y, extents.z);
371 backLowerRight = colliderBounds.center +
new Vector3(extents.x, -extents.y, extents.z);
372 frontLowerRight = colliderBounds.center +
new Vector3(extents.x, -extents.y, -extents.z);
374 DebugDrawLine(draw, frontLowerLeft, backLowerLeft, Color.blue);
375 DebugDrawLine(draw, backLowerLeft, backLowerRight, Color.red);
376 DebugDrawLine(draw, backLowerRight, frontLowerRight, Color.blue);
377 DebugDrawLine(draw, frontLowerRight, frontLowerLeft, Color.red);
379 DebugDrawLine(draw, frontUpperLeft, frontLowerLeft, Color.green);
380 DebugDrawLine(draw, backUpperLeft, backLowerLeft, Color.green);
381 DebugDrawLine(draw, backUpperRight, backLowerRight, Color.green);
382 DebugDrawLine(draw, frontUpperRight, frontLowerRight, Color.green);
385 void DebugDrawDebuggingLines(
bool draw, Vector3 center, Vector3 cameraPosition,
386 Vector3 cameraToTarget,
387 Vector3 centeredPoint, Vector3 pointOnFrustum, Vector3 recalculatedPointOnFrustum,
388 Vector3 calculatedPosition)
390 DebugDrawLine(draw, cameraPosition, center, Color.blue);
391 DebugDrawLine(draw, cameraPosition, cameraToTarget, Color.yellow);
392 DebugDrawLine(draw, cameraPosition, centeredPoint, Color.red);
393 DebugDrawLine(draw, centeredPoint, pointOnFrustum, Color.red);
394 DebugDrawLine(draw, cameraPosition, recalculatedPointOnFrustum, Color.red);
395 DebugDrawLine(draw, cameraPosition, calculatedPosition, Color.cyan);
397 #endif // UNITY_EDITOR