2026-04-18 13:57:19 -04:00
using System.Collections.Generic ;
2025-11-25 08:19:33 -05:00
using UnityEngine ;
#if UNITY_EDITOR
using UnityEditor ;
#endif
namespace SickscoreGames.HUDNavigationSystem
{
[AddComponentMenu (HNS.Name + "/" + HNS.Name), DisallowMultipleComponent]
public class HUDNavigationSystem : MonoBehaviour
{
private static HUDNavigationSystem _Instance ;
public static HUDNavigationSystem Instance {
get {
if ( _Instance = = null ) {
2026-04-18 13:57:19 -04:00
_Instance = HUDNavigationExtensions . FindFirst < HUDNavigationSystem > ( ) ;
2025-11-25 08:19:33 -05:00
}
return _Instance ;
}
}
#region Variables
// SYSTEM
public bool isEnabled // use the EnableSystem(bool) method to change this value.
{
get { return _isEnabled ; }
private set { _isEnabled = value ; }
}
[SerializeField] private bool _isEnabled = true ;
// REFERENCES
public Camera PlayerCamera ;
public Transform PlayerController ;
public _RotationReference RotationReference = _RotationReference . Camera ;
public _UpdateMode UpdateMode = _UpdateMode . LateUpdate ;
public bool KeepAliveOnLoad = true ;
// RADAR
[Tooltip("Enable, if you want to use the radar feature.")]
public bool useRadar = true ;
[Tooltip("Select the radar mode you want to use.")]
public RadarModes radarMode = RadarModes . RotateRadar ;
[Tooltip("Define the radar zoom. Change value to zoom the radar. Set to 1 for default radar zoom.")]
public float radarZoom = 1f ;
[Tooltip("Define the radar radius. Elements outside this radius will be displayed on the border of the radar.")]
public float radarRadius = 50f ;
[Tooltip("Define the maximum radar radius. Elements outside this radius will be hidden.")]
public float radarMaxRadius = 75f ;
[Tooltip("Enable, if you want to scale radar elements, when they're about to disappear from the radar.")]
public bool useRadarScaling = true ;
[Tooltip("Define the radar scale distance. Radar elements will be scaled, when close to the radar max radius. Must be smaller or equal to the radar max radius.")]
public float radarScaleDistance = 10f ;
[Tooltip("Minimum scale of the radar element. Set value to 1, if you don't want your element to scale.")]
public float radarMinScale = . 35f ;
[Tooltip("Enable, if you want to fade radar elements, when they're about to disappear from the radar.")]
public bool useRadarFading = true ;
[Tooltip("Define the radar fade distance. Radar elements will be faded, when close to the radar max radius. Must be smaller or equal to the radar max radius.")]
public float radarFadeDistance = 5f ;
[Tooltip("Minimum opacity of the radar elements. Set value to 0, to completely fade-out the element.")]
public float radarMinFade = 0f ;
[Tooltip("Enable, if you want to show arrows pointing upwards/downwards if the element is physically above or below a certain distance.")]
public bool useRadarHeightSystem = true ;
[Tooltip("Minimum distance upwards to activate the element's ABOVE arrow.")]
public float radarDistanceAbove = 10f ;
[Tooltip("Minimum distance downwards to activate the element's BELOW arrow.")]
public float radarDistanceBelow = 10f ;
[Tooltip("(DEBUG) Enable to show the radar's height gizmos.")]
public bool showRadarHeightGizmos = false ;
[SerializeField] protected Vector2 radarHeightGizmoSize = new Vector2 ( 100f , 100f ) ;
[SerializeField] protected Color radarHeightGizmoColor = new Color ( 0f , 0f , 1f , . 4f ) ;
// COMPASS BAR
[Tooltip("Enable, if you want to use the compass bar feature.")]
public bool useCompassBar = true ;
[Tooltip("Define the compass radius. Elements that don't ignore the radius will be hidden outside this radius.")]
public float compassBarRadius = 150f ;
// INDICATOR
[Tooltip("Enable, if you want to use the indicator feature. Must be separately enabled on each element.")]
public bool useIndicators = true ;
[Tooltip("Define the indicator radius. Indicators that don't ignore the radius will be hidden outside this radius.")]
public float indicatorRadius = 25f ;
[Tooltip("Define the distance below which the indicator will automatically be hidden. (0 = no auto-hide)")]
public float indicatorHideDistance = 3f ;
[Tooltip("Enable, if you want to use an offscreen indicator, when the element is not on screen.")]
public bool useOffscreenIndicators = true ;
[Tooltip("Increase this value to move the indicators further away from the screen borders.")]
public float indicatorOffscreenBorder = . 075f ;
[Tooltip("Enable, if you want to scale the indicator by distance and within defined radius.")]
public bool useIndicatorScaling = true ;
[Tooltip("Define the indicator scale radius. Indicator will scale when inside this radius. Must be smaller or equal to indicator radius.")]
public float indicatorScaleRadius = 15f ;
[Tooltip("Minimum scale of the indicator. Set value to 1, if you don't want your indicator to scale.")]
public float indicatorMinScale = . 8f ;
[Tooltip("Enable, if you want to fade the indicator by distance and within defined radius.")]
public bool useIndicatorFading = true ;
[Tooltip("Define the indicator fade radius. Indicator will fade when inside this radius. Must be smaller or equal to indicator radius.")]
public float indicatorFadeRadius = 15f ;
[Tooltip("Minimum opacity of the indicator. Set value to 0, to completely fade-out the indicator.")]
public float indicatorMinFade = 0f ;
// MINIMAP
[Tooltip("Enable, if you want to use the minimap feature.")]
public bool useMinimap = true ;
[Tooltip("Assign the map profile for your minimap.")]
public HNSMapProfile minimapProfile ;
[HideInInspector] public HNSMapProfile currentMinimapProfile ;
[Tooltip("Select the minimap mode you want to use.")]
public MinimapModes minimapMode = MinimapModes . RotatePlayer ;
2026-04-18 13:57:19 -04:00
[Tooltip("Select the minimap shape you want to use.")]
public MinimapShapes minimapShape = MinimapShapes . Rectangular ;
2025-11-25 08:19:33 -05:00
[Tooltip("Define the minimap scale. Change value to zoom the minimap.")]
public float minimapScale = . 25f ;
[Tooltip("Define the minimap radius. Elements will be displayed on the border of the minimap, depending on the minimap scale.")]
public float minimapRadius = 75f ;
[Tooltip("Enable, if you want to scale minimap elements, when they're about to disappear from the minimap.")]
public bool useMinimapScaling = true ;
[Tooltip("Define the minimap scale distance. Minimap elements will be scaled, when close to the minimap radius. Must be smaller or equal to the minimap radius.")]
public float minimapScaleDistance = 10f ;
[Tooltip("Minimum scale of the minimap element. Set value to 1, if you don't want your element to scale.")]
public float minimapMinScale = . 35f ;
[Tooltip("Enable, if you want to fade minimap elements, when they're about to disappear from the minimap.")]
public bool useMinimapFading = true ;
[Tooltip("Define the minimap fade distance. Minimap elements will be faded, when close to the minimap radius. Must be smaller or equal to the minimap radius.")]
public float minimapFadeDistance = 5f ;
[Tooltip("Minimum opacity of the minimap elements. Set value to 0, to completely fade-out the element.")]
public float minimapMinFade = 0f ;
[Tooltip("(DEBUG) Enable to show the minimap bounds gizmos.")]
public bool showMinimapBounds = true ;
[SerializeField] protected Color minimapBoundsGizmoColor = new Color ( 0f , 1f , 0f , . 85f ) ;
[Tooltip("Enable, if you want to show arrows pointing upwards/downwards if the element is physically above or below a certain distance.")]
public bool useMinimapHeightSystem = true ;
[Tooltip("Minimum distance upwards to activate the element's ABOVE arrow.")]
public float minimapDistanceAbove = 10f ;
[Tooltip("Minimum distance downwards to activate the element's BELOW arrow.")]
public float minimapDistanceBelow = 10f ;
[Tooltip("(DEBUG) Enable to show the minimap's height gizmos.")]
public bool showMinimapHeightGizmos = false ;
[SerializeField] protected Vector2 minimapHeightGizmoSize = new Vector2 ( 100f , 100f ) ;
[SerializeField] protected Color minimapHeightGizmoColor = new Color ( 0f , 0f , 1f , . 4f ) ;
[HideInInspector]
public List < HUDNavigationElement > NavigationElements ;
private HUDNavigationCanvas _HUDNavigationCanvas ;
#endregion
#region Main Methods
void Awake ( )
{
// destroy duplicate instances
if ( _Instance ! = null ) {
Destroy ( this . gameObject ) ;
return ;
}
// dont destroy on load
if ( KeepAliveOnLoad )
DontDestroyOnLoad ( this . gameObject ) ;
_Instance = this ;
}
protected virtual void Start ( )
{
// assign references
if ( _HUDNavigationCanvas = = null ) {
_HUDNavigationCanvas = HUDNavigationCanvas . Instance ;
// check if HUDNavigationCanvas exists
if ( _HUDNavigationCanvas = = null ) {
Debug . LogError ( "HUDNavigationCanvas not found in scene!" ) ;
this . enabled = false ;
return ;
}
}
// enable the system
EnableSystem ( isEnabled ) ;
// init all components
InitAllComponents ( ) ;
}
void Update ( )
{
if ( UpdateMode = = _UpdateMode . Update )
UpdateAllComponents ( ) ;
}
void LateUpdate ( )
{
if ( UpdateMode = = _UpdateMode . LateUpdate )
UpdateAllComponents ( ) ;
}
/// <summary>
/// Enable / Disable the entire system at runtime.
/// </summary>
/// <param name="value">value</param>
public void EnableSystem ( bool value )
{
// enable/disable system
isEnabled = value ;
// enable/disable HNS canvas if value has changed
if ( _HUDNavigationCanvas ! = null & & isEnabled ! = _HUDNavigationCanvas . isEnabled )
_HUDNavigationCanvas . EnableCanvas ( isEnabled ) ;
// try to automatically assign the player camera and transform if possible
if ( PlayerCamera = = null ) {
2026-04-18 13:57:19 -04:00
HNSPlayerCamera hnsCamera = HUDNavigationExtensions . FindFirst < HNSPlayerCamera > ( ) ;
2025-11-25 08:19:33 -05:00
if ( hnsCamera ! = null )
PlayerCamera = hnsCamera . GetComponent < Camera > ( ) ;
else
PlayerCamera = Camera . main ;
if ( PlayerCamera = = null )
Debug . LogError ( "[HUDNavigationSystem] Player camera unassigned. Assign camera to resume system!" ) ;
}
if ( PlayerController = = null ) {
2026-04-18 13:57:19 -04:00
HNSPlayerController hnsTransform = HUDNavigationExtensions . FindFirst < HNSPlayerController > ( ) ;
2025-11-25 08:19:33 -05:00
if ( hnsTransform ! = null )
PlayerController = hnsTransform . gameObject . transform ;
if ( PlayerController = = null )
Debug . LogError ( "[HUDNavigationSystem] Player transform unassigned. Assign transform to resume system!" ) ;
}
// refresh references
RefreshPlayerReferences ( ) ;
}
2026-04-18 13:57:19 -04:00
/// <summary>
/// Change the currently assigned canvas at runtime.
/// </summary>
/// <param name="hnsCanvas">HUD Navigation Canvas.</param>
public void ChangeCanvas ( HUDNavigationCanvas hnsCanvas )
{
if ( hnsCanvas = = null )
return ;
// assign new instance
_HUDNavigationCanvas = hnsCanvas ;
// re-init all components
InitAllComponents ( ) ;
}
2025-11-25 08:19:33 -05:00
/// <summary>
/// Change the currently assigned player camera at runtime.
/// </summary>
/// <param name="playerCamera">Player Camera.</param>
public void ChangePlayerCamera ( Camera playerCamera )
{
if ( playerCamera = = null | | playerCamera = = PlayerCamera )
return ;
PlayerCamera = playerCamera ;
RefreshPlayerReferences ( ) ;
}
/// <summary>
/// Change the currently assigned player controller at runtime.
/// </summary>
/// <param name="playerController">Player Controller.</param>
public void ChangePlayerController ( Transform playerController )
{
if ( playerController = = null | | playerController = = PlayerController )
return ;
PlayerController = playerController ;
RefreshPlayerReferences ( ) ;
}
/// <summary>
/// Add a navigation element to the collection.
/// </summary>
/// <param name="element">Element.</param>
public void AddNavigationElement ( HUDNavigationElement element )
{
if ( element = = null )
return ;
// add element, if it doesn't exist yet
if ( ! NavigationElements . Contains ( element ) )
NavigationElements . Add ( element ) ;
}
/// <summary>
/// Remove a navigation element from the collection.
/// </summary>
/// <param name="element">Element.</param>
public void RemoveNavigationElement ( HUDNavigationElement element )
{
if ( element = = null )
return ;
// remove element from list
NavigationElements . Remove ( element ) ;
}
/// <summary>
/// Enable / Disable the radar feature at runtime.
/// </summary>
/// <param name="value">value</param>
public void EnableRadar ( bool value )
{
if ( useRadar ! = value ) {
useRadar = value ;
_HUDNavigationCanvas . ShowRadar ( value ) ;
}
}
/// <summary>
/// Enable / Disable the compass bar feature at runtime.
/// </summary>
/// <param name="value">value</param>
public void EnableCompassBar ( bool value )
{
if ( useCompassBar ! = value ) {
useCompassBar = value ;
_HUDNavigationCanvas . ShowCompassBar ( value ) ;
}
}
/// <summary>
/// Enable / Disable the indicator feature at runtime.
/// </summary>
/// <param name="value">value</param>
public void EnableIndicators ( bool value )
{
if ( useIndicators ! = value ) {
useIndicators = value ;
_HUDNavigationCanvas . ShowIndicators ( value ) ;
}
}
/// <summary>
/// Enable / Disable the minimap feature at runtime.
/// </summary>
/// <param name="value">value</param>
public void EnableMinimap ( bool value )
{
if ( useMinimap ! = value ) {
useMinimap = value ;
_HUDNavigationCanvas . ShowMinimap ( value ) ;
}
}
#endregion
#region Utility Methods
void InitAllComponents ( )
{
if ( _HUDNavigationCanvas = = null )
return ;
// init radar
if ( useRadar ) {
_HUDNavigationCanvas . InitRadar ( ) ;
// make sure max radius is greater than radius
if ( radarMaxRadius < radarRadius )
radarMaxRadius = radarRadius ;
} else {
_HUDNavigationCanvas . ShowRadar ( false ) ;
}
// init compass bar
if ( useCompassBar )
_HUDNavigationCanvas . InitCompassBar ( ) ;
else
_HUDNavigationCanvas . ShowCompassBar ( false ) ;
// init indicators
if ( useIndicators )
_HUDNavigationCanvas . InitIndicators ( ) ;
else
_HUDNavigationCanvas . ShowIndicators ( false ) ;
2026-04-18 13:57:19 -04:00
2025-11-25 08:19:33 -05:00
// init minimap
2026-04-18 13:57:19 -04:00
currentMinimapProfile = minimapProfile ;
2025-11-25 08:19:33 -05:00
if ( useMinimap & & minimapProfile ! = null )
_HUDNavigationCanvas . InitMinimap ( minimapProfile ) ;
else
_HUDNavigationCanvas . ShowMinimap ( false ) ;
}
protected virtual void UpdateAllComponents ( )
{
// return, if system is disabled
if ( ! isEnabled )
return ;
// return, if references are missing
2026-04-18 13:57:19 -04:00
if ( PlayerCamera = = null | | PlayerController = = null | | _HUDNavigationCanvas = = null )
2025-11-25 08:19:33 -05:00
return ;
// update navigation elements
UpdateNavigationElements ( ) ;
// get rotation reference
Transform rotationReference = GetRotationReference ( ) ;
// update radar
if ( useRadar )
_HUDNavigationCanvas . UpdateRadar ( rotationReference , radarMode ) ;
// update compass bar
if ( useCompassBar )
_HUDNavigationCanvas . UpdateCompassBar ( rotationReference ) ;
// update minimap
if ( useMinimap & & minimapProfile ! = null & & PlayerController ! = null )
_HUDNavigationCanvas . UpdateMinimap ( rotationReference , minimapMode , PlayerController , minimapProfile , minimapScale ) ;
}
protected void UpdateNavigationElements ( )
{
if ( _HUDNavigationCanvas = = null | | NavigationElements . Count < = 0 )
return ;
// update navigation elements
foreach ( HUDNavigationElement element in NavigationElements ) {
if ( element = = null )
continue ;
// check if element is active
if ( ! element . IsActive ) {
// disable all marker instances
element . SetMarkerActive ( NavigationElementType . Radar , false ) ;
element . SetMarkerActive ( NavigationElementType . CompassBar , false ) ;
element . SetMarkerActive ( NavigationElementType . Minimap , false ) ;
element . SetIndicatorActive ( false ) ;
// skip the element
continue ;
}
// cache element values
Vector3 _worldPos = element . GetPosition ( ) ;
Vector3 _screenPos = PlayerCamera . WorldToScreenPoint ( _worldPos ) ;
float _distance = element . GetDistance ( PlayerController . transform ) ;
// update radar element
if ( useRadar & & element . Radar ! = null )
UpdateRadarElement ( element , _screenPos , _distance ) ;
// update compass bar element
if ( useCompassBar & & element . CompassBar ! = null )
UpdateCompassBarElement ( element , _screenPos , _distance ) ;
// update indicator element
if ( useIndicators & & element . Indicator ! = null )
UpdateIndicatorElement ( element , _screenPos , _distance ) ;
// update minimap element
if ( useMinimap & & element . Minimap ! = null & & currentMinimapProfile ! = null )
UpdateMinimapElement ( element , _screenPos , _distance ) ;
// element update event
element . OnElementUpdate . Invoke ( _worldPos , _screenPos , _distance ) ;
}
}
protected Transform GetRotationReference ( )
{
return ( RotationReference = = _RotationReference . Camera ) ? PlayerCamera . transform : PlayerController ;
}
void OnDrawGizmos ( )
{
#if UNITY_EDITOR
if ( PlayerController = = null | | Selection . activeGameObject ! = this . gameObject )
return ;
// draw height system debug gizmos
bool _radarHeightGizmos = useRadarHeightSystem & & showRadarHeightGizmos ;
bool _minimapHeightGizmos = useMinimapHeightSystem & & showMinimapHeightGizmos ;
if ( _radarHeightGizmos | | _minimapHeightGizmos ) {
Vector3 playerPos = PlayerController . position ;
// draw radar height planes
if ( _radarHeightGizmos ) {
Gizmos . color = radarHeightGizmoColor ;
Gizmos . DrawCube ( playerPos + ( Vector3 . up * radarDistanceAbove ) , new Vector3 ( radarHeightGizmoSize . x , . 01f , radarHeightGizmoSize . y ) ) ;
Gizmos . DrawCube ( playerPos - ( Vector3 . up * radarDistanceBelow ) , new Vector3 ( radarHeightGizmoSize . x , . 01f , radarHeightGizmoSize . y ) ) ;
}
// draw minimap height planes
if ( _minimapHeightGizmos ) {
Gizmos . color = minimapHeightGizmoColor ;
Gizmos . DrawCube ( playerPos + ( Vector3 . up * minimapDistanceAbove ) , new Vector3 ( minimapHeightGizmoSize . x , . 01f , minimapHeightGizmoSize . y ) ) ;
Gizmos . DrawCube ( playerPos - ( Vector3 . up * minimapDistanceBelow ) , new Vector3 ( minimapHeightGizmoSize . x , . 01f , minimapHeightGizmoSize . y ) ) ;
}
}
// draw map bounds
if ( showMinimapBounds & & minimapProfile ! = null ) {
Gizmos . color = minimapBoundsGizmoColor ;
Gizmos . DrawWireCube ( minimapProfile . MapBounds . center , minimapProfile . MapBounds . size ) ;
}
#endif
}
void RefreshPlayerReferences ( )
{
if ( _HUDNavigationCanvas = = null )
return ;
// check for missing / newly assigned player references
if ( PlayerCamera = = null | | PlayerController = = null )
_HUDNavigationCanvas . EnableCanvas ( false ) ; // keep system enabled, but disable canvas
else if ( isEnabled & & ! _HUDNavigationCanvas . isEnabled )
_HUDNavigationCanvas . EnableCanvas ( true ) ; // re-enable canvas, because all missing references were assigned
}
#endregion
#region Radar Methods
void UpdateRadarElement ( HUDNavigationElement element , Vector3 screenPos , float distance )
{
float _scaledRadius = radarRadius * radarZoom ;
float _scaledMaxRadius = radarMaxRadius * radarZoom ;
float _rawDistance = distance ;
// check if element is hidden within the radar
if ( element . hideInRadar ) {
element . SetMarkerActive ( NavigationElementType . Radar , false ) ;
return ;
}
// check distance
if ( distance > _scaledRadius ) {
// invoke events
if ( element . IsInRadarRadius ) {
element . IsInRadarRadius = false ;
element . OnLeaveRadius . Invoke ( element , NavigationElementType . Radar ) ;
}
// check max distance
if ( distance > _scaledMaxRadius & & ! element . ignoreRadarRadius ) {
element . SetMarkerActive ( NavigationElementType . Radar , false ) ;
return ;
}
// set scaled distance when out of range
distance = _scaledRadius ;
} else {
// invoke events
if ( ! element . IsInRadarRadius ) {
element . IsInRadarRadius = true ;
element . OnEnterRadius . Invoke ( element , NavigationElementType . Radar ) ;
}
}
2026-04-18 13:57:19 -04:00
// apply radar container rotation
element . Radar . PrefabRect . rotation = Quaternion . identity ;
// apply radar icon rotation
Quaternion iconRotation = Quaternion . identity ;
if ( element . rotateWithGameObject )
{
float zRotation = - element . transform . eulerAngles . y ;
if ( radarMode = = RadarModes . RotateRadar )
{
Transform rotationReference = GetRotationReference ( ) ;
zRotation + = rotationReference . eulerAngles . y ;
}
2025-11-25 08:19:33 -05:00
2026-04-18 13:57:19 -04:00
iconRotation = Quaternion . Euler ( 0f , 0f , zRotation ) ;
}
element . Radar . Icon . transform . rotation = iconRotation ;
2025-11-25 08:19:33 -05:00
// update marker values
float invertedDistance = radarMaxRadius - _rawDistance ;
element . SetMarkerScale ( useRadarScaling & & ! element . ignoreRadarScaling & & ! element . ignoreRadarRadius , invertedDistance , radarScaleDistance , radarMinScale , element . Radar . PrefabRect ) ;
element . SetMarkerFade ( useRadarFading & & ! element . ignoreRadarFading & & ! element . ignoreRadarRadius , invertedDistance , radarFadeDistance , radarMinFade , element . Radar . PrefabCanvasGroup ) ;
element . SetMarkerActive ( NavigationElementType . Radar , true ) ;
// calculate marker position
Vector3 posOffset = element . GetPositionOffset ( PlayerController . position ) ;
Vector3 markerPos = new Vector3 ( posOffset . x , posOffset . z , 0f ) ;
markerPos . Normalize ( ) ;
markerPos * = ( distance / _scaledRadius ) * ( _HUDNavigationCanvas . Radar . ElementContainer . GetRadius ( ) - element . GetIconRadius ( NavigationElementType . Radar ) ) ;
// set marker position
element . SetMarkerPosition ( NavigationElementType . Radar , markerPos ) ;
// handle marker's above/below arrows
2026-04-18 13:57:19 -04:00
if ( useRadarHeightSystem ) {
bool heightSystemConditions = element . useRadarHeightSystem & & element . IsInRadarRadius ;
element . ShowRadarAboveArrow ( heightSystemConditions & & - posOffset . y < - radarDistanceAbove ) ;
element . ShowRadarBelowArrow ( heightSystemConditions & & - posOffset . y > radarDistanceBelow ) ;
}
2025-11-25 08:19:33 -05:00
}
#endregion
#region CompassBar Methods
void UpdateCompassBarElement ( HUDNavigationElement element , Vector3 screenPos , float distance )
{
// check if element is hidden within the compass bar
if ( element . hideInCompassBar ) {
element . SetMarkerActive ( NavigationElementType . CompassBar , false ) ;
return ;
}
// check distance
if ( distance > compassBarRadius & & ! element . ignoreCompassBarRadius ) {
element . SetMarkerActive ( NavigationElementType . CompassBar , false ) ;
// invoke events
if ( element . IsInCompassBarRadius ) {
element . IsInCompassBarRadius = false ;
element . OnLeaveRadius . Invoke ( element , NavigationElementType . CompassBar ) ;
}
return ;
}
// invoke events
if ( ! element . IsInCompassBarRadius ) {
element . IsInCompassBarRadius = true ;
element . OnEnterRadius . Invoke ( element , NavigationElementType . CompassBar ) ;
}
// set marker position
if ( screenPos . z < = 0 ) {
// hide marker and skip element
element . SetMarkerActive ( NavigationElementType . CompassBar , false ) ;
return ;
}
// show compass bar distance?
element . ShowCompassBarDistance ( ( int ) distance ) ;
// set marker active
element . SetMarkerActive ( NavigationElementType . CompassBar , true ) ;
// set marker position
element . SetMarkerPosition ( NavigationElementType . CompassBar , screenPos , _HUDNavigationCanvas . CompassBar . ElementContainer ) ;
}
#endregion
#region Indicator Methods
protected virtual void UpdateIndicatorElement ( HUDNavigationElement element , Vector3 screenPos , float distance )
{
if ( useIndicators & & element . showIndicator ) {
// check indicator distance
if ( ( distance > indicatorRadius & & ! element . ignoreIndicatorRadius ) ) {
element . SetIndicatorActive ( false ) ;
// invoke events
if ( element . IsInIndicatorRadius ) {
element . IsInIndicatorRadius = false ;
element . OnLeaveRadius . Invoke ( element , NavigationElementType . Indicator ) ;
}
} else {
// check if element is visible on screen
bool _isElementOnScreen = element . IsVisibleOnScreen ( screenPos ) ;
if ( ! _isElementOnScreen ) {
if ( useOffscreenIndicators & & element . showOffscreenIndicator ) {
// flip if indicator is behind us
if ( screenPos . z < 0f ) {
screenPos . x = Screen . width - screenPos . x ;
screenPos . y = Screen . height - screenPos . y ;
}
// calculate off-screen position/rotation
Vector3 screenCenter = new Vector3 ( Screen . width , Screen . height , 0f ) / 2f ;
screenPos - = screenCenter ;
float angle = Mathf . Atan2 ( screenPos . y , screenPos . x ) ;
angle - = 90f * Mathf . Deg2Rad ;
float cos = Mathf . Cos ( angle ) ;
float sin = - Mathf . Sin ( angle ) ;
float cotangent = cos / sin ;
float offset = Mathf . Min ( screenCenter . x , screenCenter . y ) ;
offset = Mathf . Lerp ( 0f , offset , indicatorOffscreenBorder ) ;
Vector3 screenBounds = screenCenter - new Vector3 ( offset , offset , 0f ) ;
float boundsY = ( cos > 0f ) ? screenBounds . y : - screenBounds . y ;
screenPos = new Vector3 ( boundsY / cotangent , boundsY , 0f ) ;
// when out of bounds, get point on appropriate side
if ( screenPos . x > screenBounds . x ) // out => right
screenPos = new Vector3 ( screenBounds . x , screenBounds . x * cotangent , 0f ) ;
else if ( screenPos . x < - screenBounds . x ) // out => left
screenPos = new Vector3 ( - screenBounds . x , - screenBounds . x * cotangent , 0f ) ;
screenPos + = screenCenter ;
// update indicator rotation
element . SetIndicatorOffscreenRotation ( Quaternion . Euler ( 0f , 0f , angle * Mathf . Rad2Deg ) ) ;
} else {
// hide indicator offscreen
element . SetIndicatorActive ( false ) ;
return ;
}
}
// show indicator distance?
element . ShowIndicatorDistance ( _isElementOnScreen , ( int ) distance ) ;
// set indicator on/offscreen
element . SetIndicatorOnOffscreen ( _isElementOnScreen ) ;
// update indicator values
element . SetIndicatorPosition ( screenPos , _HUDNavigationCanvas . Indicator . ElementContainer ) ;
element . SetMarkerScale ( useIndicatorScaling & & ! element . ignoreIndicatorScaling , distance , indicatorScaleRadius , indicatorMinScale , element . Indicator . PrefabRect ) ;
element . SetMarkerFade ( useIndicatorFading & & ! element . ignoreIndicatorFading , distance , indicatorFadeRadius , indicatorMinFade , element . Indicator . PrefabCanvasGroup ) ;
element . SetIndicatorActive ( ( indicatorHideDistance > 0f & & ! element . ignoreIndicatorHideDistance ) ? distance > indicatorHideDistance : true ) ;
// invoke events
if ( ! element . IsInIndicatorRadius ) {
element . IsInIndicatorRadius = true ;
element . OnEnterRadius . Invoke ( element , NavigationElementType . Indicator ) ;
}
}
} else {
element . SetIndicatorActive ( false ) ;
}
}
#endregion
#region Minimap Methods
void UpdateMinimapElement ( HUDNavigationElement element , Vector3 screenPos , float distance )
{
float _rawDistance = distance ;
// check if element is hidden within the minimap
if ( element . hideInMinimap ) {
element . SetMarkerActive ( NavigationElementType . Minimap , false ) ;
return ;
}
// check distance
if ( distance > minimapRadius ) {
// invoke events
if ( element . IsInMinimapRadius ) {
element . IsInMinimapRadius = false ;
element . OnLeaveRadius . Invoke ( element , NavigationElementType . Minimap ) ;
}
// hide element
if ( ! element . ignoreMinimapRadius ) {
element . SetMarkerActive ( NavigationElementType . Minimap , false ) ;
return ;
}
} else {
// invoke events
if ( ! element . IsInMinimapRadius ) {
element . IsInMinimapRadius = true ;
element . OnEnterRadius . Invoke ( element , NavigationElementType . Minimap ) ;
}
}
// rotate marker within minimap with gameobject?
if ( element . rotateWithGameObjectMM )
element . Minimap . Icon . transform . rotation = Quaternion . Euler ( new Vector3 ( 0f , 0f , - element . transform . eulerAngles . y ) ) ;
else
element . Minimap . Icon . transform . rotation = Quaternion . identity ;
// update marker values
float invertedDistance = minimapRadius - _rawDistance ;
element . SetMarkerScale ( useMinimapScaling & & ! element . ignoreMinimapScaling & & ! element . ignoreMinimapRadius , invertedDistance , minimapScaleDistance , minimapMinScale , element . Minimap . PrefabRect ) ;
element . SetMarkerFade ( useMinimapFading & & ! element . ignoreMinimapFading & & ! element . ignoreMinimapRadius , invertedDistance , minimapFadeDistance , minimapMinFade , element . Minimap . PrefabCanvasGroup ) ;
element . SetMarkerActive ( NavigationElementType . Minimap , true ) ;
// calculate marker position
Vector2 unitScale = minimapProfile . GetMapUnitScale ( ) ;
Vector3 posOffset = element . GetPositionOffset ( PlayerController . position ) ;
Vector3 markerPos = new Vector3 ( posOffset . x * unitScale . x , posOffset . z * unitScale . y , 0f ) * minimapScale ;
// adjust marker position when using minimap rotation mode
if ( minimapMode = = MinimapModes . RotateMinimap )
markerPos = PlayerController . MinimapRotationOffset ( markerPos ) ;
// always keep marker within minimap rect
bool outOfBounds ;
2026-04-18 13:57:19 -04:00
if ( minimapShape = = MinimapShapes . Rectangular )
markerPos = _HUDNavigationCanvas . Minimap . ElementContainer . KeepInRectBounds ( markerPos , out outOfBounds ) ;
else
markerPos = _HUDNavigationCanvas . Minimap . ElementContainer . KeepInCircleBounds ( markerPos , out outOfBounds ) ;
2025-11-25 08:19:33 -05:00
// set marker position
element . SetMarkerPosition ( NavigationElementType . Minimap , markerPos ) ;
// handle marker's above/below arrows
2026-04-18 13:57:19 -04:00
if ( useMinimapHeightSystem ) {
bool heightSystemConditions = element . useMinimapHeightSystem & & element . IsInMinimapRadius & & ! outOfBounds ;
element . ShowMinimapAboveArrow ( heightSystemConditions & & - posOffset . y < - minimapDistanceAbove ) ;
element . ShowMinimapBelowArrow ( heightSystemConditions & & - posOffset . y > minimapDistanceBelow ) ;
}
2025-11-25 08:19:33 -05:00
}
#endregion
#region Configuration Methods
/// <summary>
/// Applies a new scene configuration.
/// </summary>
/// <param name="config">Scene Configuration.</param>
public void ApplySceneConfiguration ( HNSSceneConfiguration config )
{
if ( config = = null )
return ;
// override radar settings
if ( config . overrideRadarSettings ) {
// radar usage changed?
if ( _HUDNavigationCanvas ! = null & & this . useRadar ! = config . useRadar )
_HUDNavigationCanvas . ShowRadar ( config . useRadar ) ;
this . useRadar = config . useRadar ;
this . radarMode = config . radarMode ;
this . radarZoom = config . radarZoom ;
this . radarRadius = config . radarRadius ;
this . radarMaxRadius = config . radarMaxRadius ;
this . useRadarScaling = config . useRadarScaling ;
this . radarScaleDistance = config . radarScaleDistance ;
this . radarMinScale = config . radarMinScale ;
this . useRadarFading = config . useRadarFading ;
this . radarFadeDistance = config . radarFadeDistance ;
this . radarMinFade = config . radarMinFade ;
this . useRadarHeightSystem = config . useRadarHeightSystem ;
this . radarDistanceAbove = config . radarDistanceAbove ;
this . radarDistanceBelow = config . radarDistanceBelow ;
}
// override compass bar settings
if ( config . overrideCompassBarSettings ) {
// compass bar usage changed?
if ( _HUDNavigationCanvas ! = null & & this . useCompassBar ! = config . useCompassBar )
_HUDNavigationCanvas . ShowCompassBar ( config . useCompassBar ) ;
this . useCompassBar = config . useCompassBar ;
this . compassBarRadius = config . compassBarRadius ;
}
// override indicator settings
if ( config . overrideIndicatorSettings ) {
// indicator usage changed?
if ( _HUDNavigationCanvas ! = null & & this . useIndicators ! = config . useIndicators )
_HUDNavigationCanvas . ShowIndicators ( config . useIndicators ) ;
this . useIndicators = config . useIndicators ;
this . indicatorRadius = config . indicatorRadius ;
this . indicatorHideDistance = config . indicatorHideDistance ;
this . useOffscreenIndicators = config . useOffscreenIndicators ;
this . indicatorOffscreenBorder = config . indicatorOffscreenBorder ;
this . useIndicatorScaling = config . useIndicatorScaling ;
this . indicatorScaleRadius = config . indicatorScaleRadius ;
this . indicatorMinScale = config . indicatorMinScale ;
this . useIndicatorFading = config . useIndicatorFading ;
this . indicatorFadeRadius = config . indicatorFadeRadius ;
this . indicatorMinFade = config . indicatorMinFade ;
}
// override minimap settings
if ( config . overrideMinimapSettings ) {
// assign new minimap profile
if ( this . currentMinimapProfile ! = config . minimapProfile ) {
this . currentMinimapProfile = config . minimapProfile ;
if ( _HUDNavigationCanvas ! = null )
_HUDNavigationCanvas . SetMinimapProfile ( this . currentMinimapProfile ) ;
}
// minimap usage changed?
if ( _HUDNavigationCanvas ! = null & & this . useMinimap ! = config . useMinimap )
_HUDNavigationCanvas . ShowMinimap ( config . useMinimap ) ;
this . useMinimap = config . useMinimap ;
this . minimapMode = config . minimapMode ;
2026-04-18 13:57:19 -04:00
this . minimapShape = config . minimapShape ;
2025-11-25 08:19:33 -05:00
this . minimapScale = config . minimapScale ;
this . minimapRadius = config . minimapRadius ;
this . useMinimapScaling = config . useMinimapScaling ;
this . minimapScaleDistance = config . minimapScaleDistance ;
this . minimapMinScale = config . minimapMinScale ;
this . useMinimapFading = config . useMinimapFading ;
this . minimapFadeDistance = config . minimapFadeDistance ;
this . minimapMinFade = config . minimapMinFade ;
this . showMinimapBounds = config . showMinimapBounds ;
this . useMinimapHeightSystem = config . useMinimapHeightSystem ;
this . minimapDistanceAbove = config . minimapDistanceAbove ;
this . minimapDistanceBelow = config . minimapDistanceBelow ;
}
}
#endregion
}
#region Subclasses
[System.Serializable]
public enum _RotationReference
{
Camera , Controller
}
[System.Serializable]
public enum _UpdateMode
{
Update , LateUpdate
}
[System.Serializable]
public enum RadarModes
{
RotateRadar , RotatePlayer
}
[System.Serializable]
public enum MinimapModes
{
RotateMinimap , RotatePlayer
}
2026-04-18 13:57:19 -04:00
[System.Serializable]
public enum MinimapShapes
{
Rectangular , Circular
}
2025-11-25 08:19:33 -05:00
#endregion
}