Skip to content

Instantly share code, notes, and snippets.

@frarees
Last active October 21, 2020 09:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save frarees/1e5ba5202f1e51f33456 to your computer and use it in GitHub Desktop.
Save frarees/1e5ba5202f1e51f33456 to your computer and use it in GitHub Desktop.
Follow a (Rect)Transform
using UnityEngine;
public class Follow : MonoBehaviour
{
[SerializeField] Camera m_Camera;
[SerializeField] Transform m_Target;
[SerializeField] Vector3 m_WorldOffset;
[SerializeField] Vector3 m_ScreenOffset;
[SerializeField, Tooltip ("Will not update depth/Z")] bool m_KeepDepth;
[SerializeField, Tooltip ("Won't work if Keep Depth is ON")] float m_DepthOffset;
Canvas m_Canvas;
/// <remarks>
/// Note that if target reparents and its canvas gets changed
/// this script won't notice and could stop positioning properly.
/// </remarks>
Canvas m_TargetCanvas;
Vector3 m_ScreenPos;
Vector3 m_WorldPos;
public Transform target {
get {
return m_Target;
} set {
m_Target = value;
UpdateTargetCanvas ();
}
}
new public Camera camera {
get {
return m_Camera ? m_Camera : Camera.main;
} set {
m_Camera = value;
}
}
public Vector3 worldOffset {
get {
return m_WorldOffset;
} set {
m_WorldOffset = value;
}
}
public Vector2 screenOffset {
get {
return m_ScreenOffset;
} set {
m_ScreenOffset = value;
}
}
void UpdateCanvas ()
{
m_Canvas = GetComponentInParent<Canvas> ();
}
void UpdateTargetCanvas ()
{
m_TargetCanvas = m_Target ? m_Target.GetComponentInParent<Canvas> () : null;
}
void Awake ()
{
UpdateCanvas ();
UpdateTargetCanvas ();
}
void OnValidate ()
{
UpdateTargetCanvas ();
}
void OnTransformParentChanged ()
{
UpdateCanvas ();
}
void OnEnable ()
{
Update ();
}
void Update ()
{
if (!camera)
return;
if (!m_Target)
return;
Vector3 pos = transform.position;
if (m_TargetCanvas) {
m_ScreenPos = RectTransformUtility.WorldToScreenPoint (GetCanvasCamera (m_TargetCanvas), m_Target.position + m_WorldOffset);
m_ScreenPos += m_ScreenOffset;
if (RectTransformUtility.ScreenPointToWorldPointInRectangle (
m_TargetCanvas.transform as RectTransform,
m_ScreenPos,
GetCanvasCamera (m_TargetCanvas),
out m_WorldPos)) {
if (m_KeepDepth)
m_WorldPos.z = pos.z;
else
m_WorldPos.z = m_Target.position.z + m_DepthOffset;
transform.position = m_WorldPos;
}
} else if (m_Canvas) {
m_ScreenPos = RectTransformUtility.WorldToScreenPoint (camera, m_Target.position + m_WorldOffset);
m_ScreenPos += m_ScreenOffset;
if (RectTransformUtility.ScreenPointToWorldPointInRectangle (
m_Canvas.transform as RectTransform,
m_ScreenPos,
GetCanvasCamera (m_Canvas),
out m_WorldPos)) {
if (m_KeepDepth)
m_WorldPos.z = pos.z;
else
m_WorldPos.z = m_Target.position.z + m_DepthOffset;
transform.position = m_WorldPos;
}
} else {
m_ScreenPos = camera.WorldToScreenPoint (m_Target.position + m_WorldOffset);
m_ScreenPos += m_ScreenOffset;
m_WorldPos = camera.ScreenToWorldPoint (m_ScreenPos);
if (m_KeepDepth)
m_WorldPos.z = pos.z;
else
m_WorldPos.z = m_Target.position.z + m_DepthOffset;
transform.position = camera.ScreenToWorldPoint (m_ScreenPos);
}
}
Camera GetCanvasCamera (Canvas canvas) {
if (!canvas)
return null;
switch (canvas.renderMode) {
case RenderMode.ScreenSpaceOverlay:
return null;
case RenderMode.ScreenSpaceCamera:
case RenderMode.WorldSpace:
return canvas.worldCamera;
default:
return null;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment