The Collider Radar detects all 3D Colliders included in the LayerMask.
- Script is ideally placed on a child object of the player.
- The player is required to have a Rigidbody for the physics events OnTriggerEnter/Exit.
- A Radar UI object, background and icon prefab must be set up for this to work. (not provided)
This example does not include pooling, and simply Creates/Destroys icons as colliders enter and exit the trigger.
using System.Collections.Generic;
using UnityEngine;
namespace Rovsau.Unity.ColliderRadar
{
[RequireComponent(typeof(SphereCollider))]
public class ColliderRadar : MonoBehaviour
{
// ToDo: Better to use one dictionary.
private Dictionary<int, Transform> _trackedObjects = new Dictionary<int, Transform>();
private Dictionary<int, RectTransform> _radarIcons = new Dictionary<int, RectTransform>();
[Header("Prefab")]
public GameObject _radarTargetIcon;
[Header("Radar Object")]
public RectTransform _radarIconContainer;
[Header("Config")]
[SerializeField] private LayerMask _radarLayers;
[SerializeField] private float _range = 20f;
[Tooltip("The additional range from which the icon will be visibly lingering at the outermost border.")]
[SerializeField] private float _rangeFalloff = 10f;
[SerializeField] private bool _applyRotation = true;
[SerializeField] private Transform _player;
private float _radarSize;
private float _radarScale;
private void Awake()
{
_player = transform;
_radarSize = _radarIconContainer.rect.width * 0.5f;
_radarScale = _radarSize / _range;
GetComponent<SphereCollider>().radius = _range + _rangeFalloff;
}
private void FixedUpdate()
{
foreach (var (key, icon) in _radarIcons)
{
icon.anchoredPosition = GetIconLocation(_trackedObjects[key]);
}
}
private void OnTriggerEnter(Collider other)
{
if (HasLayerInMask(other.gameObject.layer))
{
// Track object.
int instanceID = other.transform.GetInstanceID();
if (_trackedObjects.TryAdd(instanceID, other.transform))
{
// Create radar icon.
RectTransform icon = Instantiate(_radarTargetIcon, _radarIconContainer).GetComponent<RectTransform>();
icon.localPosition = GetIconLocation(other.transform);
icon.name = other.name;
_radarIcons.Add(instanceID, icon);
}
}
}
private void OnTriggerExit(Collider other)
{
int instanceID = other.transform.GetInstanceID();
if (_radarIcons.TryGetValue(instanceID, out RectTransform icon))
{
// Remove tracking.
_trackedObjects.Remove(instanceID);
_radarIcons.Remove(instanceID);
Destroy(icon.gameObject);
}
}
public bool HasLayerInMask(int layerIndex)
{
return (_radarLayers & (1 << layerIndex)) != 0;
}
private Vector2 GetIconLocation(Transform target)
{
// Get vector distance on a 2D plane (XZ).
Vector3 distance = target.position - _player.position;
Vector2 location = new Vector2(distance.x, distance.z) * _radarScale;
if (_applyRotation)
{
// Project player position on to an XZ plane.
Vector3 playerForwardDirectionXZ = Vector3.ProjectOnPlane(_player.forward, Vector3.up);
Quaternion rotation = Quaternion.LookRotation(playerForwardDirectionXZ);
// Flip Y axis.
rotation.eulerAngles = new Vector3(rotation.eulerAngles.x, -rotation.eulerAngles.y, rotation.eulerAngles.z);
// Rotate in 3D space.
Vector3 rotatedIconLocation = rotation * new Vector3(location.x, 0, location.y);
// Convert from 3D to 2D (XZ)
location = new Vector2(rotatedIconLocation.x, rotatedIconLocation.z);
}
// Keep within max radius.
location = Vector2.ClampMagnitude(location, _radarSize);
return location;
}
}
}