Add radar blip system: Radar spawner + directional RadarTarget
Radar.Spawn(transform, lifetime) clones the "Target Template", names it "Target" and attaches a RadarTarget that self-destructs after lifetime. RadarTarget aims each frame toward camera->target (full 3D LookRotation), drives the point renderer's _BaseColor from a forward-bearing gradient (ahead/side/behind depth cue lost in the flat disc projection) via a MaterialPropertyBlock, and scales the point renderer transform by camera->target distance. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
e571375cab
commit
f5de233e0c
4 files changed
+160
No files matched your search
@@ -0,0 +1,56 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sits on the "Radar" overlay object (a disc rendered by the Radar Camera in front of the main camera).
|
||||||
|
/// Spawns "Target" blips - clones of the child "Target Template" - each of which continuously points from
|
||||||
|
/// the reference camera toward a world-space transform (visualising the bearing camera -> target) and then
|
||||||
|
/// destroys itself after a given lifetime. See <see cref="RadarTarget"/> for the per-blip behaviour.
|
||||||
|
/// </summary>
|
||||||
|
[DisallowMultipleComponent]
|
||||||
|
public sealed class Radar : MonoBehaviour
|
||||||
|
{
|
||||||
|
[Tooltip("Template each spawned Target is cloned from. Auto-resolves to the child \"Target Template\".")]
|
||||||
|
[SerializeField] private Transform targetTemplate;
|
||||||
|
|
||||||
|
[Tooltip("Camera the direction is measured FROM (camera -> target). Auto-resolves to the Camera this radar sits under.")]
|
||||||
|
[SerializeField] private Camera referenceCamera;
|
||||||
|
|
||||||
|
private void Awake()
|
||||||
|
{
|
||||||
|
if (!targetTemplate)
|
||||||
|
{
|
||||||
|
var t = transform.Find("Target Template");
|
||||||
|
if (t) targetTemplate = t;
|
||||||
|
}
|
||||||
|
if (!referenceCamera) referenceCamera = GetComponentInParent<Camera>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Spawns a Target blip that each frame orients toward <paramref name="target"/> (from the reference
|
||||||
|
/// camera) and destroys itself after <paramref name="lifetime"/> seconds. Returns the spawned blip, or
|
||||||
|
/// null if it could not be created.
|
||||||
|
/// </summary>
|
||||||
|
public RadarTarget Spawn(Transform target, float lifetime)
|
||||||
|
{
|
||||||
|
if (!target)
|
||||||
|
{
|
||||||
|
Debug.LogWarning("[Radar] Spawn called without a target transform.", this);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!targetTemplate)
|
||||||
|
{
|
||||||
|
Debug.LogWarning("[Radar] No Target Template assigned or found.", this);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Transform clone = Instantiate(targetTemplate, targetTemplate.parent);
|
||||||
|
clone.name = "Target";
|
||||||
|
clone.localPosition = targetTemplate.localPosition;
|
||||||
|
clone.localScale = targetTemplate.localScale;
|
||||||
|
clone.gameObject.SetActive(true);
|
||||||
|
|
||||||
|
var blip = clone.GetComponent<RadarTarget>();
|
||||||
|
blip.Init(target, referenceCamera, lifetime);
|
||||||
|
return blip;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b8be89860f43b3240bb09cd9931ceaf2
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A single radar blip spawned by <see cref="Radar"/>. Each frame it orients itself so its forward (+Z, where
|
||||||
|
/// the "Point" marker sits) faces the direction from the reference camera to a world-space target, visualising
|
||||||
|
/// the bearing camera -> target. Tracks continuously (the camera turns / the target moves) and self-destructs
|
||||||
|
/// once its lifetime elapses - or earlier if its target is destroyed. The point's base colour and size encode
|
||||||
|
/// the two things the flat radar disc can't show via position alone: <see cref="forwardGradient"/> tints it by
|
||||||
|
/// how far the target sits ahead of / behind the camera, and its scale shrinks with camera -> target distance.
|
||||||
|
/// </summary>
|
||||||
|
[DisallowMultipleComponent]
|
||||||
|
public sealed class RadarTarget : MonoBehaviour
|
||||||
|
{
|
||||||
|
public Renderer pointRenderer;
|
||||||
|
|
||||||
|
[Tooltip("Point tint by bearing: sampled at 0 when the target is dead ahead of the camera, at 1 when directly behind. Flip the keys for the reverse.")]
|
||||||
|
[SerializeField] Gradient forwardGradient;
|
||||||
|
[Tooltip("Point transform scale at the near (x) and far (y) end of pointDistanceRange.")]
|
||||||
|
[SerializeField] Vector2 pointDistanceScale;
|
||||||
|
[Tooltip("camera -> target distance mapped onto pointDistanceScale: x = near distance, y = far distance.")]
|
||||||
|
[SerializeField] Vector2 pointDistanceRange;
|
||||||
|
|
||||||
|
private Transform _target;
|
||||||
|
private Camera _camera;
|
||||||
|
private float _lifetime;
|
||||||
|
private float _age;
|
||||||
|
private bool _initialised;
|
||||||
|
|
||||||
|
private MaterialPropertyBlock _mpb;
|
||||||
|
private static readonly int BaseColorId = Shader.PropertyToID("_BaseColor");
|
||||||
|
|
||||||
|
public Transform Target => _target;
|
||||||
|
public float RemainingLifetime => Mathf.Max(0f, _lifetime - _age);
|
||||||
|
|
||||||
|
public void Init(Transform target, Camera referenceCamera, float lifetime)
|
||||||
|
{
|
||||||
|
_target = target;
|
||||||
|
_camera = referenceCamera;
|
||||||
|
_lifetime = lifetime;
|
||||||
|
_age = 0f;
|
||||||
|
_initialised = true;
|
||||||
|
Aim();
|
||||||
|
}
|
||||||
|
|
||||||
|
// LateUpdate so the aim uses the camera's final position/rotation for this frame.
|
||||||
|
private void LateUpdate()
|
||||||
|
{
|
||||||
|
if (!_initialised) return;
|
||||||
|
|
||||||
|
_age += Time.deltaTime;
|
||||||
|
if (_age >= _lifetime)
|
||||||
|
{
|
||||||
|
Destroy(gameObject);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The world target may despawn (e.g. a caught hider) before this blip expires - drop it, don't dangle.
|
||||||
|
if (!_target)
|
||||||
|
{
|
||||||
|
Destroy(gameObject);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Aim();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Aim()
|
||||||
|
{
|
||||||
|
if (!_target || !_camera) return;
|
||||||
|
Vector3 dir = _target.position - _camera.transform.position;
|
||||||
|
float sqr = dir.sqrMagnitude;
|
||||||
|
if (sqr < 1e-8f) return; // camera sitting on the target -> keep the last orientation/colour/scale
|
||||||
|
|
||||||
|
transform.rotation = Quaternion.LookRotation(dir);
|
||||||
|
|
||||||
|
float distance = Mathf.Sqrt(sqr);
|
||||||
|
ApplyDistanceScale(distance);
|
||||||
|
ApplyForwardColor(dir / distance);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shrinks the blip with distance: pointDistanceRange (near, far) remapped onto pointDistanceScale (near, far), clamped.
|
||||||
|
private void ApplyDistanceScale(float distance)
|
||||||
|
{
|
||||||
|
if (!pointRenderer) return;
|
||||||
|
float t = Mathf.InverseLerp(pointDistanceRange.x, pointDistanceRange.y, distance);
|
||||||
|
float s = Mathf.Lerp(pointDistanceScale.x, pointDistanceScale.y, t);
|
||||||
|
pointRenderer.transform.localScale = new Vector3(s, s, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tints the point's base colour by how far the target sits ahead of (0) vs directly behind (1) the camera.
|
||||||
|
private void ApplyForwardColor(Vector3 dirNormalized)
|
||||||
|
{
|
||||||
|
if (!pointRenderer || forwardGradient == null) return;
|
||||||
|
float t = Vector3.Angle(_camera.transform.forward, dirNormalized) / 180f;
|
||||||
|
_mpb ??= new MaterialPropertyBlock();
|
||||||
|
pointRenderer.GetPropertyBlock(_mpb);
|
||||||
|
_mpb.SetColor(BaseColorId, forwardGradient.Evaluate(t));
|
||||||
|
pointRenderer.SetPropertyBlock(_mpb);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b4a573fc0fda31d42aec388198183b81
|
||||||
Reference in new issue
Block a user