using scsmmedia; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; // Sci-Fi Ship Controller. Copyright (c) 2018-2023 SCSM Pty Ltd. All rights reserved. namespace SciFiShipController { /// /// Heads Up Display - typically used from an in-cockpit player view or setup. /// The general approach here is to use RectTransforms that are NOT stretched /// and are anchored at the centre. /// Setup Notes: /// HUDPanel should be anchored at four corners of screen (stretched) /// Display Reticle panel should be anchored at the centre of HUD /// Altitude Panel should be anchored to centre /// Altitude Text pivot point should be 0,0 (left bottom corner of textbox) /// [AddComponentMenu("Sci-Fi Ship Controller/Misc/Ship Display Module")] [HelpURL("http://scsmmedia.com/ssc-documentation")] public class ShipDisplayModule : MonoBehaviour { #region Enumerations #endregion #region Public Static Variables public readonly static string hudPanelName = "HUDPanel"; public readonly static string targetsPanelName = "TargetsPanel"; public readonly static string gaugesPanelName = "GaugesPanel"; public readonly static string attitudePanelName = "AttitudePanel"; public readonly static string headingPanelName = "HeadingPanel"; public readonly static string overlayPanelName = "OverlayPanel"; public readonly static string attitudeScrollName = "AttitudeScroll"; public readonly static string headingScrollName = "HeadingScroll"; public readonly static string headingIndicatorName = "HeadingIndicator"; [System.NonSerialized] public static readonly AnimationCurve easeInOutCurve = AnimationCurve.EaseInOut(0f, 0f, 1f, 1f); #endregion #region Public Variables and Properties - General /// /// If enabled, the Initialise() will be called as soon as Start() runs. This should be disabled if you are /// instantiating the HUD through code. /// public bool initialiseOnStart = false; /// /// Show or Hide the HUD when it is first Initialised /// public bool isShowOnInitialise = true; /// /// Show overlay image on HUD. At runtime call ShowOverlay() or HideOverlay() /// public bool showOverlay = true; /// /// The head-up display's normalised width of the screen. 1.0 is full width, 0.5 is half width. /// At runtime call shipDisplayModule.SetHUDSize(..) /// [Range(0.1f, 1f)] public float displayWidth = 0.5f; /// /// The head-up display's normalised height of the screen. 1.0 is full height, 0.5 is half height. /// At runtime call shipDisplayModule.SetHUDSize(..) /// [Range(0.1f, 1f)] public float displayHeight = 0.75f; /// /// The head-up display's normalised offset between the left (-1) and the right (1) from the centre (0) of the screen. /// At runtime call shipDisplayModule.SetHUDOffset(..) /// [Range(-1f, 1f)] public float displayOffsetX = 0f; /// /// The head-up display's normalised offset between the bottom (-1) and the top (1) from the centre (0) of the screen. /// At runtime call shipDisplayModule.SetHUDOffset(..) /// [Range(-1f, 1f)] public float displayOffsetY = 0f; /// /// Primary colour of the heads-up display. /// At runtime call shipDisplayModule.SetPrimaryColour(..) /// public Color32 primaryColour = Color.grey; /// /// Automatically hide the screen cursor or mouse pointer after it has been stationary /// for a fixed period of time. Automatically show the cursor if the mouse if moved /// provided that the Display Reticle is on shown. /// public bool autoHideCursor = true; /// /// The number of seconds to wait until after the cursor has not moved before hiding it /// public float hideCursorTime = 3f; /// /// Is the ship display module initialised? Gets set at runtime when Initialise() is called. /// public bool IsInitialised { get; private set; } /// /// Is the heads-up display shown? To set use ShowHUD() or HideHUD(). /// IsHUDShown should never be true at runtime if IsInitialised is false /// public bool IsHUDShown { get; private set; } /// /// Get a reference to the HUD canvas /// public Canvas GetCanvas { get { return IsInitialised ? canvas : GetComponent(); } } /// /// The overall brightness of the HUD. At runtime use /// SetBrightness(value) /// [Range(0f, 1f)] public float brightness = 1f; /// /// The sort order of the canvas in the scene. Higher numbers are on top. /// At runtime call shipDisplayModule.SetCanvasSortOrder(..) /// public int canvasSortOrder = 2; /// /// The default size the HUD was designed with. This should always /// return 1920x1080 /// public Vector2 ReferenceResolution { get { return refResolution; } } /// /// The current scaled resolution of the HUD canvas /// public Vector2 CanvasResolution { get { return cvsResolutionFull; } } /// /// The current scale factor of the HUD canvas /// public Vector3 CanvasScaleFactor { get { return cvsScaleFactor; } } /// /// The current actual screen resolution. This may be different from /// what the /// public Vector2 ScreenResolution { get { return screenResolution; } } #endregion #region Public Variables and Properties - Flicker /// /// Whenever the HUD is shown, should it flicker on? /// public bool isShowHUDWithFlicker = false; /// /// Whenever the HUD is hidden, should it flicker off? /// public bool isHideHUDWithFlicker = false; /// /// The time, in seconds, the effect takes to reach a steady state /// [Range(0.01f, 5f)] public float flickerDefaultDuration = 1f; /// /// The minimum time, in seconds, that the effect is inactive or off /// [Range(0f, 2f)] public float flickerMinInactiveTime = 0.1f; /// /// The maximum time, in seconds, that the effect is inactive or off /// [Range(0f, 2f)] public float flickerMaxInactiveTime = 0.2f; /// /// The minimum time, in seconds, that the effect is active or on /// [Range(0f, 2f)] public float flickerMinActiveTime = 0.1f; /// /// The maximum time, in seconds, that the effect is active or on /// [Range(0f, 2f)] public float flickerMaxActiveTime = 0.2f; /// /// Smooth the flickering effect. Higher values give a smoother effect /// [Range(0, 5)] public float flickerSmoothing = 3f; /// /// The intensity of the effect will randomly change /// public bool flickerVariableIntensity = false; /// /// The maximum intensity of the effect used during the on cycle when /// flickerVariableIntensity is enabled. /// This is a multiplier of the starting intensity of the effect. /// Value must be between 0.01 and 1.0. /// [Range(0.01f, 1f)] public float flickerMaxIntensity = 1f; #endregion #region Public Variables and Properties - Reticles /// /// The guidHash of the currently selected / displayed reticle /// public int guidHashActiveDisplayReticle = 0; /// /// The list of display reticles that can be used with /// this Ship Display. Call ReinitialiseVariables() if /// you modify the list at runtime. /// public List displayReticleList; /// /// Get the current number of display reticles /// public int GetNumberDisplayReticles { get { return IsInitialised ? numDisplayReticles : displayReticleList == null ? 0 : displayReticleList.Count; } } /// /// [INTERNAL USE ONLY] /// public bool isDisplayReticleListExpanded = true; /// /// For consistency, is the display reticle currently shown when the /// module has already been initialised? /// public bool IsDisplayReticleShown { get { return showActiveDisplayReticle; } } /// /// Get the Display Reticle position on the screen in Viewport coordinates. x = 0.0-1.0, y = 0.0-1.0. 0,0 is bottom left corner. /// public Vector2 DisplayReticleViewportPoint { get { return new Vector2((displayReticleOffsetX + 1f) * 0.5f, (displayReticleOffsetY + 1f) * 0.5f); } } /// /// Show the current (active) Display Reticle /// on the HUD. At runtime use ShowDisplayReticle() /// or HideDisplayReticle(). /// public bool showActiveDisplayReticle = false; /// /// The Display Reticle's normalised offset between the left (-1) and the right (1) from the centre (0) of the screen. /// At runtime call shipDisplayModule.SetDisplayReticleOffset(..) /// [Range(-1f, 1f)] public float displayReticleOffsetX = 0f; /// /// The Display Reticle's normalised offset between the bottom (-1) and the top (1) from the centre (0) of the screen. /// At runtime call shipDisplayModule.SetDisplayReticleOffset(..) /// [Range(-1f, 1f)] public float displayReticleOffsetY = 0f; /// /// Should the Display Reticle follow the cursor or mouse screen position? /// public bool lockDisplayReticleToCursor = false; /// /// Colour of the active Display Reticle. /// At runtime call shipDisplayModule.SetDisplayReticleColour(..) /// public Color32 activeDisplayReticleColour = Color.white; /// /// This is the main camera that HUD will reference. If left empty, this will /// be auto-populated with your first Camera with a tag of MainCamera. /// Can be changed at runtime with shipDisplayModule.SetCamera(..). /// public Camera mainCamera = null; #endregion #region Public Variables and Properties - Altitude and Speed /// /// The ship in the scene that will supply the data for this HUD /// public ShipControlModule sourceShip = null; /// /// Show the Altitude indicator on the HUD. At runtime use ShowAltitude() or HideAltitude(). /// Typically only used when near the surface of a planet. See also groundPlaneHeight. /// public bool showAltitude = false; /// /// Used to determine altitude when near a planet's surface. On Earth this would typically /// be sea-level but it can be used to set an artificial zero-height which may be useful /// when flying over the surface of a planet. /// public float groundPlaneHeight = 0f; /// /// The colour of the Altitude text (number). /// At runtime call shipDisplayModule.SetAltitudeTextColour(..) /// public Color32 altitudeTextColour = Color.grey; /// /// Show the air speed indicator on the HUD. At runtime use ShowAirSpeed() or HideAirSpeed(). /// public bool showAirspeed = false; /// /// The colour of the Air Speed text (number). /// At runtime call shipDisplayModule.SetAirSpeedTextColour(..) /// public Color32 airspeedTextColour = Color.grey; #endregion #region Public Variables and Properties - Attitude /// /// Show or hide the Attitude. /// At runtime use ShowAttitude() or HideAttitude() /// public bool showAttitude = false; /// /// Primary colour of the scrollable heading /// At runtime call shipDisplayModule.SetDisplayAttitudePrimaryColour(..) /// public Color attitudePrimaryColour = Color.white; #endregion #region Public Variables and Properties - Heading /// /// Show or hide the Heading or direction as a scrollable ribbon in the UI. /// At runtime use ShowHeading() or HideHeading() /// public bool showHeading = false; /// /// Show or hide the small heading indicator /// At runtime use ShowHeadingIndictor() or HideHeadingIndicator() /// public bool showHeadingIndicator = true; /// /// Primary colour of the scrollable heading /// At runtime call shipDisplayModule.SetDisplayHeadingPrimaryColour(..) /// public Color headingPrimaryColour = Color.white; /// /// The small indicator colour of the scrollable heading /// At runtime call shipDisplayModule.SetDisplayHeadingIndicatorColour(..) /// public Color headingIndicatorColour = Color.green; #endregion #region Public Variables and Properties - Gauges /// /// The list of display gauges that can be used with /// this Ship Display. Call ReinitialiseVariables() if /// you modify this list at runtime. Where possible use the API /// methods to Add, Delete, or set gauge attributes. /// public List displayGaugeList; /// /// Get the current number of display gauge /// public int GetNumberDisplayGauges { get { return IsInitialised ? numDisplayGauges : displayGaugeList == null ? 0 : displayGaugeList.Count; } } /// /// [INTERNAL USE ONLY] /// public bool isDisplayGaugeListExpanded = true; #endregion #region Public Variables and Properties - Messages /// /// The list of display messages that can be used with /// this Ship Display. Call ReinitialiseVariables() if /// you modify this list at runtime. Where possible use the API /// methods to Add, Delete, or set messages attributes. /// public List displayMessageList; /// /// Get the current number of display messages /// public int GetNumberDisplayMessages { get { return IsInitialised ? numDisplayMessages : displayMessageList == null ? 0 : displayMessageList.Count; } } /// /// [INTERNAL USE ONLY] /// public bool isDisplayMessageListExpanded = true; #endregion #region Public Variables and Properties - Targets /// /// The list of display targets that can be used with /// this Ship Display. Call ReinitialiseVariables() if /// you modify this list at runtime. Where possible use the API /// methods to Add, Delete, or set target attributes. /// public List displayTargetList; /// /// Get the current number of Display Targets. /// public int GetNumberDisplayTargets { get { return IsInitialised ? numDisplayTargets : displayTargetList == null ? 0 : displayTargetList.Count; } } /// /// Get the size of the Targets viewport as a normalised (0.0-1.0) value of the current screensize /// public Vector2 GetTargetsViewportSize { get { return new Vector2(targetsViewWidth, targetsViewHeight); } } /// /// Get the offset from the screen centre of the Targets viewport. Values are -1.0 to 1.0 /// public Vector2 GetTargetsViewportOffset { get { return new Vector2(targetsViewOffsetX, targetsViewOffsetY); } } /// /// When DisplayTarget slots have an active RadarItem assigned to them, should the reticles be automatically /// moved on the HUD? /// public bool autoUpdateTargetPositions = true; /// /// The Targets normalised viewable width of the screen. /// 1.0 is full width, 0.5 is half width. /// At runtime call shipDisplayModule.SetTargetsViewportSize(..) /// [Range(0.1f, 1f)] public float targetsViewWidth = 1.0f; /// /// The Targets normalised viewable height of the screen. /// 1.0 is full height, 0.5 is half height. /// At runtime call shipDisplayModule.SetTargetsViewportSize(..) /// [Range(0.1f, 1f)] public float targetsViewHeight = 0.5f; /// /// The X offset from centre of the screen for the Targets viewport /// At runtime call shipDisplayModule.SetTargetsViewportOffset(..) /// [Range(-1f, 1f)] public float targetsViewOffsetX = 0f; /// /// The Y offset from centre of the screen for the Targets viewport /// At runtime call shipDisplayModule.SetTargetsViewportOffset(..) /// [Range(-1f, 1f)] public float targetsViewOffsetY = 0.5f; /// /// The maximum distance, in metres, that targets can be away from the ship /// public float targetingRange = 5000f; /// /// [INTERNAL USE ONLY] /// public bool isDisplayTargetListExpanded = true; #endregion #region Public Variables and Properties - FUTURE FEATURES /// /// FUTURE /// //public bool showArtificialHorizon = false; // FUTURE - FPV indicator //public bool showFlightPathVector = false; // FUTURE - wing's angle relative to airflow //public bool showAngleOfAttack = false; // FUTURE - target designation indicator //public bool showTargetDesignator = false; // Other options could be closing (target) velocity, range (to target), selected weapons, amno available etc. #endregion #region Public Variables and Properties - INTERNAL ONLY /// /// [INTERNAL ONLY] /// [HideInInspector] public bool allowRepaint = false; /// /// [INTERNAL ONLY] /// public bool showGeneralSettingsInEditor = false; /// /// [INTERNAL ONLY] /// public bool showReticleSettingsInEditor = false; /// /// [INTERNAL ONLY] /// public bool showAltSpeedSettingsInEditor = false; /// /// [INTERNAL ONLY] /// public bool showAttitudeSettingsInEditor = false; /// /// [INTERNAL ONLY] /// public bool showFlickerSettingsInEditor = false; /// /// [INTERNAL ONLY] /// public bool showHeadingSettingsInEditor = false; /// /// [INTERNAL ONLY] /// public bool showGaugeSettingsInEditor = false; /// /// [INTERNAL ONLY] /// public bool showMessageSettingsInEditor = false; /// /// [INTERNAL ONLY] /// public bool showTargetSettingsInEditor = false; /// /// [INTERNAL ONLY] /// Outside play mode, show a bounding box for where the HUD will be placed /// public bool showHUDOutlineInScene = true; /// /// [INTERNAL ONLY] /// Outside play mode, show a bounding box for where Targets can be displayed /// public bool showTargetsOutlineInScene = false; /// /// [INTERNAL ONLY] /// public bool IsEditorMode { get { return editorMode; } } #endregion #region Public Delegates public delegate void CallbackOnBrightnessChange(float newBrightness); public delegate void CallbackOnSizeChange(float newWidth, float newHeight); /// /// The name of the custom method that is called immediately /// after brightness has changed. Your method must take 1 float /// parameter. This should be a lightweight method to avoid /// performance issues. It could be used to update your custom /// HUD elements. /// public CallbackOnBrightnessChange callbackOnBrightnessChange = null; /// /// The name of the custom method that is called immediately after the /// HUD size has changed. This method must take 2 float parameters. It /// should be a lightweight method to avoid performance issues. It could /// be used to update your custom HUD elements. /// public CallbackOnSizeChange callbackOnSizeChange = null; #endregion #region Private Variables - General private Canvas canvas = null; private CanvasScaler canvasScaler = null; // Scaled canvas resolution. See CheckHUDResize() private Vector2 cvsResolutionFull = Vector2.one; private Vector2 cvsResolutionHalf = Vector2.one; private Vector2 prevResolutionFull = Vector2.one; // Default reference resolution e.g. 1920x1080 private Vector2 refResolution = Vector2.one; private Vector3 cvsScaleFactor = Vector3.one; private Vector2 screenResolution = Vector2.one; private Transform hudPanel = null; private bool isMainCameraAssigned = false; private Color tempColour = Color.clear; // Used to update HUD from the editor when not in play mode private bool editorMode = false; private List tempTextList = null; private List tempImgList = null; #endregion #region Private Variables - Attitude private RectTransform attitudeRectTfrm = null; private RectTransform attitudeScrollPanel = null; private UnityEngine.UI.Image attitudeScrollImg = null; private UnityEngine.UI.Image attitudeMaskImg = null; private bool isAttitudeScrolling = false; private Vector3 tempAttitudeOffset = Vector3.zero; private Vector3 attitudeInitPosition = Vector3.zero; /// /// This gets calculated in InitialiseAttitude(..) at runtime. /// private float attitudePixelsPerDegree = 1080f / 180f; /// /// The attitude normalised offset between the left (-1) and the right (1) from the centre (0) of the screen. /// At runtime call shipDisplayModule.SetAttitudeOffset(..) /// [SerializeField, Range(-1f, 1f)] private float attitudeOffsetX = 0f; /// /// The attitude normalised offset between the bottom (-1) and the top (1) from the centre (0) of the screen. /// At runtime call shipDisplayModule.SetAttitudeOffset(..) /// [SerializeField, Range(-1f, 1f)] private float attitudeOffsetY = 0f; /// /// The normalised masked width of the scrollable attitude. 1.0 is full width of the screen, 0.5 is half width. /// [SerializeField, Range(0.1f, 1f)] private float attitudeWidth = 0.75f; /// /// The normalised masked height of the scrollable attitude. 1.0 is full height of the screen, 0.5 is half height. /// [SerializeField, Range(0.1f, 1f)] private float attitudeHeight = 1f; /// /// The sprite (texture) that will scroll up/down /// [SerializeField] private Sprite attitudeScrollSprite = null; /// /// The sprite (texture) that will mask the scrollable attitude sprite /// [SerializeField] private Sprite attitudeMaskSprite = null; /// /// The number of pixels between the top of the scroll sprite and the first (90) pitch line. /// It is assumed that this is the same for between the bottom and the -90 pitch line. /// [SerializeField, Range(0f, 1000f)] private float attitudeScrollSpriteBorderWidth = 250f; #endregion #region Private Variables - Flickering /// /// Is the HUD currently flickering and will it end in the Shown state? /// This tracks if flickering is currently happening. /// private bool isFlickeringEndStateOn = false; /// /// Is the HUD currently flickering and will it end in the Hidden state? /// This tracks if flickering is currently happening. /// private bool isFlickeringEndStateOff = false; private float flickerDurationTimer = 0f; private float flickerInactiveTimer = 0f; private float flickerActiveTimer = 0f; private float flickerDuration = 0f; private float flickerActiveTime = 0f; private float flickerInactiveTime = 0f; private bool isFlickerWaiting = false; private SSCRandom hudRandom = null; private float flickerIntensity = 0f; private float flickerStartIntensity = 0f; private Queue flickerHistory = null; private float flickerHistoryTotal = 0f; #endregion #region Private Variables - Brightness private SSCColour baseReticleColour; private SSCColour baseOverlayColour; private SSCColour baseAltitudeTextColour; private SSCColour baseAirspeedTextColour; private SSCColour baseAttitudePrimaryColour; private SSCColour baseHeadingPrimaryColour; private SSCColour baseHeadingIndicatorColour; #endregion #region Private Variables - Heading private RectTransform headingRectTfrm = null; private RectTransform headingScrollPanel = null; private RectTransform headingIndicatorPanel = null; private UnityEngine.UI.Image headingScrollImg = null; private UnityEngine.UI.Image headingMaskImg = null; private UnityEngine.UI.Image headingIndicatorImg = null; private bool isHeadingScrolling = false; private Vector3 headingInitPosition = Vector3.zero; private Vector3 tempHeadingOffset = Vector3.zero; /// /// This gets calculated in InitialiseHeading(..) at runtime. /// private float headingPixelsPerDegree = 1920f / 360f; /// /// The heading normalised offset between the left (-1) and the right (1) from the centre (0) of the screen. /// At runtime call shipDisplayModule.SetHeadingOffset(..) /// [SerializeField, Range(-1f, 1f)] private float headingOffsetX = 0f; /// /// The heading normalised offset between the bottom (-1) and the top (1) from the centre (0) of the screen. /// At runtime call shipDisplayModule.SetHeadingOffset(..) /// [SerializeField, Range(-1f, 1f)] private float headingOffsetY = 0f; /// /// The normalised masked width of the scrollable heading. 1.0 is full width of the screen, 0.5 is half width. /// [SerializeField, Range(0.1f, 1f)] private float headingWidth = 0.75f; /// /// The normalised masked height of the scrollable heading. 1.0 is full height of the screen, 0.5 is half height. /// [SerializeField, Range(0.1f, 1f)] private float headingHeight = 1f; /// /// The sprite (texture) that will scroll left/right /// [SerializeField] private Sprite headingScrollSprite = null; /// /// The sprite (texture) that will mask the scrollable heading sprite /// [SerializeField] private Sprite headingMaskSprite = null; /// /// The small sprite (texture) that will indicate or point to the heading on the HUD /// [SerializeField] private Sprite headingIndicatorSprite = null; #endregion #region Private Variables - Reticle private RectTransform hudRectTfrm = null; private Transform reticlePanel = null; private float reticleWidth = 1f; private float reticleHeight = 1f; private DisplayReticle currentDisplayReticle = null; private UnityEngine.UI.Image displayReticleImg = null; private int numDisplayReticles = 0; #endregion #region Private Variables - Gauges private int numDisplayGauges = 0; private RectTransform gaugesRectTfrm = null; private Vector3 tempGaugeOffset = Vector3.zero; #endregion #region Private Variables - Message private int numDisplayMessages = 0; private Vector3 tempMessageOffset; #endregion #region Private Variables - Cursor private bool isCursorVisible = true; private float cursorTimer = 0f; // Switch to using the New Input System if it is available #if SSC_UIS private Vector2 currentMousePosition = Vector2.zero; private Vector2 lastMousePosition = Vector2.zero; #else private Vector3 currentMousePosition = Vector3.zero; private Vector3 lastMousePosition = Vector3.zero; #endif #endregion #region Private Variables - Altitude and Speed private UnityEngine.UI.Image primaryOverlayImg = null; private RectTransform overlayPanel = null; private RectTransform altitudeTextRectTfrm = null; private Text altitudeText = null; private Vector3 altitudeInitPosition; private Vector3 altitudeCurrentPosition; private SCSMString altitudeString = null; private RectTransform airspeedTextRectTfrm = null; private Text airspeedText = null; private Vector3 airspeedInitPosition; private Vector3 airspeedCurrentPosition; private SCSMString airspeedString = null; #endregion #region Private Variables - Targets private int numDisplayTargets = 0; private RectTransform targetsRectTfrm = null; private Vector3 tempTargetOffset; private List tempIncludeFactionList = null; private List tempIncludeSquadronList = null; private SSCRadar sscRadar = null; #endregion #region Private Initialisation Methods // Start is called before the first frame update void Start() { if (initialiseOnStart) { Initialise(); } } #endregion #region Update Methods // Update is called once per frame void Update() { float dTime = Time.deltaTime; #region Flickering if (isFlickeringEndStateOn || isFlickeringEndStateOff) { flickerDurationTimer += dTime; // Disable flickering if (flickerDurationTimer > flickerDuration) { flickerHistory.Clear(); if (isFlickeringEndStateOn) { isFlickeringEndStateOn = false; // Restore the original brightness SetBrightness(flickerStartIntensity); ShowOrHideHUD(true); } // FlickeringOff else { isFlickeringEndStateOff = false; // Restore the original brightness brightness = flickerStartIntensity; ShowOrHideHUD(false); } } else if (isFlickerWaiting) { flickerInactiveTimer += dTime; if (flickerInactiveTimer > flickerInactiveTime) { isFlickerWaiting = false; flickerActiveTimer = 0f; flickerActiveTime = hudRandom != null ? hudRandom.Range(flickerMinActiveTime, flickerMaxActiveTime + 0.001f): 1f; ShowOrHideHUD(true); } } else { flickerActiveTimer += dTime; if (flickerActiveTimer > flickerActiveTime) { flickerHistory.Clear(); flickerHistory.Enqueue(0f); flickerInactiveTimer = 0f; flickerInactiveTime = hudRandom != null ? hudRandom.Range(flickerMinInactiveTime, flickerMaxInactiveTime + 0.001f) : 1f; isFlickerWaiting = true; ShowOrHideHUD(false); } else if (flickerVariableIntensity) { if (flickerSmoothing > 0) { // Remove the first value from the queue if (flickerHistory.Count > 0 && flickerHistory.Count > flickerSmoothing + 1) { flickerHistoryTotal -= flickerHistory.Dequeue(); } flickerIntensity = hudRandom.Range(0f, flickerStartIntensity * flickerMaxIntensity); flickerHistoryTotal += flickerIntensity; // Add the new value to the end of the queue flickerHistory.Enqueue(flickerIntensity); SetBrightness(flickerHistoryTotal / (float)flickerHistory.Count); } else { // No smoothing flickerIntensity = hudRandom.Range(0f, flickerStartIntensity * flickerMaxIntensity); SetBrightness(flickerIntensity); } } } } #endregion #region Check Window Resize if (IsHUDShown) { // Would be nice to not call this so often CheckHUDResize(true); } #endregion #region AutoHideCursor and Reticle cursor control if (autoHideCursor || (lockDisplayReticleToCursor && showActiveDisplayReticle)) { #if SSC_UIS currentMousePosition = UnityEngine.InputSystem.Mouse.current.position.ReadValue(); #else currentMousePosition = Input.mousePosition; #endif #region Auto-hide Cursor if (autoHideCursor) { if (isCursorVisible) { cursorTimer += dTime; // If use has move the mouse, reset the timer if (lastMousePosition != currentMousePosition) { lastMousePosition = currentMousePosition; cursorTimer = 0f; } // After hideCursorTime secs, hide it else if (cursorTimer > hideCursorTime) { ShowOrHideCursor(false); } } // Check if mouse has moved (does user wish to click on something?) else if (lastMousePosition != currentMousePosition) { lastMousePosition = currentMousePosition; ShowOrHideCursor(true); } } #endregion #region Local Display Reticle to Cursor if (IsInitialised && isMainCameraAssigned && lockDisplayReticleToCursor && showActiveDisplayReticle) { // It is safe to switch between Vector3 and Vector2 as each has an expicit // operator which either adds z = 0 or drops the z component. Vector2 viewPoint = mainCamera.ScreenToViewportPoint(currentMousePosition); // Convert 0.0 to 1.0 into -1.0 to 1.0. SetDisplayReticleOffset(viewPoint.x * 2f - 1f, viewPoint.y * 2f - 1f); // v1.2.8+ cursor should be hidden when reticle is following the cursor. if (isCursorVisible && !autoHideCursor && IsHUDShown) { ShowOrHideCursor(false); } } #endregion } #endregion #region Update Altitude, Speed, Heading if ((showAltitude || showAirspeed || isHeadingScrolling || isAttitudeScrolling) && IsHUDShown && sourceShip != null && sourceShip.ShipIsEnabled() && sourceShip.IsInitialised) { if (showAltitude) { /// TODO - round altitude to 100s altitudeString.Set((int)(sourceShip.shipInstance.TransformPosition.y - groundPlaneHeight)); if (altitudeText != null) { altitudeText.text = altitudeString.ToString(); } } if (showAirspeed) { // Show in km/h airspeedString.Set((int)(sourceShip.shipInstance.LocalVelocity.z*3.6f)); if (airspeedText != null) { airspeedText.text = airspeedString.ToString(); } } if (isHeadingScrolling) { Vector3 _shipFwd = sourceShip.shipInstance.TransformForward; Vector3 _headingPerpendicular = Vector3.Cross(Vector3.forward, _shipFwd); float _headingDir = -Vector3.Dot(_headingPerpendicular, Vector3.up); // Get the forward angle of the ship in WS (ignoring the Up axis) headingScrollImg.transform.position = headingInitPosition + new Vector3(Vector3.Angle(new Vector3(_shipFwd.x, 0f, _shipFwd.z), Vector3.forward) * Mathf.Sign(_headingDir) * headingPixelsPerDegree, 0f, 0f); } if (isAttitudeScrolling) { float _rollAngle = sourceShip.shipInstance.RollAngle; float _rollAngleRAD = -_rollAngle * Mathf.Deg2Rad; float _scrollDistance = -sourceShip.shipInstance.PitchAngle * attitudePixelsPerDegree; float _scrollRadius = _scrollDistance * Mathf.Sqrt(2 * (1 - Mathf.Cos(_rollAngleRAD))); float _scrollX = _scrollRadius * Mathf.Cos(_rollAngleRAD/2f); float _scrollY = _scrollRadius * Mathf.Sin(_rollAngleRAD/2f); if (_rollAngle > 0f) { // Flip values when rolling to the right _scrollX = -_scrollX; _scrollY = -_scrollY; } Vector3 attitudeScrollOffset = new Vector3(_scrollX, _scrollDistance - _scrollY, 0f); // Adjust the position and rotation attitudeScrollImg.transform.position = attitudeInitPosition + attitudeScrollOffset; // Rotate the panel so that the mask rotates at the same time. attitudeRectTfrm.localRotation = Quaternion.Euler(0f, 0f, _rollAngle); // Seems like we can adjust the image WS rotation at the same time, rather than having to do attitudeRectTfrm.localRotation. //attitudeScrollImg.transform.SetPositionAndRotation(attitudeInitPosition + attitudeScrollOffset, Quaternion.Euler(0f, 0f, _rollAngle)); //Debug.Log("[DEBUG] _rollAngle: " + _rollAngle.ToString("0.00") + " _rollAngleRAD: " + _rollAngleRAD.ToString("0.00") + // " _scrollDistance: " + _scrollDistance.ToString("0.00") + " _scrollRadius: " + _scrollRadius.ToString("0.00") + // " scrollX: " + _scrollX.ToString("0.00") + " scrollY: " + _scrollY.ToString("0.00")); } } #endregion #region Message updates if (IsHUDShown) { // NOTE: It might be cheaper to maintain an int[] of scrollable messages for (int dmIdx = 0; dmIdx < numDisplayMessages; dmIdx++) { DisplayMessage displayMsg = displayMessageList[dmIdx]; #region Fade-in Message if (displayMsg.fadeinTimer > 0f && displayMsg.fadeinDuration > 0f) { displayMsg.fadeinTimer -= dTime; if (displayMsg.fadeinTimer <= 0f) { displayMsg.fadeinTimer = 0f; } SetDisplayMessageTextFade(displayMsg, false); if (displayMsg.showBackground) { SetDisplayMessageBackgroundFade(displayMsg, false); } } #endregion #region Fade-out Message else if (displayMsg.fadeoutTimer > 0f && displayMsg.fadeoutDuration > 0f) { displayMsg.fadeoutTimer -= dTime; if (displayMsg.fadeoutTimer <= 0f) { displayMsg.fadeoutTimer = 0f; displayMsg.showMessage = false; } SetDisplayMessageTextFade(displayMsg, true); if (displayMsg.showBackground) { SetDisplayMessageBackgroundFade(displayMsg, true); } } #endregion if (displayMsg.scrollDirectionInt != DisplayMessage.ScrollDirectionNone) { ScrollMessage(displayMessageList[dmIdx]); } } } #endregion #region Target Position Updates if (autoUpdateTargetPositions && IsHUDShown) { UpdateTargetPositions(); } #endregion } #endregion #region Private and Internal Methods - General /// /// Show or hide the hardware (mouse) cursor in the game view /// NOTE: This will sometimes fail to turn off the cursor in the editor /// Game View when it doesn't have focus, but will work fine in a build. /// /// private void ShowOrHideCursor(bool isShown) { Cursor.visible = isShown; isCursorVisible = isShown; if (isShown) { cursorTimer = 0f; } } /// /// Show or Hide the HUD. /// IsHUDShown should never be true at runtime if IsInitialised is false /// /// private void ShowOrHideHUD(bool isShown) { if (IsInitialised) { IsHUDShown = isShown; hudPanel.gameObject.SetActive(isShown); } // HUD should never be shown at runtime if IsInitialised is false else { IsHUDShown = false; } } private IEnumerator UnlockCursor() { yield return new WaitForEndOfFrame(); Cursor.lockState = CursorLockMode.None; } /// /// Check to see if the window has been resized. Ideally should be /// called a max once per frame (less often would be nice). /// TODO - trigger a HUD refresh if changed. /// private void CheckHUDResize(bool isRefreshIfChanged) { cvsResolutionFull = GetHUDFullSize(true); cvsResolutionHalf.x = cvsResolutionFull.x * 0.5f; cvsResolutionHalf.y = cvsResolutionFull.y * 0.5f; if (cvsResolutionFull.x != prevResolutionFull.x || cvsResolutionFull.y != prevResolutionFull.y) { refResolution = GetHUDFullSize(false); cvsScaleFactor = canvas == null ? Vector3.one : canvas.transform.localScale; screenResolution = new Vector2(Screen.width, Screen.height); if (isRefreshIfChanged) { RefreshHUD(); } prevResolutionFull.x = cvsResolutionFull.x; prevResolutionFull.y = cvsResolutionFull.y; } } /// /// Return full the width and height of the HUD Panel. /// If the module hasn't been initialised it will return 1920x1080. /// Assume the dev hasn't changed the CanvasScaler reference resolution settings. /// When using the HUD canvas current dimensions, always set isScaled = true. /// Returns x,y values in pixels. /// /// /// private Vector2 GetHUDFullSize(bool isScaled) { // Default SSC scaler resolution is 1920x1080 if (isScaled) { if (IsInitialised || canvas != null) { // The default [width, height] * inversed scaled [width, height] //return new Vector2(canvas.pixelRect.width / canvas.scaleFactor, canvas.pixelRect.height / canvas.scaleFactor); return ((RectTransform)canvas.transform).sizeDelta; } // We don't know, so just return the original size... else { return new Vector2(1920f, 1080f); } } else { if (IsInitialised || canvasScaler != null) { return canvasScaler.referenceResolution; } else { return new Vector2(1920f, 1080f); } } } /// /// Return half the width and height of the HUD Panel. /// If the module hasn't been initialised it will return half 1920x1080. /// Assuming the dev hasn't changed it, it should always return the same /// when initialised. /// When using the HUD canvas current dimensions, always set isScaled = true. /// Returns x,y values in pixels. /// /// /// private Vector2 GetHUDHalfSize(bool isScaled) { // Default scaler resolution is 1920x1080 if (isScaled) { if (IsInitialised || canvas != null) { // The default [width, height] * inversed scaled [width, height] //return new Vector2(canvas.pixelRect.width / canvas.scaleFactor * 0.5f, canvas.pixelRect.height / canvas.scaleFactor * 0.5f); return ((RectTransform)canvas.transform).sizeDelta * 0.5f; } // We don't know, so just return the original half size... else { return new Vector2(960f, 540f); } } else { if (IsInitialised || canvasScaler != null) { return new Vector2(canvasScaler.referenceResolution.x * 0.5f, canvasScaler.referenceResolution.y * 0.5f); } else { return new Vector2(960f, 540f); } } } /// /// [INTERNAL ONLY] /// Call CheckHUDResize(false) before calling this method /// private void GetHUDPanels() { if (hudRectTfrm != null) { hudPanel = hudRectTfrm.transform; reticlePanel = SSCUtils.GetChildTransform(hudPanel, "DisplayReticlePanel", this.name); overlayPanel = SSCUtils.GetChildRectTransform(hudPanel, overlayPanelName, this.name); // For backward compatibility with earlier versions of the HUD1 prefab if (overlayPanel == null) { overlayPanel = SSCUtils.GetChildRectTransform(hudPanel, "AltitudePanel", this.name); if (overlayPanel != null) { overlayPanel.name = overlayPanelName; } } altitudeTextRectTfrm = SSCUtils.GetChildRectTransform(overlayPanel, "AltitudeText", this.name); airspeedTextRectTfrm = SSCUtils.GetChildRectTransform(overlayPanel, "AirSpeedText", this.name); targetsRectTfrm = GetTargetsPanel(); gaugesRectTfrm = GetGaugesPanel(); headingRectTfrm = GetHeadingPanel(); } } /// /// [INTERNAL ONLY] /// private void GetImgComponents() { if (reticlePanel != null) { displayReticleImg = reticlePanel.GetComponent(); } if (overlayPanel != null) { primaryOverlayImg = overlayPanel.GetComponent(); } } /// /// [INTERNAL ONLY] /// private void GetTextComponents() { if (altitudeTextRectTfrm != null) { altitudeText = altitudeTextRectTfrm.GetComponent(); } if (airspeedTextRectTfrm != null) { airspeedText = airspeedTextRectTfrm.GetComponent(); } } /// /// [INTERNAL ONLY] /// Used to change HUD outside play mode /// public void InitialiseEditorEssentials() { if (canvas == null) { canvas = GetCanvas; } hudRectTfrm = SSCUtils.GetChildRectTransform(transform, hudPanelName, this.name); if (hudRectTfrm != null) { // Call before GetHUDPanels() CheckHUDResize(false); GetHUDPanels(); GetImgComponents(); GetTextComponents(); InitialiseAttitude(true); InitialiseHeading(true); InitialiseMessages(); InitialiseTargets(); editorMode = true; ShowOrHideAttitude(showAttitude); ShowOrHideHeading(showHeading); } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: ShipDisplayModule.InitialiseEditorEssentials() could not find canvas HUDPanel. Did you start with the sample HUD prefab from Prefabs/Visuals folder?"); } #endif } /// /// The Overlay Panel contains the Overlay image and child objects /// Altitude Text and Airspeed Text. /// private void ShowOrHideOverlayPanel() { // Should the Overlay panel be enabled? if ((showAltitude || showAirspeed || showOverlay) && !overlayPanel.gameObject.activeSelf) { overlayPanel.gameObject.SetActive(true); } else if (!showAltitude && !showAirspeed && !showOverlay && overlayPanel.gameObject.activeSelf) { overlayPanel.gameObject.SetActive(false); } } /// /// Show or Hide the Overlay image on the HUD. Turn on HUD if required /// /// private void ShowOrHideOverlay(bool isShown) { if (IsInitialised) { showOverlay = isShown; ShowOrHideOverlayPanel(); if (primaryOverlayImg != null) { primaryOverlayImg.enabled = isShown; } if (showOverlay && !IsHUDShown) { ShowOrHideHUD(true); } } } #endregion #region Private and Internal Methods - Brightness /// /// Remembers the starting colours of various elements so that they can be /// used to apply brightness to those elements at runtime. /// Sets the initial brightness. /// NOTE: Brightness doesn't work so well if colour has low alpha to begin with. /// private void InitialiseBrightness() { if (displayReticleImg != null) { baseReticleColour = displayReticleImg.color; } if (primaryOverlayImg != null) { baseOverlayColour = primaryOverlayImg.color; } if (altitudeText != null) { baseAltitudeTextColour = altitudeText.color; } if (airspeedText != null) { baseAirspeedTextColour = airspeedText.color; } if (attitudeScrollImg != null) { baseAttitudePrimaryColour = attitudeScrollImg.color; } if (headingScrollImg != null) { baseHeadingPrimaryColour = headingScrollImg.color; } if (headingIndicatorImg != null) { baseHeadingIndicatorColour = headingIndicatorImg.color; } SetBrightness(brightness); } private void SetOverlayBrightness() { if (primaryOverlayImg != null) { // Skip more expensive HSV->RGBA and new Color op if possible if (brightness == 1f) { SSCUtils.Color32toColorNoAlloc(ref primaryColour, ref tempColour); primaryOverlayImg.color = tempColour; } else { primaryOverlayImg.color = baseOverlayColour.GetColorWithBrightness(brightness); } } } private void SetReticleBrightness() { if (displayReticleImg != null) { // Skip more expensive HSV->RGBA and new Color op if possible if (brightness == 1f) { SSCUtils.Color32toColorNoAlloc(ref activeDisplayReticleColour, ref tempColour); displayReticleImg.color = tempColour; } else { displayReticleImg.color = baseReticleColour.GetColorWithBrightness(brightness); } } } private void SetAltitudeTextBrightness() { if (altitudeText != null) { // Skip more expensive HSV->RGBA and new Color op if possible if (brightness == 1f) { SSCUtils.Color32toColorNoAlloc(ref altitudeTextColour, ref tempColour); altitudeText.color = tempColour; } else { altitudeText.color = baseAltitudeTextColour.GetColorWithBrightness(brightness); } } } private void SetAirSpeedTextBrightness() { if (airspeedText != null) { // Skip more expensive HSV->RGBA and new Color op if possible if (brightness == 1f) { SSCUtils.Color32toColorNoAlloc(ref airspeedTextColour, ref tempColour); airspeedText.color = tempColour; } else { airspeedText.color = baseAirspeedTextColour.GetColorWithBrightness(brightness); } } } private void SetAttitudeBrightness() { if (attitudeScrollImg != null) { // Skip more expensive HSV->RGBA and new Color op if possible if (brightness == 1f) { attitudeScrollImg.color = attitudePrimaryColour; } else { attitudeScrollImg.color = baseAttitudePrimaryColour.GetColorWithBrightness(brightness); } } } private void SetHeadingBrightness() { if (headingScrollImg != null) { // Skip more expensive HSV->RGBA and new Color op if possible if (brightness == 1f) { headingScrollImg.color = headingPrimaryColour; headingIndicatorImg.color = headingIndicatorColour; } else { headingScrollImg.color = baseHeadingPrimaryColour.GetColorWithBrightness(brightness); headingIndicatorImg.color = baseHeadingIndicatorColour.GetColorWithBrightness(brightness); } } } private void SetDisplayGaugeForegroundBrightness(DisplayGauge displayGauge) { if (displayGauge != null) { if (IsInitialised || editorMode) { if (displayGauge.CachedFgImgComponent != null) { if (brightness == 1f) { displayGauge.CachedFgImgComponent.color = displayGauge.foregroundColour; } else { displayGauge.CachedFgImgComponent.color = displayGauge.baseForegroundColour.GetColorWithBrightness(brightness); } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayGaugeForegroundBrightness - displayGauge.CachedFgImgComponent is null"); } #endif } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayGaugeForegroundBrightness - displayGauge is null"); } #endif } private void SetDisplayGaugeBackgroundBrightness(DisplayGauge displayGauge) { if (displayGauge != null) { if (IsInitialised || editorMode) { if (displayGauge.CachedBgImgComponent != null) { if (brightness == 1f) { displayGauge.CachedBgImgComponent.color = displayGauge.backgroundColour; } else { displayGauge.CachedBgImgComponent.color = displayGauge.baseBackgroundColour.GetColorWithBrightness(brightness); } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayGaugeBackgroundBrightness - displayGauge.CachedBgImgComponent is null"); } #endif } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayGaugeBackgroundBrightness - displayGauge is null"); } #endif } /// /// Adjust the brightness of the gauge text. If it is a numeric gauge with a label, /// also adjust the brightness of the label. /// /// private void SetDisplayGaugeTextBrightness(DisplayGauge displayGauge) { if (displayGauge != null) { if (IsInitialised || editorMode) { if (displayGauge.CachedTextComponent != null) { if (brightness == 1f) { displayGauge.CachedTextComponent.color = displayGauge.textColour; } else { displayGauge.CachedTextComponent.color = displayGauge.baseTextColour.GetColorWithBrightness(brightness); } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayGaugeTextBrightness - displayGauge.CachedTextComponent is null"); } #endif if (displayGauge.CachedLabelTextComponent != null) { if (brightness == 1f) { displayGauge.CachedLabelTextComponent.color = displayGauge.textColour; } else { displayGauge.CachedLabelTextComponent.color = displayGauge.baseTextColour.GetColorWithBrightness(brightness); } } } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayGaugeTextBrightness - displayGauge is null"); } #endif } private void SetDisplayMessageBackgroundBrightness(DisplayMessage displayMessage) { if (displayMessage != null) { if (IsInitialised || editorMode) { if (displayMessage.CachedBgImgComponent != null) { if (brightness == 1f) { displayMessage.CachedBgImgComponent.color = displayMessage.backgroundColour; } else { displayMessage.CachedBgImgComponent.color = displayMessage.baseBackgroundColour.GetColorWithBrightness(brightness); } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayMessageBackgroundBrightness - displayMessage.CachedBgImgComponent is null"); } #endif } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayMessageBackgroundBrightness - displayMessage is null"); } #endif } private void SetDisplayMessageBackgroundFade (DisplayMessage displayMessage, bool isFadeOut) { if (displayMessage != null) { if (IsInitialised || editorMode) { if (displayMessage.CachedBgImgComponent != null) { float fadeValue = easeInOutCurve.Evaluate(isFadeOut ? displayMessage.GetFadeOutValue() : displayMessage.GetFadeInValue()); displayMessage.CachedBgImgComponent.color = displayMessage.baseBackgroundColour.GetColorWithFadedBrightness(brightness, fadeValue); } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayMessageBackgroundFade - displayMessage.CachedBgImgComponent is null"); } #endif } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayMessageBackgroundFade - displayMessage is null"); } #endif } private void SetDisplayMessageTextBrightness(DisplayMessage displayMessage) { if (displayMessage != null) { if (IsInitialised || editorMode) { if (displayMessage.CachedTextComponent != null) { if (brightness == 1f) { displayMessage.CachedTextComponent.color = displayMessage.textColour; } else { displayMessage.CachedTextComponent.color = displayMessage.baseTextColour.GetColorWithBrightness(brightness); } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayMessageTextBrightness - displayMessage.CachedTextComponent is null"); } #endif } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayMessageTextBrightness - displayMessage is null"); } #endif } private void SetDisplayMessageTextFade (DisplayMessage displayMessage, bool isFadeOut) { if (displayMessage != null) { if (IsInitialised || editorMode) { if (displayMessage.CachedTextComponent != null) { float fadeValue = easeInOutCurve.Evaluate(isFadeOut ? displayMessage.GetFadeOutValue() : displayMessage.GetFadeInValue()); //Debug.Log("[DEBUG] msg fadeinTimer: " + displayMessage.fadeinTimer + " fadeValue: " + fadeValue); displayMessage.CachedTextComponent.color = displayMessage.baseTextColour.GetColorWithFadedBrightness(brightness, fadeValue); } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayMessageTextFade - displayMessage.CachedTextComponent is null"); } #endif } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayMessageTextFade - displayMessage is null"); } #endif } /// /// Set the brightness of all slots (copies) of a DisplayTarget /// /// private void SetDisplayTargetBrightness(DisplayTarget displayTarget) { if (displayTarget != null) { if (IsInitialised || editorMode) { for (int sIdx = 0; sIdx < displayTarget.maxNumberOfTargets; sIdx++) { if (displayTarget.displayTargetSlotList[sIdx].CachedImgComponent != null) { if (brightness == 1f) { displayTarget.displayTargetSlotList[sIdx].CachedImgComponent.color = displayTarget.reticleColour; } else { displayTarget.displayTargetSlotList[sIdx].CachedImgComponent.color = displayTarget.baseReticleColour.GetColorWithBrightness(brightness); } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayTargetBrightness - displayTarget slot " + sIdx + " CachedImgComponent is null"); } #endif } } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayTargetBrightness - displayTarget is null"); } #endif } #endregion #region Private or Internal Methods - Reticle /// /// Show or Hide the reticle. Turn on the HUD if required. /// Always hide the cursor (hardware pointer) when the reticle is shown. /// /// private void ShowOrHideReticle(bool isShown) { if (IsInitialised) { showActiveDisplayReticle = isShown; if (isShown) { // Check the active display reticle if (guidHashActiveDisplayReticle == 0) { showActiveDisplayReticle = false; currentDisplayReticle = null; LoadDisplayReticleSprite(currentDisplayReticle); #if UNITY_EDITOR Debug.LogWarning("ShipDisplayModule - could not show the Display Rectile because there was no active rectile"); #endif } // If there is no current reticle, or if we need to change the reticle, // look up the hash value and load the sprite (if there is a match) else if (currentDisplayReticle == null || (currentDisplayReticle.guidHash != guidHashActiveDisplayReticle)) { currentDisplayReticle = displayReticleList.Find(dr => dr.guidHash == guidHashActiveDisplayReticle); LoadDisplayReticleSprite(currentDisplayReticle); } } if (showActiveDisplayReticle && lockDisplayReticleToCursor) { Cursor.visible = false; } reticlePanel.gameObject.SetActive(showActiveDisplayReticle); if (showActiveDisplayReticle && !IsHUDShown) { ShowOrHideHUD(true); } } } /// /// Load the reticle sprite into the UI image on the panel /// /// private void LoadDisplayReticleSprite(DisplayReticle displayReticle) { if (displayReticle != null) { displayReticleImg.sprite = displayReticle.primarySprite; } else { displayReticleImg.sprite = null; } } #endregion #region Private or Internal Methods - Overlay, Altitude and Speed /// /// Show or Hide the Altitude indicator on the HUD. Turn on HUD if required. /// /// private void ShowOrHideAltitude(bool isShown) { if (IsInitialised) { showAltitude = isShown; ShowOrHideOverlayPanel(); if (altitudeText != null) { altitudeText.gameObject.SetActive(showAltitude); } if (showAltitude && !IsHUDShown) { ShowOrHideHUD(true); } } } /// /// Show or Hide the Air Speed indicator on the HUD. Turn on HUD if required /// /// private void ShowOrHideAirSpeed(bool isShown) { if (IsInitialised) { showAirspeed = isShown; ShowOrHideOverlayPanel(); if (airspeedText != null) { airspeedText.gameObject.SetActive(showAirspeed); } if (showAirspeed && !IsHUDShown) { ShowOrHideHUD(true); } } } #endregion #region Private or Internal Methods - Attitude /// /// Get or Create the scrollable attitude. /// The attitude image is masked by the parent mask image. /// private void GetOrCreateAttitude() { if (attitudeRectTfrm == null) { attitudeRectTfrm = GetAttitudePanel(); } if (attitudeRectTfrm != null) { // Find or create the Mask for the scrollable attitude if (attitudeMaskImg == null) { attitudeMaskImg = attitudeRectTfrm.GetComponent(); if (attitudeMaskImg == null) { attitudeMaskImg = attitudeRectTfrm.gameObject.AddComponent(); UnityEngine.UI.Mask attitudeMask = attitudeRectTfrm.gameObject.AddComponent(); if (attitudeMaskImg != null) { attitudeMaskImg.raycastTarget = false; attitudeMaskImg.preserveAspect = false; } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.GetOrCreateAttitude() - could not add mask image component to Attitude panel"); } #endif if (attitudeMask != null) { attitudeMask.showMaskGraphic = false; } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.GetOrCreateAttitude() - could not add mask component to Attitude panel"); } #endif } } } // Find or create the scrollable panel and image. This is a child of the attitude panel and mask if (attitudeScrollImg == null) { // The panel width and height are 1.0 which is the full resolution e.g. 1920x1080 attitudeScrollPanel = SSCUtils.GetOrCreateChildRectTransform(attitudeRectTfrm, cvsResolutionFull, attitudeScrollName, 0f, 0f, 1f, 1f, 0.5f, 0.5f, 0.5f, 0.5f); if (attitudeScrollPanel != null) { attitudeScrollImg = attitudeScrollPanel.GetComponent(); if (attitudeScrollImg == null) { attitudeScrollImg = attitudeScrollPanel.gameObject.AddComponent(); if (attitudeScrollImg != null) { attitudeScrollImg.raycastTarget = false; attitudeScrollImg.preserveAspect = true; } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.GetOrCreateAttitude() - could not add background to Attitude panel"); } #endif } } } // TODO - Find or create the small indicator panel and image (NOT SURE IF WE NEED THIS YET). } /// /// Initialise the scrollable Attitude. /// /// private void InitialiseAttitude(bool isInitialising) { GetOrCreateAttitude(); SetDisplayAttitudeScrollSprite(attitudeScrollSprite); SetDisplayAttitudeMaskSprite(attitudeMaskSprite); SetDisplayAttitudeOffset(attitudeOffsetX, attitudeOffsetY); SetDisplayAttitudeSize(attitudeWidth, attitudeHeight); if (attitudeScrollImg != null) { if (isInitialising) { attitudeInitPosition = attitudeScrollImg.transform.position; } RefreshAttitudeAfterResize(); } } /// /// Show or hide the scrollable Attitude display. Turn on HUD if required. /// /// private void ShowOrHideAttitude(bool isShown) { if (IsInitialised || IsEditorMode) { showAttitude = isShown; if (attitudeRectTfrm != null) { attitudeRectTfrm.gameObject.SetActive(showAttitude); } if (attitudeScrollImg != null) { attitudeScrollImg.enabled = isShown; isAttitudeScrolling = isShown; } else { isAttitudeScrolling = false; } if (showAttitude && !IsHUDShown) { ShowOrHideHUD(true); } } } #endregion #region Private or Internal Methods - Gauges /// /// [INTERNAL USE ONLY] /// Create a new Gauge RectTransform under the HUD GaugesPanel in the hierarchy /// Add a background Image /// Add a (foreground) fillable iamge /// Add a text panel /// /// /// public RectTransform CreateGaugePanel(DisplayGauge displayGauge) { RectTransform gaugePanel = null; if (gaugesRectTfrm == null) { gaugesRectTfrm = GetGaugesPanel(); } if (gaugesRectTfrm != null && displayGauge != null) { float panelWidth = displayGauge.displayWidth; float panelHeight = displayGauge.displayHeight; gaugePanel = SSCUtils.GetOrCreateChildRectTransform(gaugesRectTfrm, cvsResolutionFull, "Gauge_" + displayGauge.guidHash, 0f, 0f, panelWidth, panelHeight, 0.5f, 0.5f, 0.5f, 0.5f); if (gaugePanel != null) { Image bgimgComponent = gaugePanel.gameObject.AddComponent(); if (bgimgComponent != null) { bgimgComponent.raycastTarget = false; } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.CreateGaugePanel() - could not add background to Gauge panel"); } #endif RectTransform gaugeAmtPanel = SSCUtils.GetOrCreateChildRectTransform(gaugePanel, cvsResolutionFull, "GaugeAmount", 0f, 0f, panelWidth, panelHeight, 0.5f, 0.5f, 0.5f, 0.5f); if (gaugeAmtPanel != null) { Image amtimgComponent = gaugeAmtPanel.gameObject.AddComponent(); if (amtimgComponent != null) { amtimgComponent.raycastTarget = false; amtimgComponent.type = Image.Type.Filled; amtimgComponent.fillMethod = (Image.FillMethod)displayGauge.fillMethod; } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.CreateGaugePanel() - could not add fill image to Gauge panel"); } #endif } RectTransform gaugeTxtPanel = SSCUtils.GetOrCreateChildRectTransform(gaugePanel, cvsResolutionFull, "GaugeText", 0f, 0f, panelWidth, panelHeight, 0.5f, 0.5f, 0.5f, 0.5f); if (gaugeTxtPanel != null) { UnityEngine.UI.Text textComponent = gaugeTxtPanel.gameObject.AddComponent(); if (textComponent != null) { textComponent.raycastTarget = false; textComponent.resizeTextForBestFit = true; // Text is add in InitialiseGauge(). textComponent.text = string.Empty; if (Application.isPlaying) { textComponent.font = SSCUtils.GetDefaultFont(); } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.CreateGaugePanel() - could not add text to Gauge panel"); } #endif } if (IsInitialised || editorMode) { // Update the number of gauges. Count the list rather than assume the gauge didn't already exist. numDisplayGauges = displayGaugeList == null ? 0 : displayGaugeList.Count; InitialiseGauge(displayGauge); } } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: ShipDisplayModule.CreateGaugePanel() - displayGauge is null or could not find or create " + gaugesPanelName); } #endif return gaugePanel; } /// /// [INTERNAL USE ONLY] /// Find the Gauge RectTransform under the HUD in the hierarchy. /// /// /// private RectTransform GetGaugePanel(int guidHash) { if (gaugesRectTfrm == null) { gaugesRectTfrm = GetGaugesPanel(); } if (gaugesRectTfrm != null) { return SSCUtils.GetChildRectTransform(gaugesRectTfrm.transform, "Gauge_" + guidHash, this.name); } else { return null; } } /// /// Get or create a gauge label. This only applies to numeric gauges with an additional /// text component or label. /// If it is the wrong gauge type, remove any secondary label. /// This will set or update the displayGauge.CachedLabelTextComponent. /// /// private void GetOrCreateOrRemoveGaugeLabel(DisplayGauge displayGauge) { UnityEngine.UI.Text textComponent = null; if (displayGauge != null) { int gaugeTypeInt = (int)displayGauge.gaugeType; // Is the component already cached? if (displayGauge.CachedLabelTextComponent != null) { // If this gauge should have a secondary label, set it to the cached component if (gaugeTypeInt == DisplayGauge.DGTypeNumberWithLabel1Int) { textComponent = displayGauge.CachedLabelTextComponent; } else { // A secondary label (Text component) does not apply to this gauge, so remove it. if (Application.isPlaying) { Destroy(displayGauge.CachedLabelTextComponent.gameObject); } else { #if UNITY_EDITOR UnityEditor.Undo.DestroyObjectImmediate(displayGauge.CachedLabelTextComponent.gameObject); #else DestroyImmediate(displayGauge.CachedLabelTextComponent.gameObject); #endif } // Clear the cached component displayGauge.CachedLabelTextComponent = null; } } else if (gaugeTypeInt == DisplayGauge.DGTypeNumberWithLabel1Int) { // Not cached, so attempt to find it RectTransform gaugePanel = displayGauge.CachedGaugePanel == null ? GetGaugePanel(displayGauge.guidHash) : displayGauge.CachedGaugePanel; if (gaugePanel != null) { float panelWidth = displayGauge.displayWidth; float panelHeight = displayGauge.displayHeight / 2f; RectTransform gaugeLabelPanel = SSCUtils.GetOrCreateChildRectTransform(gaugePanel, cvsResolutionFull, "GaugeLabel", 0f, 0f, panelWidth, panelHeight, 0.5f, 0.5f, 0.5f, 0.5f); if (gaugeLabelPanel != null) { textComponent = gaugeLabelPanel.GetComponent(); if (textComponent == null) { // Text component doesn't exist, so add and configure it textComponent = gaugeLabelPanel.gameObject.AddComponent(); if (textComponent != null) { textComponent.raycastTarget = false; textComponent.resizeTextForBestFit = true; textComponent.text = displayGauge.gaugeLabel; if (Application.isPlaying) { textComponent.font = SSCUtils.GetDefaultFont(); } displayGauge.CachedLabelTextComponent = textComponent; } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.CreateGaugePanel() - could not add text to Gauge panel"); } #endif } else { displayGauge.CachedLabelTextComponent = textComponent; } } } } } } /// /// Cache the DisplayGauge RectTransform, Image and Text component. /// Set the Text colour. /// /// private void InitialiseGauge(DisplayGauge displayGauge) { displayGauge.CachedGaugePanel = GetGaugePanel(displayGauge.guidHash); // Cache FillMethod is None to avoid enum lookup at runtime displayGauge.isFillMethodNone = displayGauge.fillMethod == DisplayGauge.DGFillMethod.None; if (displayGauge.CachedGaugePanel != null) { displayGauge.CachedBgImgComponent = displayGauge.CachedGaugePanel.GetComponent(); if (displayGauge.CachedBgImgComponent != null) { // If there is a sprite but it hasn't been loaded into the image, do it now if (displayGauge.backgroundSprite != null && displayGauge.CachedBgImgComponent.sprite == null) { SetDisplayGaugeBackgroundSprite(displayGauge, displayGauge.backgroundSprite); } // Cache the gauge foreground Image component if (tempImgList == null) { tempImgList = new List(1); } displayGauge.CachedGaugePanel.GetComponentsInChildren(tempImgList); if (tempImgList.Count > 0) { // GetComponentsInChildren will also return the background image which we don't want tempImgList.Remove(displayGauge.CachedBgImgComponent); if (tempImgList.Count > 0) { displayGauge.CachedFgImgComponent = tempImgList[0]; // If there is a sprite but it hasn't been loaded into the image, do it now if (displayGauge.foregroundSprite != null && displayGauge.CachedFgImgComponent.sprite == null) { SetDisplayGaugeForegroundSprite(displayGauge, displayGauge.foregroundSprite); } SetDisplayGaugeValue(displayGauge, displayGauge.gaugeValue); } } } // Cache the gauge Text component if (tempTextList == null) { tempTextList = new List(2); } displayGauge.CachedGaugePanel.GetComponentsInChildren(tempTextList); if (tempTextList.Count > 0) { displayGauge.CachedTextComponent = tempTextList[0]; // NOTE: The order may not be guaranteed here so this might or might not be an issue // with numeric gauges. if (tempTextList.Count > 1) { displayGauge.CachedLabelTextComponent = tempTextList[1]; } } // SetDisplayGaugeForegroundColour does not update the baseForegroundColour if the foregroundColour has not changed. So do it in here instead displayGauge.baseForegroundColour.Set(displayGauge.foregroundColour.r, displayGauge.foregroundColour.g, displayGauge.foregroundColour.b, displayGauge.foregroundColour.a, true); SetDisplayGaugeForegroundBrightness(displayGauge); // SetDisplayGaugeBackgroundColour does not update the baseBackgroundColour if the backgroundColour has not changed. So do it in here instead displayGauge.baseBackgroundColour.Set(displayGauge.backgroundColour.r, displayGauge.backgroundColour.g, displayGauge.backgroundColour.b, displayGauge.backgroundColour.a, true); SetDisplayGaugeBackgroundBrightness(displayGauge); // SetDisplayGaugeTextColour does not update the baseTextColour if the textColour has not changed. So do it in here instead displayGauge.baseTextColour.Set(displayGauge.textColour.r, displayGauge.textColour.g, displayGauge.textColour.b, displayGauge.textColour.a, true); SetDisplayGaugeTextBrightness(displayGauge); SetDisplayGaugeText(displayGauge, displayGauge.gaugeString); SetDisplayGaugeTextAlignment(displayGauge, displayGauge.textAlignment); SetDisplayGaugeTextFontStyle(displayGauge, (UnityEngine.FontStyle)displayGauge.fontStyle); SetDisplayGaugeTextFontSize(displayGauge, displayGauge.isBestFit, displayGauge.fontMinSize, displayGauge.fontMaxSize); if (displayGauge.CachedLabelTextComponent != null) { SetDisplayGaugeLabel(displayGauge, displayGauge.gaugeLabel); SetDisplayGaugeLabelAlignment(displayGauge, displayGauge.labelAlignment); } SetDisplayGaugeSize(displayGauge, displayGauge.displayWidth, displayGauge.displayHeight); SetDisplayGaugeOffset(displayGauge, displayGauge.offsetX, displayGauge.offsetY); // By default gauges are initialised off. This is to prevent them suddenly appearing when first created. if (Application.isPlaying) { displayGauge.CachedGaugePanel.gameObject.SetActive(false); } } } /// /// Initialise all the DisplayGauges by caching the RectTransforms. /// private void InitialiseGauges() { int _numDisplayGauges = GetNumberDisplayGauges; if (_numDisplayGauges > 0) { RectTransform hudRectTfrm = GetHUDPanel(); if (hudRectTfrm != null) { CheckHUDResize(false); for (int dgIdx = 0; dgIdx < _numDisplayGauges; dgIdx++) { InitialiseGauge(displayGaugeList[dgIdx]); } if (_numDisplayGauges > 0) { RefreshGaugesSortOrder(); } } #if UNITY_EDITOR else { Debug.LogWarning("ShipDisplayModule.InitialiseGauges() - could not find HUD Panel"); } #endif } } /// /// Show or Hide the Display Gauge on the HUD. Turn on the HUD if required. /// /// /// private void ShowOrHideGauge(DisplayGauge displayGauge, bool isShown) { if (IsInitialised) { if (displayGauge.CachedGaugePanel != null) { displayGauge.showGauge = isShown; displayGauge.CachedGaugePanel.gameObject.SetActive(isShown); } if (isShown && !IsHUDShown) { ShowOrHideHUD(true); } } } /// /// Show or hide all gauges /// /// private void ShowOrHideGauges(bool isShown) { if (IsInitialised) { for (int dmIdx = 0; dmIdx < numDisplayGauges; dmIdx++) { ShowOrHideGauge(displayGaugeList[dmIdx], isShown); } } } /// /// Show or Hide all gauges based on their current settings /// private void ShowOrHideGauges() { if (IsInitialised) { for (int dmIdx = 0; dmIdx < numDisplayGauges; dmIdx++) { DisplayGauge displayGauge = displayGaugeList[dmIdx]; ShowOrHideGauge(displayGauge, displayGauge.showGauge); } } } #endregion #region Private or Internal Methods - Heading /// /// Initialise the scrollable heading or compass. /// /// private void InitialiseHeading(bool isInitialising) { GetOrCreateHeading(); SetDisplayHeadingScrollSprite(headingScrollSprite); SetDisplayHeadingMaskSprite(headingMaskSprite); SetDisplayHeadingIndicatorSprite(headingIndicatorSprite); SetDisplayHeadingOffset(headingOffsetX, headingOffsetY); SetDisplayHeadingSize(headingWidth, headingHeight); if (isInitialising && headingScrollImg != null) { headingInitPosition = headingScrollImg.transform.position; // The heading img should have 2 sets of 0-360 deg to avoid scrolling partly out of view. // The image is autoscaled to the full width of the default HUD screen size. headingPixelsPerDegree = cvsResolutionFull.x / 720f; } } /// /// Get or Create the scrollable heading. /// The heading image is masked by the parent mask image. /// private void GetOrCreateHeading() { if (headingRectTfrm == null) { headingRectTfrm = GetHeadingPanel(); } if (headingRectTfrm != null) { // Find or create the Mask for the scrollable heading if (headingMaskImg == null) { headingMaskImg = headingRectTfrm.GetComponent(); if (headingMaskImg == null) { headingMaskImg = headingRectTfrm.gameObject.AddComponent(); UnityEngine.UI.Mask headingMask = headingRectTfrm.gameObject.AddComponent(); if (headingMaskImg != null) { headingMaskImg.raycastTarget = false; headingMaskImg.preserveAspect = false; } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.GetOrCreateHeading() - could not add mask image component to Heading panel"); } #endif if (headingMask != null) { headingMask.showMaskGraphic = false; } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.GetOrCreateHeading() - could not add mask component to Heading panel"); } #endif } } // Find or create the scrollable panel and image. This is a child of the heading panel and mask if (headingScrollImg == null) { // The panel width and height are 1.0 which is the full resolution e.g. 1920x1080 headingScrollPanel = SSCUtils.GetOrCreateChildRectTransform(headingRectTfrm, cvsResolutionFull, headingScrollName, 0f, 0f, 1f, 1f, 0.5f, 0.5f, 0.5f, 0.5f); if (headingScrollPanel != null) { headingScrollImg = headingScrollPanel.GetComponent(); if (headingScrollImg == null) { headingScrollImg = headingScrollPanel.gameObject.AddComponent(); if (headingScrollImg != null) { headingScrollImg.raycastTarget = false; headingScrollImg.preserveAspect = true; } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.GetOrCreateHeading() - could not add background to Heading panel"); } #endif } } } // Find or create the small indicator panel and image. if (headingIndicatorImg == null) { // The panel width and height are 1.0 which is the full resolution e.g. 1920x1080 headingIndicatorPanel = SSCUtils.GetOrCreateChildRectTransform(headingRectTfrm, cvsResolutionFull, headingIndicatorName, 0f, 0f, 16f / cvsResolutionFull.x, 16f / cvsResolutionFull.y, 0.5f, 0.5f, 0.5f, 0.5f); if (headingIndicatorPanel != null) { headingIndicatorImg = headingIndicatorPanel.GetComponent(); if (headingIndicatorImg == null) { headingIndicatorImg = headingIndicatorPanel.gameObject.AddComponent(); if (headingIndicatorImg != null) { headingIndicatorImg.raycastTarget = false; headingIndicatorImg.preserveAspect = true; } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.GetOrCreateHeading() - could not add indicator to Heading panel"); } #endif } } } } } /// /// Show or Hide the scrollable Heading on the HUD. Turn on HUD if required. /// /// private void ShowOrHideHeading(bool isShown) { if (IsInitialised || IsEditorMode) { showHeading = isShown; if (headingRectTfrm != null) { headingRectTfrm.gameObject.SetActive(showHeading); } if (headingScrollImg != null) { headingScrollImg.enabled = isShown; isHeadingScrolling = isShown; } else { isHeadingScrolling = false; } if (showHeading && !IsHUDShown) { ShowOrHideHUD(true); } } } /// /// Show or Hide the small Heading indicator on the HUD. /// /// private void ShowOrHideHeadingIndicator(bool isShown) { if (IsInitialised || IsEditorMode) { showHeadingIndicator = isShown; if (headingIndicatorImg != null) { headingIndicatorImg.enabled = isShown; } } } #endregion #region Private or Internal Methods - Messages /// /// Cache the DisplayMessage RectTransform, Image and Text component. /// Set the Text colour. /// /// /// private void InitialiseMessage(DisplayMessage displayMessage, RectTransform hudRectTfrm) { displayMessage.CachedMessagePanel = GetMessagePanel(displayMessage.guidHash, hudRectTfrm); displayMessage.scrollDirectionInt = (int)displayMessage.scrollDirection; displayMessage.scrollOffsetX = 0f; displayMessage.scrollOffsetY = 0f; if (displayMessage.CachedMessagePanel != null) { displayMessage.CachedBgImgComponent = displayMessage.CachedMessagePanel.GetComponent(); if (displayMessage.CachedBgImgComponent != null) { displayMessage.CachedBgImgComponent.enabled = displayMessage.showBackground; } // Cache the message Text component if (tempTextList == null) { tempTextList = new List(1); } displayMessage.CachedMessagePanel.GetComponentsInChildren(tempTextList); if (tempTextList.Count > 0) { displayMessage.CachedTextComponent = tempTextList[0]; } // SetDisplayMessageBackgroundColour does not update the baseBackgroundColour if the backgroundColour has not changed. So do it in here instead displayMessage.baseBackgroundColour.Set(displayMessage.backgroundColour.r, displayMessage.backgroundColour.g, displayMessage.backgroundColour.b, displayMessage.backgroundColour.a, true); SetDisplayMessageBackgroundBrightness(displayMessage); // SetDisplayMessageTextColour does not update the baseTextColour if the textColour has not changed. So do it in here instead displayMessage.baseTextColour.Set(displayMessage.textColour.r, displayMessage.textColour.g, displayMessage.textColour.b, displayMessage.textColour.a, true); SetDisplayMessageTextBrightness(displayMessage); SetDisplayMessageText(displayMessage, displayMessage.messageString); SetDisplayMessageTextAlignment(displayMessage, displayMessage.textAlignment); SetDisplayMessageTextFontSize(displayMessage, displayMessage.isBestFit, displayMessage.fontMinSize, displayMessage.fontMaxSize); SetDisplayMessageSize(displayMessage, displayMessage.displayWidth, displayMessage.displayHeight); if (displayMessage.scrollDirectionInt != DisplayMessage.ScrollDirectionNone) { // scrollWidth/Height should be the same as those used in ScrollMessage(..) // The left/right and top/bottom scroll limits can be ignored OR can consider the width/height of the message. // Fullscreen scrolling = +/- half width of message + half the width of canvas float scrollWidth = displayMessage.isScrollFullscreen ? displayMessage.displayWidth * cvsResolutionHalf.x + cvsResolutionHalf.x : displayMessage.displayWidth * cvsResolutionFull.x; // Fullscreen scrolling = +/- half height of message + half the height of canvas float scrollHeight = displayMessage.isScrollFullscreen ? displayMessage.displayHeight * cvsResolutionHalf.y + cvsResolutionHalf.y : displayMessage.displayHeight * cvsResolutionFull.y; // Start at beginning of scrolling position (e.g. Offscreen left/right) if (displayMessage.scrollDirectionInt == DisplayMessage.ScrollDirectionLR) { displayMessage.scrollOffsetX = -scrollWidth; } else if (displayMessage.scrollDirectionInt == DisplayMessage.ScrollDirectionRL) { displayMessage.scrollOffsetX = scrollWidth; } if (displayMessage.scrollDirectionInt == DisplayMessage.ScrollDirectionBT) { displayMessage.scrollOffsetY = -scrollHeight; } else if (displayMessage.scrollDirectionInt == DisplayMessage.ScrollDirectionTB) { displayMessage.scrollOffsetY = scrollHeight; } } SetDisplayMessageOffset(displayMessage, displayMessage.offsetX, displayMessage.offsetY); // By default messages are initialised off. This is to prevent them suddenly appearing when first created. if (Application.isPlaying) { displayMessage.CachedMessagePanel.gameObject.SetActive(false); } } } /// /// Initialise all the DisplayMessages by caching the RectTransforms. /// private void InitialiseMessages() { int _numDisplayMessages = GetNumberDisplayMessages; if (_numDisplayMessages > 0) { RectTransform hudRectTfrm = GetHUDPanel(); if (hudRectTfrm != null) { CheckHUDResize(false); for (int dmIdx = 0; dmIdx < _numDisplayMessages; dmIdx++) { InitialiseMessage(displayMessageList[dmIdx], hudRectTfrm); } if (_numDisplayMessages > 0) { RefreshMessagesSortOrder(); } } #if UNITY_EDITOR else { Debug.LogWarning("ShipDisplayModule.InitialiseMessages() - could not find HUD Panel"); } #endif } } /// /// Show or Hide the Display Message on the HUD. Turn on the HUD if required. /// When fading out a message, it isn't instantly turned off. /// Option to override the fade settings and instantly hide or show the message. /// /// /// /// private void ShowOrHideMessage(DisplayMessage displayMessage, bool isShown, bool isOverrideFade = false) { if (IsInitialised) { if (displayMessage.CachedMessagePanel != null) { if (isShown) { if (isOverrideFade) { displayMessage.fadeinTimer = 0f; } // Is a fade-out already in progress? else if (displayMessage.fadeoutDuration > 0f && displayMessage.fadeoutTimer > 0f) { // (1 - Normalise amount faded out) * fade in duration. displayMessage.fadeinTimer = (1f - (displayMessage.fadeoutTimer / displayMessage.fadeoutDuration)) * displayMessage.fadeinDuration; } else { displayMessage.fadeinTimer = displayMessage.fadeinDuration; if (displayMessage.fadeinTimer > 0f) { // Instantly update to avoid flashing the faded in message before Update() is called. SetDisplayMessageTextFade(displayMessage, false); if (displayMessage.showBackground) { SetDisplayMessageBackgroundFade(displayMessage, false); } } } // Should never be fading out when turning the message on displayMessage.fadeoutTimer = 0f; } else if (isOverrideFade) { displayMessage.fadeoutTimer = 0f; } else if (displayMessage.fadeoutDuration > 0f) { // Is a fade-in already in progress? if (displayMessage.fadeinDuration > 0f && displayMessage.fadeinTimer > 0f) { // (1 - Normalise amount faded in) * fade out duration. displayMessage.fadeoutTimer = (1f - (displayMessage.fadeinTimer / displayMessage.fadeinDuration)) * displayMessage.fadeoutDuration; } else { displayMessage.fadeoutTimer = displayMessage.fadeoutDuration; } // Should never be fading in when turning the message off displayMessage.fadeinTimer = 0f; // Override instantly turning off the message isShown = true; } else { displayMessage.showMessage = false; } displayMessage.showMessage = isShown; displayMessage.CachedMessagePanel.gameObject.SetActive(isShown); } if (isShown && !IsHUDShown) { ShowOrHideHUD(true); } } } /// /// Show or hide all messages. /// Option to override fade settings and instantly show or hide. /// /// /// private void ShowOrHideMessages(bool isShown, bool isOverrideFade = false) { if (IsInitialised) { for (int dmIdx = 0; dmIdx < numDisplayMessages; dmIdx++) { ShowOrHideMessage(displayMessageList[dmIdx], isShown, isOverrideFade); } } } /// /// Show or Hide all messages based on their current settings. /// This is called by /// private void ShowOrHideMessages() { if (IsInitialised) { for (int dmIdx = 0; dmIdx < numDisplayMessages; dmIdx++) { DisplayMessage displayMessage = displayMessageList[dmIdx]; // Shown messages should fade in if required. // Hidden messages should be instantly hidden. ShowOrHideMessage(displayMessage, displayMessage.showMessage, !displayMessage.showMessage); } } } /// /// [INTERNAL USE ONLY] /// Find the Message RectTransform under the HUD in the hierarchy. /// If hudRectTrfm is null, it will attempt to find it. /// /// /// /// private RectTransform GetMessagePanel(int guidHash, RectTransform hudRectTrfm) { if (hudRectTfrm == null) { hudRectTfrm = GetHUDPanel(); } if (hudRectTfrm != null) { return SSCUtils.GetChildRectTransform(hudRectTfrm.transform, "Message_" + guidHash, this.name); } else { return null; } } /// /// [INTERNAL USE ONLY] /// Create a new Message RectTransform under the HUD in the hierarchy /// /// /// /// public RectTransform CreateMessagePanel(DisplayMessage displayMessage, RectTransform hudRectTfrm) { RectTransform messagePanel = null; if (displayMessage != null) { float panelWidth = displayMessage.displayWidth; float panelHeight = displayMessage.displayHeight; //Canvas _canvas = GetCanvas; //Vector3 _canvasScale = _canvas == null ? Vector3.one : _canvas.transform.localScale; if (IsInitialised) { numDisplayMessages++; } messagePanel = SSCUtils.GetOrCreateChildRectTransform(hudRectTfrm, cvsResolutionFull, "Message_" + displayMessage.guidHash, 0f, 0f, panelWidth, panelHeight, 0.5f, 0.5f, 0.5f, 0.5f); if (messagePanel != null) { Image imgComponent = messagePanel.gameObject.AddComponent(); if (imgComponent != null) { imgComponent.raycastTarget = false; } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.CreateMessagePanel() - could not add background to Message panel"); } #endif RectTransform messageTxtPanel = SSCUtils.GetOrCreateChildRectTransform(messagePanel, cvsResolutionFull, "MessageText", 0f, 0f, panelWidth, panelHeight, 0.5f, 0.5f, 0.5f, 0.5f); if (messageTxtPanel != null) { Text textComponent = messageTxtPanel.gameObject.AddComponent(); if (textComponent != null) { textComponent.raycastTarget = false; textComponent.resizeTextForBestFit = true; // Text is addd in InitialiseMessage(). textComponent.text = string.Empty; if (Application.isPlaying) { textComponent.font = SSCUtils.GetDefaultFont(); } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.CreateMessagePanel() - could not add text to Message panel"); } #endif } if (IsInitialised || editorMode) { InitialiseMessage(displayMessage, hudRectTfrm); } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.CreateMessagePanel() - could not create Message panel"); } #endif } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: ShipDisplayModule.CreateMessagePanel() - displayMessage is null"); } #endif return messagePanel; } /// /// Show or hide the message background by enabling or disabling the image script /// /// /// private void ShowOrHideDisplayMessageBackground(DisplayMessage displayMessage, bool isShown) { if (IsInitialised || editorMode) { if (!editorMode) { displayMessage.showBackground = isShown; } displayMessage.CachedBgImgComponent.enabled = isShown; } } /// /// Scroll message across the display. Assumes module is initialised, message is not null, /// and scrollDirection is not None. /// /// private void ScrollMessage(DisplayMessage displayMessage) { if (displayMessage.showMessage) { // Determine the distance to scroll float scrollFactor = displayMessage.scrollSpeed * Time.deltaTime * canvas.scaleFactor * 0.1f; float deltaX = scrollFactor * cvsResolutionFull.x; float deltaY = scrollFactor * cvsResolutionFull.y; // scrollWidth/Height should be the same as starting positions set in InitialiseMessage(..) // The left/right and top/bottom scroll limits can be ignored OR can consider the width/height of the message. // Fullscreen scrolling = +/- half width of message + half the width of canvas float scrollWidth = displayMessage.isScrollFullscreen ? displayMessage.displayWidth * cvsResolutionHalf.x + cvsResolutionHalf.x : displayMessage.displayWidth * cvsResolutionFull.x; // Fullscreen scrolling = +/- half height of message + half the height of canvas float scrollHeight = displayMessage.isScrollFullscreen ? displayMessage.displayHeight * cvsResolutionHalf.y + cvsResolutionHalf.y : displayMessage.displayHeight * cvsResolutionFull.y; // Calc the localPosition of the message tempMessageOffset.x = displayMessage.offsetX * cvsResolutionHalf.x; tempMessageOffset.y = displayMessage.offsetY * cvsResolutionHalf.y; if (displayMessage.scrollDirectionInt == DisplayMessage.ScrollDirectionLR) { if (displayMessage.isScrollFullscreen) { tempMessageOffset.x = 0f; } displayMessage.scrollOffsetX += deltaX; displayMessage.scrollOffsetY = 0f; if (displayMessage.scrollOffsetX > scrollWidth) { displayMessage.scrollOffsetX = -scrollWidth; } } else if (displayMessage.scrollDirectionInt == DisplayMessage.ScrollDirectionRL) { if (displayMessage.isScrollFullscreen) { tempMessageOffset.x = 0f; } displayMessage.scrollOffsetX -= deltaX; displayMessage.scrollOffsetY = 0f; if (displayMessage.scrollOffsetX < -scrollWidth) { displayMessage.scrollOffsetX = scrollWidth; } } else if (displayMessage.scrollDirectionInt == DisplayMessage.ScrollDirectionBT) { if (displayMessage.isScrollFullscreen) { tempMessageOffset.y = 0f; } displayMessage.scrollOffsetX = 0f; displayMessage.scrollOffsetY += deltaY; if (displayMessage.scrollOffsetY > scrollHeight) { displayMessage.scrollOffsetY = -scrollHeight; } } else // Top to Bottom { if (displayMessage.isScrollFullscreen) { tempMessageOffset.y = 0f; } displayMessage.scrollOffsetX = 0f; displayMessage.scrollOffsetY -= deltaY; if (displayMessage.scrollOffsetY < -scrollHeight) { displayMessage.scrollOffsetY = scrollHeight; } } Transform dmTrfm = displayMessage.CachedMessagePanel.transform; tempMessageOffset.x += displayMessage.scrollOffsetX; tempMessageOffset.y += displayMessage.scrollOffsetY; tempMessageOffset.z = dmTrfm.localPosition.z; dmTrfm.localPosition = tempMessageOffset; } } #endregion #region Private or Internal Methods - Targets /// /// [INTERNAL USE ONLY] /// Create a new Target RectTransform under the HUD in the hierarchy for the give slot /// /// /// /// public RectTransform CreateTargetPanel(DisplayTarget displayTarget, int slotIndex) { RectTransform targetPanel = null; if (targetsRectTfrm == null) { targetsRectTfrm = GetTargetsPanel(); } if (displayTarget != null && targetsRectTfrm != null) { if (slotIndex >= 0 && slotIndex < DisplayTarget.MAX_DISPLAYTARGET_SLOTS) { // Convert into 0.0 to 1.0 value. The width/height values assume default 1920x1080. float panelWidth = displayTarget.width / refResolution.x; float panelHeight = displayTarget.height / refResolution.y; //if (IsInitialised && slotIndex == 0) { numDisplayTargets++; } targetPanel = SSCUtils.GetOrCreateChildRectTransform(targetsRectTfrm, cvsResolutionFull, "Target_" + displayTarget.guidHash + "_" + slotIndex.ToString(), 0f, 0f, panelWidth, panelHeight, 0.5f, 0.5f, 0.5f, 0.5f); if (targetPanel != null) { Image imgComponent = targetPanel.gameObject.AddComponent(); if (imgComponent != null) { imgComponent.raycastTarget = false; } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.CreateTargetPanel() - could not add image to Target panel"); } #endif if ((IsInitialised || editorMode) && !displayTarget.isInitialised) { InitialiseTarget(displayTarget); } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.CreateTargetPanel() - could not create Target panel"); } #endif } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.CreateTargetPanel() - could not create Target panel. Slot number is outside the range 0 to " + DisplayTarget.MAX_DISPLAYTARGET_SLOTS.ToString()); } #endif } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: ShipDisplayModule.CreateTargetPanel() - displayTarget is null or could not find or create " + targetsPanelName); } #endif return targetPanel; } /// /// [INTERNAL USE ONLY] /// Find the Target RectTransform under the HUD in the hierarchy. /// If the parent targetsRectTfrm is null, it will attempt to find or create it. /// /// /// /// private RectTransform GetTargetPanel(int guidHash, int slotIndex) { if (targetsRectTfrm == null) { targetsRectTfrm = GetTargetsPanel(); } if (targetsRectTfrm != null) { return SSCUtils.GetChildRectTransform(targetsRectTfrm.transform, "Target_" + guidHash + "_" + slotIndex, this.name); } else { return null; } } /// /// [INTERNAL ONLY] /// Used by the editor when moving DisplayTargets around in the list. /// /// public void RefreshDisplayTargetSlots() { numDisplayTargets = displayTargetList == null ? 0 : displayTargetList.Count; for (int dtIdx = 0; dtIdx < numDisplayTargets; dtIdx++) { DisplayTarget displayTarget = displayTargetList[dtIdx]; // Initialise the Display Target slots displayTarget.displayTargetSlotList = new List(displayTarget.maxNumberOfTargets); for (int slotIdx = 0; slotIdx < displayTarget.maxNumberOfTargets; slotIdx++) { DisplayTargetSlot displayTargetSlot = InitialiseTargetSlot(displayTarget, slotIdx); displayTarget.displayTargetSlotList.Add(displayTargetSlot); } // InitialiseTargetSlot will turn all the reticles off, so update the DisplayTarget property too displayTarget.showTarget = false; } } /// /// Initialise the display target /// /// /// private void InitialiseTarget(DisplayTarget displayTarget) { if (displayTarget.maxNumberOfTargets < 0) { displayTarget.maxNumberOfTargets = 1; } else if (displayTarget.maxNumberOfTargets > DisplayTarget.MAX_DISPLAYTARGET_SLOTS) { displayTarget.maxNumberOfTargets = DisplayTarget.MAX_DISPLAYTARGET_SLOTS; } // Initialise the Display Target slots displayTarget.displayTargetSlotList = new List(displayTarget.maxNumberOfTargets); for (int slotIdx = 0; slotIdx < displayTarget.maxNumberOfTargets; slotIdx++) { DisplayTargetSlot displayTargetSlot = InitialiseTargetSlot(displayTarget, slotIdx); displayTarget.displayTargetSlotList.Add(displayTargetSlot); } SetDisplayTargetSize(displayTarget, displayTarget.width, displayTarget.height); // SetDisplayTargetReticleColour does not update the baseReticleColour if the reticleColour has not changed. So do it in here instead displayTarget.baseReticleColour.Set(displayTarget.reticleColour.r, displayTarget.reticleColour.g, displayTarget.reticleColour.b, displayTarget.reticleColour.a, true); SetDisplayTargetBrightness(displayTarget); displayTarget.isInitialised = true; } /// /// Initialise a DisplayTarget Slot. This includes: /// 1. caching the panel and image component /// 2. Loading the reticle sprite /// 3. Setting the offset to 0,0 /// /// /// /// private DisplayTargetSlot InitialiseTargetSlot(DisplayTarget displayTarget, int slotIdx) { DisplayTargetSlot displayTargetSlot = new DisplayTargetSlot(); displayTargetSlot.CachedTargetPanel = GetTargetPanel(displayTarget.guidHash, slotIdx); if (displayTargetSlot.CachedTargetPanel != null) { displayTargetSlot.CachedImgComponent = displayTargetSlot.CachedTargetPanel.GetComponent(); // Add the Reticle sprite to the image for this Display Target if (displayTargetSlot.CachedImgComponent != null) { // TODO - consider getting the sprite only once per DisplayTarget displayTargetSlot.CachedImgComponent.sprite = GetDisplayReticleSprite(displayTarget.guidHashDisplayReticle); } SetDisplayTargetOffset(displayTargetSlot, 0f, 0f); // By default targets are initialised off. This is to prevent them suddenly appearing when first created. if (Application.isPlaying) { displayTargetSlot.showTargetSlot = false; displayTargetSlot.CachedTargetPanel.gameObject.SetActive(false); } } return displayTargetSlot; } /// /// Initialise all the DisplayTargets by caching the RectTransforms. /// private void InitialiseTargets() { int _numDisplayTargets = GetNumberDisplayTargets; if (_numDisplayTargets > 0) { if (targetsRectTfrm == null) { targetsRectTfrm = GetTargetsPanel(); } if (targetsRectTfrm != null) { CheckHUDResize(false); for (int dmIdx = 0; dmIdx < _numDisplayTargets; dmIdx++) { InitialiseTarget(displayTargetList[dmIdx]); } if (_numDisplayTargets > 0) { RefreshTargetsSortOrder(); } } #if UNITY_EDITOR else { Debug.LogWarning("ShipDisplayModule.InitialiseTargets() - could not find " + targetsPanelName); } #endif } } /// /// Show or Hide DisplayTarget slot on the HUD. /// By design, if the HUD is not shown, the Target slot will not be show. /// /// /// private void ShowOrHideTargetSlot(DisplayTargetSlot displayTargetSlot, bool isShown) { if ((IsInitialised || editorMode) && displayTargetSlot != null && displayTargetSlot.CachedTargetPanel != null) { displayTargetSlot.showTargetSlot = isShown; displayTargetSlot.CachedTargetPanel.gameObject.SetActive(isShown); } } /// /// Show or Hide all slots of a Display Target on the HUD. /// By design, if the HUD is not shown, the Targets will not be show. /// /// /// private void ShowOrHideTarget(DisplayTarget displayTarget, bool isShown) { if (IsInitialised || editorMode) { displayTarget.showTarget = isShown; for (int sIdx = 0; sIdx < displayTarget.maxNumberOfTargets; sIdx++) { ShowOrHideTargetSlot(displayTarget.displayTargetSlotList[sIdx], isShown); } } } /// /// Show or hide all targets /// /// private void ShowOrHideTargets(bool isShown) { if (IsInitialised) { for (int dmIdx = 0; dmIdx < numDisplayTargets; dmIdx++) { ShowOrHideTarget(displayTargetList[dmIdx], isShown); } } } /// /// Show or Hide all targets based on their current settings /// private void ShowOrHideTargets() { if (IsInitialised) { for (int dmIdx = 0; dmIdx < numDisplayTargets; dmIdx++) { DisplayTarget displayTarget = displayTargetList[dmIdx]; ShowOrHideTarget(displayTarget, displayTarget.showTarget); } } } /// /// Update the position on the HUD for each of the Display Target slots /// that are shown. /// private void UpdateTargetPositions() { if (IsInitialised) { if (sscRadar == null) { sscRadar = SSCRadar.GetOrCreateRadar(); } if (sscRadar != null) { // Loop through the display targets for (int dtgtIdx = 0; dtgtIdx < numDisplayTargets; dtgtIdx++) { DisplayTarget displayTarget = displayTargetList[dtgtIdx]; if (displayTarget.showTarget) { // Loop through the display target slots for (int slotIdx = 0; slotIdx < displayTarget.maxNumberOfTargets; slotIdx++) { DisplayTargetSlot displayTargetSlot = displayTarget.displayTargetSlotList[slotIdx]; if (displayTargetSlot.radarItemKey.radarItemIndex >= 0 && displayTargetSlot.showTargetSlot) { SSCRadarItem radarItem = sscRadar.GetRadarItem(displayTargetSlot.radarItemKey); if (radarItem != null) { SetDisplayTargetPosition(displayTarget, slotIdx, radarItem.position); } //#if UNITY_EDITOR //else //{ // // This could be a timing issue // Debug.LogWarning("ShipDisplayModule.UpdateTargetPositions - could not find radar item with index " + displayTargetSlot.radarItemKey.radarItemIndex + " for DisplayTarget " + (dtgtIdx+1).ToString("000") + " slot " + slotIdx); //} //#endif } } } } } } } #endregion #region Public API Methods - General /// /// Initialise the ShipDisplayModule. Either set initialiseOnStart to false and call this /// method in your code, or set initialiseOnStart to true in the inspector and don't call /// this method. /// public void Initialise() { // calling this method twice may incorrectly set the positions of the Altitude and speed panels. if (IsInitialised) { return; } canvas = GetComponent(); canvasScaler = GetComponent(); hudRectTfrm = SSCUtils.GetChildRectTransform(transform, hudPanelName, this.name); if (canvas != null && hudRectTfrm != null) { canvas.sortingOrder = canvasSortOrder; // Get the reference resolution from the canvas scaler. If it hasn't been changed this should // always be 1920x1080 for SSC. //if (canvasScaler != null) { refResolutionFull = canvasScaler.referenceResolution; } //else { refResolutionFull.x = 1920f; refResolutionFull.y = 1080f; } // use the scaled size rather than the original reference resolution size. CheckHUDResize(false); GetHUDPanels(); if (reticlePanel != null && overlayPanel != null && altitudeTextRectTfrm != null && airspeedTextRectTfrm != null) { GetImgComponents(); GetTextComponents(); // Store the starting position of the Altitude text altitudeInitPosition = altitudeTextRectTfrm.localPosition; altitudeCurrentPosition = altitudeInitPosition; // Initialise the string used to store the Altitude value and reduce GC. altitudeString = new SCSMString(6); altitudeString.SetMaxIntLength(6); // Store the starting position of the Air speed text airspeedInitPosition = airspeedTextRectTfrm.localPosition; airspeedCurrentPosition = airspeedInitPosition; // Initialise the string used to store the Airspeed value and reduce GC. airspeedString = new SCSMString(6); airspeedString.SetMaxIntLength(6); // Create an empty array of integers to use when processing tempIncludeFactionList = new List(20); tempIncludeSquadronList = new List(20); InitialiseAttitude(true); InitialiseHeading(true); ReinitialiseVariables(); hudRandom = new SSCRandom(); // Set the seed to an arbitary prime number (but it could be anything really) if (hudRandom != null) { hudRandom.SetSeed(691); } flickerHistory = new Queue(5); #if UNITY_EDITOR if (!isMainCameraAssigned) { Debug.LogWarning("ShipDisplayModule could not find a camera with the MainCamera tag set"); } #endif IsInitialised = displayReticleImg != null && isMainCameraAssigned; RefreshHUD(); if (isShowHUDWithFlicker && isShowOnInitialise) { FlickerOn(flickerDefaultDuration); } else { ShowOrHideHUD(isShowOnInitialise); } } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: ShipDisplayModule.Initialise() could not find canvas HUDPanel. Did you start with the sample HUD prefab from Prefabs/Visuals folder?"); } #endif } /// /// Check to see if the ship is the same as the sourceShip currently assigned to the HUD. /// If either are null, the method will return false. /// /// /// public bool IsSourceShip(ShipControlModule shipControlModule) { if (sourceShip == null || shipControlModule == null) { return false; } else { return shipControlModule.GetShipId == sourceShip.GetShipId; } } /// /// Check to see if the ship is the same as the soureShip currently assigned to the HUD. /// If either are null, the method will return false. /// /// /// public bool IsSourceShip(Ship ship) { if (sourceShip == null || ship == null) { return false; } else { return ship.shipId == sourceShip.GetShipId; } } /// /// Call this if you modify any of the following at runtime. /// 1) displayReticleList /// 2) The DisplayReticlePanel size /// 3) Add or remove the MainCamera tag from a camera /// 4) displayMessageList /// 5) displayTargetList /// public void ReinitialiseVariables() { ValidateReticleList(); numDisplayReticles = displayReticleList == null ? 0 : displayReticleList.Count; if (reticlePanel != null) { RectTransform reticleRectTransform = reticlePanel.GetComponent(); if (reticleRectTransform != null) { reticleWidth = reticleRectTransform.sizeDelta.x; reticleHeight = reticleRectTransform.sizeDelta.y; } } // If the camera hasn't been assigned, attempt to use the first camera with the MainCamera tag. if (mainCamera == null && Camera.main != null) { mainCamera = Camera.main; } isMainCameraAssigned = mainCamera != null; ValidateGaugeList(); numDisplayGauges = displayGaugeList == null ? 0 : displayGaugeList.Count; ValidateMessageList(); numDisplayMessages = displayMessageList == null ? 0 : displayMessageList.Count; ValidateTargetList(); numDisplayTargets = displayTargetList == null ? 0 : displayTargetList.Count; } /// /// Refresh all components of the HUD. Typically called after the screen has been resized. /// public void RefreshHUD() { if (IsInitialised || editorMode) { // Set the initial position SetDisplayReticleOffset(displayReticleOffsetX, displayReticleOffsetY); SetHUDSize(displayWidth, displayHeight); SetHUDOffset(displayOffsetX, displayOffsetY); // Show or Hide initial setup ShowOrHideReticle(showActiveDisplayReticle); ShowOrHideAltitude(showAltitude); SetPrimaryColour(primaryColour); SetDisplayReticleColour(activeDisplayReticleColour); SetAltitudeTextColour(altitudeTextColour); ShowOrHideAirSpeed(showAirspeed); SetAirSpeedTextColour(airspeedTextColour); ShowOrHideOverlay(showOverlay); InitialiseAttitude(false); ShowOrHideAttitude(showAttitude); InitialiseHeading(false); ShowOrHideHeading(showHeading); InitialiseMessages(); ShowOrHideMessages(); InitialiseTargets(); ShowOrHideTargets(); InitialiseGauges(); ShowOrHideGauges(); // Set brightness after the colours have been set. InitialiseBrightness(); } } /// /// Show the heads-up display. Has no effect if IsInitialised is false. /// public void ShowHUD() { // Prob makes sense to start in a non-flickering state. StopFlickering(); if (isShowHUDWithFlicker) { FlickerOn(flickerDefaultDuration); } else { ShowOrHideHUD(true); } } /// /// Hide the heads-up display. Has no effect if IsInitialised is false. /// public void HideHUD() { // Prob makes sense to start in a non-flickering state. StopFlickering(); if (isHideHUDWithFlicker) { FlickerOff(flickerDefaultDuration); } else { ShowOrHideHUD(false); } } /// /// Show the Overlay image on the heads-up display. Has no effect if IsInitialised is false. /// public void ShowOverlay() { ShowOrHideOverlay(true); } /// /// Hide the Overlay image on the heads-up display. Has no effect if IsInitialised is false. /// public void HideOverlay() { ShowOrHideOverlay(false); } /// /// Set or assign the main camera used by the heads-up display for calculations. /// /// public void SetCamera(Camera camera) { mainCamera = camera; isMainCameraAssigned = camera != null; } /// /// Set the HUD to use a particular display. Displays or monitors are numbered from 1 to 8. /// /// 1 to 8 public void SetCanvasTargetDisplay (int displayNumber) { if (IsInitialised && SSCUtils.VerifyTargetDisplay(displayNumber, true)) { Canvas _canvas = GetCanvas; if (_canvas != null) { _canvas.targetDisplay = displayNumber - 1; } } } /// /// Set the offset (position) of the HUD. /// If the module has been initialised, this will also re-position the HUD. /// /// Horizontal offset from centre. Range between -1 and 1 /// Vertical offset from centre. Range between -1 and 1 public void SetHUDOffset(float offsetX, float offsetY) { // Clamp the x,y values -1 to 1. if (offsetX < -1f) { displayOffsetX = -1f; } else if (offsetX > 1f) { displayOffsetX = 1f; } else { if (offsetX != displayOffsetX) { displayOffsetX = offsetX; } } if (offsetY < -1f) { displayOffsetY = -1f; } else if (offsetY > 1f) { displayOffsetY = 1f; } else { if (offsetY != displayOffsetY) { displayOffsetY = offsetY; } } if (IsInitialised) { // Here we use the half original (reference) HUD size as it doesn't need to be scaled in any way. // The parent HUD canvas is correctly scaled and sized to the actual monitor or device display. overlayPanel.localPosition = new Vector3(offsetX * cvsResolutionFull.x, offsetY * cvsResolutionFull.y, overlayPanel.localPosition.z); } } /// /// Set the size of the HUD overlay image and text. /// If the module has been initialised, this will also resize the HUD. /// The values are only updated if they are outside the range 0.0 to 1.0 or have changed. /// /// /// public void SetHUDSize(float width, float height) { // Clamp the x,y values 0.0 to 1.0 if (width < 0f) { displayWidth = 0f; } else if (width > 1f) { displayWidth = 1f; } else if (width != displayWidth) { displayWidth = width; } if (height < 0f) { displayHeight = 0f; } else if (height > 1f) { displayHeight = 1f; } else if (height != displayHeight) { displayHeight = height; } if (IsInitialised) { // Here we use the original (reference) HUD size as it doesn't need to be scaled in any way. // The parent HUD canvas is correctly scaled and sized to the actual monitor or device display. overlayPanel.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, displayWidth * cvsResolutionFull.x); overlayPanel.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, displayHeight * cvsResolutionFull.y); // altitudeInitPosition is in 1920x1080 sizing. Convert into canvas space with cvsResolutionFull.x / refResolution.x // Move the altitude text relative to the resized overlay image altitudeCurrentPosition.x = altitudeInitPosition.x * displayWidth * cvsResolutionFull.x / refResolution.x; altitudeCurrentPosition.y = altitudeInitPosition.y * displayHeight * cvsResolutionFull.y / refResolution.y; altitudeTextRectTfrm.localPosition = altitudeCurrentPosition; // Move the airspeed text relative to the resized overlay image airspeedCurrentPosition.x = airspeedInitPosition.x * displayWidth * cvsResolutionFull.x / refResolution.x; airspeedCurrentPosition.y = airspeedInitPosition.y * displayHeight * cvsResolutionFull.y / refResolution.y; airspeedTextRectTfrm.localPosition = airspeedCurrentPosition; if (callbackOnSizeChange != null) { callbackOnSizeChange(displayWidth, displayHeight); } } } /// /// Set the primary colour of the heads-up display. Only update the colour if it has actually changed. /// If the module has been initialised, this will also re-colour the HUD with the appropriate brightness. /// /// public void SetPrimaryColour(Color32 newColour) { SSCUtils.UpdateColour(ref newColour, ref primaryColour, ref baseOverlayColour, true); if (IsInitialised || editorMode) { if (primaryOverlayImg != null) { SetOverlayBrightness(); } } } /// /// Set the primary colour of the heads-up display. Only update the colour if it has actually changed. /// If the module has been initialised, this will also re-colour the HUD with the appropriate brightness. /// /// public void SetPrimaryColour(Color newColour) { SSCUtils.UpdateColour(ref newColour, ref primaryColour, ref baseOverlayColour, true); if (IsInitialised || editorMode) { if (primaryOverlayImg != null) { if (brightness == 1f) { primaryOverlayImg.color = newColour; } else { primaryOverlayImg.color = baseOverlayColour.GetColorWithBrightness(brightness); } } } } /// /// Set the overall brightness of the HUD. /// /// Range 0.0 to 1.0 public void SetBrightness(float newBrightness) { // Clamp 0.0 to 1.0 if (newBrightness < 0f) { brightness = 0f; } else if (newBrightness > 1f) { brightness = 1f; } brightness = newBrightness; if (IsInitialised || editorMode) { SetOverlayBrightness(); SetReticleBrightness(); SetAltitudeTextBrightness(); SetAirSpeedTextBrightness(); SetAttitudeBrightness(); SetHeadingBrightness(); int _numDisplayMessages = GetNumberDisplayMessages; // Set the brightness of all the messages for (int dmIdx = 0; dmIdx < _numDisplayMessages; dmIdx++) { SetDisplayMessageBackgroundBrightness(displayMessageList[dmIdx]); SetDisplayMessageTextBrightness(displayMessageList[dmIdx]); } int _numDisplayTargets = GetNumberDisplayTargets; // Set the brightness for all the targets for (int dtgtIdx = 0; dtgtIdx < _numDisplayTargets; dtgtIdx++) { SetDisplayTargetBrightness(displayTargetList[dtgtIdx]); } int _numDisplayGauges = GetNumberDisplayGauges; // Set the brightness for all the gauges for (int dgIdx = 0; dgIdx < _numDisplayGauges; dgIdx++) { SetDisplayGaugeForegroundBrightness(displayGaugeList[dgIdx]); SetDisplayGaugeBackgroundBrightness(displayGaugeList[dgIdx]); SetDisplayGaugeTextBrightness(displayGaugeList[dgIdx]); } } if (callbackOnBrightnessChange != null) { callbackOnBrightnessChange(brightness); } } /// /// Set the sort order in the scene of the HUD. Higher values appear on top. /// /// public void SetCanvasSortOrder(int newSortOrder) { canvasSortOrder = newSortOrder; if (IsInitialised || editorMode) { Canvas _canvas = GetCanvas; if (_canvas != null) { _canvas.sortingOrder = newSortOrder; } } } /// /// Turn on or off the HUD. Has no effect if not initialised. See also ShowHUD() and HideHUD() /// public void ToggleHUD() { // Prob makes sense to start in a non-flickering state. StopFlickering(); IsHUDShown = !IsHUDShown; if (isShowHUDWithFlicker && IsHUDShown) { FlickerOn(flickerDefaultDuration); } else if (isHideHUDWithFlicker && !IsHUDShown) { FlickerOff(flickerDefaultDuration); } else { ShowOrHideHUD(IsHUDShown); } } #endregion #region Public API Methods - Panels /// /// Get the Attitude Panel /// Create it if it does not already exist /// /// public RectTransform GetAttitudePanel() { if (hudRectTfrm == null) { hudRectTfrm = GetHUDPanel(); } if (hudRectTfrm != null) { //if (cvsResolutionFull.x == 0f) { cvsResolutionFull = ReferenceResolution; } // Centred panel (like messages) return SSCUtils.GetOrCreateChildRectTransform(hudRectTfrm, cvsResolutionFull, attitudePanelName, 0f, 0f, 1f, 1f, 0.5f, 0.5f, 0.5f, 0.5f); } else { return null; } } /// /// Get the HUD RectTransform or panel /// /// public RectTransform GetHUDPanel() { return SSCUtils.GetChildRectTransform(transform, hudPanelName, this.name); } /// /// Get the Heading Panel /// Create it if it does not already exist /// /// public RectTransform GetHeadingPanel() { if (hudRectTfrm == null) { hudRectTfrm = GetHUDPanel(); } if (hudRectTfrm != null) { // Stretched panel //return SSCUtils.GetOrCreateChildRectTransform(hudRectTfrm, cvsResolutionFull, headingPanelName, 0f, 0f, 1f, 1f, 0f, 0f, 1f, 1f); // Centred panel (like messages) return SSCUtils.GetOrCreateChildRectTransform(hudRectTfrm, cvsResolutionFull, headingPanelName, 0f, 0f, 1f, 1f, 0.5f, 0.5f, 0.5f, 0.5f); } else { return null; } } /// /// Get the parent panel for the Display Targets. /// Create it if it does not already exist /// /// public RectTransform GetTargetsPanel() { if (hudRectTfrm == null) { hudRectTfrm = GetHUDPanel(); } if (hudRectTfrm != null) { // Stretched panel return SSCUtils.GetOrCreateChildRectTransform(hudRectTfrm, cvsResolutionFull, targetsPanelName, 0f, 0f, 1f, 1f, 0f, 0f, 1f, 1f); } else { return null; } } /// /// Get the parent panel for the Display Gauges. /// Create it if it does not already exist /// /// public RectTransform GetGaugesPanel() { if (hudRectTfrm == null) { hudRectTfrm = GetHUDPanel(); } if (hudRectTfrm != null) { // Stretched panel return SSCUtils.GetOrCreateChildRectTransform(hudRectTfrm, cvsResolutionFull, gaugesPanelName, 0f, 0f, 1f, 1f, 0f, 0f, 1f, 1f); } else { return null; } } #endregion #region Public API Methods - Cursor /// /// Show the hardware (mouse) cursor. /// This also restarts the countdown auto-hide /// timer if that is enabled. /// public void ShowCursor() { ShowOrHideCursor(true); } /// /// Hide the hardware (mouse) cursor. /// NOTE: This will sometimes fail to turn off the cursor in the editor /// Game View when it doesn't have focus, but will work fine in a build. /// public void HideCursor() { ShowOrHideCursor(false); } /// /// Centre the hardware (mouse) cursor in the centre of the screen. /// WARNING: This will wait until the next frame before it returns. /// public void CentreCursor() { Cursor.lockState = CursorLockMode.Locked; StartCoroutine(UnlockCursor()); } /// /// Toggle the hardware (mouse) cursor on or off. /// NOTE: This will sometimes fail to turn off the cursor in the editor /// Game View when it doesn't have focus, but will work fine in a build. /// public void ToggleCursor() { ShowOrHideCursor(!Cursor.visible); } #endregion #region Public API Methods - Display Reticle /// /// Returns the guidHash of the Reticle in the list given the index or /// zero-based position in the list. Will return 0 if no matching Reticle /// is found. /// Will return 0 if the module hasn't been initialised. /// /// /// public int GetDisplayReticleGuidHash(int index) { int guidHash = 0; if (IsInitialised || editorMode) { if (index >= 0 && index < GetNumberDisplayReticles) { if (displayReticleList[index] != null) { guidHash = displayReticleList[index].guidHash; } } } return guidHash; } /// /// Returns the guidHash of the Reticle in the list given the name of the sprite. /// Will return 0 if no matching Reticle is found. /// WARNING: This will increase GC. Use GetDisplayReticleGuidHash(int index) where possible. /// /// /// public int GetDisplayReticleGuidHash(string spriteName) { int guidHash = 0; DisplayReticle _tempDR = null; if ((IsInitialised || editorMode) && !string.IsNullOrEmpty(spriteName)) { int _numDisplayReticles = GetNumberDisplayReticles; string _spriteNameLowerCase = spriteName.ToLower(); for (int drIdx = 0; drIdx < _numDisplayReticles; drIdx++) { _tempDR = displayReticleList[drIdx]; if (_tempDR != null && _tempDR.primarySprite != null && _tempDR.primarySprite.name.ToLower() == _spriteNameLowerCase) { guidHash = _tempDR.guidHash; break; } } } return guidHash; } /// /// Get a DisplayReticle given its guidHash. /// See also GetDisplayReticleGuidHash(..). /// Will return null if guidHash parameter is 0, it cannot be found /// or the module has not been initialised. /// /// /// public DisplayReticle GetDisplayReticle(int guidHash) { DisplayReticle displayReticle = null; DisplayReticle _tempDR = null; if (IsInitialised || editorMode) { int _numDisplayReticles = GetNumberDisplayReticles; for (int drIdx = 0; drIdx < _numDisplayReticles; drIdx++) { _tempDR = displayReticleList[drIdx]; if (_tempDR != null && _tempDR.guidHash == guidHash) { displayReticle = _tempDR; break; } } } return displayReticle; } /// /// Get the UI sprite (image) for a DisplayReticle. /// See also GetDisplayReticleGuidHash(..). /// /// /// public Sprite GetDisplayReticleSprite(int guidHash) { Sprite sprite = null; DisplayReticle _tempDR = null; if (IsInitialised || editorMode) { int _numDisplayReticles = GetNumberDisplayReticles; for (int drIdx = 0; drIdx < _numDisplayReticles; drIdx++) { _tempDR = displayReticleList[drIdx]; if (_tempDR != null && _tempDR.guidHash == guidHash) { sprite = _tempDR.primarySprite; break; } } } return sprite; } /// /// Change the DiplayReticle sprite on the UI panel. /// See also GetDisplayReticleGuidHash(..). /// NOTE: The module must be initialised. /// /// public void ChangeDisplayReticle(int guidHash) { if (IsInitialised || editorMode) { guidHashActiveDisplayReticle = guidHash; currentDisplayReticle = GetDisplayReticle(guidHash); LoadDisplayReticleSprite(currentDisplayReticle); } } /// /// Hide or turn off the Display Reticle /// public void HideDisplayReticle() { ShowOrHideReticle(false); } /// /// Set the offset (position) of the Display Reticle on the HUD. /// If the module has been initialised, this will also re-position the Display Reticle. /// Same as SetDisplayReticleOffset(offset.x, offset.y) /// /// Horizontal and Vertical offset from centre. Values should be between -1 and 1 public void SetDisplayReticleOffset(Vector2 offset) { SetDisplayReticleOffset(offset.x, offset.y); } /// /// Set the offset (position) of the Display Reticle on the HUD. /// If the module has been initialised, this will also re-position the Display Reticle. /// /// Horizontal offset from centre. Range between -1 and 1 /// Vertical offset from centre. Range between -1 and 1 public void SetDisplayReticleOffset(float offsetX, float offsetY) { // Verify the x,y values are within range -1 to 1. if (offsetX >= -1f && offsetX <= 1f) { displayReticleOffsetX = offsetX; } if (offsetY >= -1f && offsetY <= 1f) { displayReticleOffsetY = offsetY; } if (IsInitialised || editorMode) { Vector2 halfHUDSize = GetHUDHalfSize(true); if (lockDisplayReticleToCursor) { // Centre on mouse pointer more accurately (reticle can go half outside screen dimensions on edges) displayReticleImg.transform.localPosition = new Vector3(offsetX * halfHUDSize.x, offsetY * halfHUDSize.y, displayReticleImg.transform.localPosition.z); } else { // Original default behaviour - reticle remains within screen dimensions. displayReticleImg.transform.localPosition = new Vector3(offsetX * (halfHUDSize.x - (reticleWidth / 2f)), offsetY * (halfHUDSize.y - (reticleHeight / 2f)), displayReticleImg.transform.localPosition.z); } } } /// /// Set the active Display Reticle colour. Only update the colour if it has actually changed. /// If the module has been initialised, this will also re-colour the reticle with the appropriate brightness. /// /// public void SetDisplayReticleColour(Color32 newColour) { SSCUtils.UpdateColour(ref newColour, ref activeDisplayReticleColour, ref baseReticleColour, true); if (IsInitialised) { if (displayReticleImg != null) { SetReticleBrightness(); } } } /// /// Set the active Display Reticle colour. Only update the colour if it has actually changed. /// If the module has been initialised, this will also re-colour the reticle with the appropriate brightness. /// /// public void SetDisplayReticleColour(Color newColour) { SSCUtils.UpdateColour(ref newColour, ref activeDisplayReticleColour, ref baseReticleColour, true); if (IsInitialised || editorMode) { if (displayReticleImg != null) { if (brightness == 1f) { displayReticleImg.color = newColour; } else { displayReticleImg.color = baseReticleColour.GetColorWithBrightness(brightness); } } } } /// /// Show the Display Reticle on the HUD. The HUD will automatically be shown if it is not already visible. /// public void ShowDisplayReticle() { ShowOrHideReticle(true); } /// /// Show or Hide the Display Reticle on the HUD. The HUD will automatically be shown if required. /// public void ToggleDisplayReticle() { ShowOrHideReticle(!showActiveDisplayReticle); } /// /// Create a new list if required /// public void ValidateReticleList() { if (displayReticleList == null) { displayReticleList = new List(1); } } #endregion #region Public API Methods - Altitude /// /// Attempt to show the Altitude indicator. Turn on HUD if required. /// public void ShowAltitude() { ShowOrHideAltitude(true); } /// /// Attempt to turn off the Altitude indicator /// public void HideAltitude() { ShowOrHideAltitude(false); } /// /// Set the altitude text colour on the heads-up display. Only update the colour if it has actually changed. /// If the module has been initialised, this will also re-colour the HUD with the appropriate brightness. /// /// public void SetAltitudeTextColour(Color32 newColour) { SSCUtils.UpdateColour(ref newColour, ref altitudeTextColour, ref baseAltitudeTextColour, true); if (IsInitialised || editorMode) { if (altitudeText != null) { SetAltitudeTextBrightness(); } } } /// /// Set the altitude text colour on the heads-up display. Only update the colour if it has actually changed. /// If the module has been initialised, this will also re-colour the HUD with the appropriate brightness. /// /// public void SetAltitudeTextColour(Color newColour) { SSCUtils.UpdateColour(ref newColour, ref altitudeTextColour, ref baseAltitudeTextColour, true); if (IsInitialised || editorMode) { if (altitudeText != null) { if (brightness == 1f) { altitudeText.color = newColour; } else { altitudeText.color = baseAltitudeTextColour.GetColorWithBrightness(brightness); } } } } #endregion #region Public API Methods - Air Speed /// /// Attempt to show the Air speed indicator. Turn on HUD if required. /// public void ShowAirSpeed() { ShowOrHideAirSpeed(true); } /// /// Attempt to turn off the AirSpeed indicator /// public void HideAirSpeed() { ShowOrHideAirSpeed(false); } /// /// Set the airspeed text colour on the heads-up display. Only update the colour if it has actually changed. /// If the module has been initialised, this will also re-colour the HUD with the appropriate brightness. /// /// public void SetAirSpeedTextColour(Color32 newColour) { SSCUtils.UpdateColour(ref newColour, ref airspeedTextColour, ref baseAirspeedTextColour, true); if (IsInitialised || editorMode) { if (airspeedText != null) { SetAirSpeedTextBrightness(); } } } /// /// Set the airspeed text colour on the heads-up display. Only update the colour if it has actually changed. /// If the module has been initialised, this will also re-colour the HUD with the appropriate brightness. /// /// public void SetAirSpeedTextColour(Color newColour) { SSCUtils.UpdateColour(ref newColour, ref airspeedTextColour, ref baseAirspeedTextColour, true); if (IsInitialised || editorMode) { if (airspeedText != null) { if (brightness == 1f) { airspeedText.color = newColour; } else { airspeedText.color = baseAirspeedTextColour.GetColorWithBrightness(brightness); } } } } #endregion #region Public API Methods - Attitude /// /// Attempt to turn off the Attitude display /// public void HideAttitude() { ShowOrHideAttitude(false); } /// /// Set the sprite that will mask the Display Attitude scrollable sprite /// /// public void SetDisplayAttitudeMaskSprite (Sprite newSprite) { attitudeMaskSprite = newSprite; if (attitudeMaskImg != null) { attitudeMaskImg.sprite = attitudeMaskSprite; } } /// /// This is called when the HUD is resized or some elements of the Attitude control are modified /// public void RefreshAttitudeAfterResize() { // Recalculate the pixels per degree in the pitch ladder if (attitudeScrollImg != null && attitudeScrollImg.mainTexture != null) { // The attitude scroll img should be -90 to 90 deg. Ideally it should have a "border" // at the top and bottom of blank (transparent) space to prevent +-90 being right // at the top/bottom of the HUD. // The image is autoscaled to the full height of the default HUD screen size. float imgHeight = attitudeScrollImg.mainTexture.height; // If the image if (imgHeight < 10f) { imgHeight = 1024f; } attitudePixelsPerDegree = cvsResolutionFull.y * ((imgHeight - attitudeScrollSpriteBorderWidth * 2f) / imgHeight) / 180f; } #if UNITY_EDITOR else { Debug.LogWarning("RefreshAttitudeAfterResize - Attitude scroll sprite has no UI texture"); } #endif } /// /// Set the Display Attitude primary colour. Only update the colour if it has actually changed. /// If the module has been initialised, this will also re-colour the attitude with the appropriate brightness. /// /// public void SetDisplayAttitudePrimaryColour (Color newColour) { SSCUtils.UpdateColour(ref newColour, ref attitudePrimaryColour, ref baseAttitudePrimaryColour, true); SetAttitudeBrightness(); } /// /// Set the Display Attitude scroll sprite. /// /// public void SetDisplayAttitudeScrollSprite (Sprite newSprite) { attitudeScrollSprite = newSprite; if (attitudeScrollImg != null) { attitudeScrollImg.sprite = attitudeScrollSprite; // Recalculate the pixels per degree for scrolling. RefreshAttitudeAfterResize(); } } /// /// Set the normalised size of the scrollable Display Attitude. /// Currently width is always 1.0 /// /// /// public void SetDisplayAttitudeSize (float width, float height) { // Verify the width,height values are within range 0.1 to 1. if (height >= 0.1f && height <= 1f) { attitudeHeight = height; } attitudeWidth = 1f; if (IsInitialised || editorMode) { #if UNITY_EDITOR if (editorMode) { CheckHUDResize(false); UnityEditor.Undo.RecordObject(attitudeRectTfrm.transform, string.Empty); } #endif // Here we use the original (reference) HUD size as it doesn't need to be scaled in any way. // The parent HUD canvas is correctly scaled and sized to the actual monitor or device display. attitudeRectTfrm.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, attitudeWidth * cvsResolutionFull.x); attitudeRectTfrm.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, attitudeHeight * cvsResolutionFull.y); } } /// /// Set the normalised offset (position) of the Display Attitude on the HUD. /// If the module has been initialised, this will also re-position the Display Attitude. /// /// Horizontal offset from centre. Range between -1 and 1 /// Vertical offset from centre. Range between -1 and 1 public void SetDisplayAttitudeOffset (float offsetX, float offsetY) { // Verify the x,y values are within range -1 to 1. if (offsetX >= -1f && offsetX <= 1f) { attitudeOffsetX = offsetX; } if (offsetY >= -1f && offsetY <= 1f) { attitudeOffsetY = offsetY; } if (IsInitialised || editorMode) { Transform attTrfm = attitudeRectTfrm.transform; #if UNITY_EDITOR if (editorMode) { CheckHUDResize(false); UnityEditor.Undo.RecordObject(attTrfm, string.Empty); } #endif // Use the scaled HUD size. This works in and out of play mode tempAttitudeOffset.x = attitudeOffsetX * cvsResolutionHalf.x; tempAttitudeOffset.y = attitudeOffsetY * cvsResolutionHalf.y; tempAttitudeOffset.z = attTrfm.localPosition.z; attTrfm.localPosition = tempAttitudeOffset; // Reset the position of the scrollable image if (attitudeScrollPanel != null) { attitudeScrollPanel.localPosition = Vector3.zero; attitudeInitPosition = attitudeScrollPanel.transform.position; //if (showAttitudeIndicator && attitudeIndicatorPanel != null) //{ // attitudeIndicatorPanel.localPosition = new Vector3(attitudeScrollPanel.localPosition.x, attitudeScrollPanel.localPosition.y - 20f, attitudeScrollPanel.localPosition.z); //} } } } /// /// Set the border width of the Scroll Sprite (texture) in pixels /// /// public void SetDisplayAttitudeSpriteBorderWidth (float newBorderWidth) { if (newBorderWidth >= 0f) { attitudeScrollSpriteBorderWidth = newBorderWidth; RefreshAttitudeAfterResize(); } } /// /// Attempt to show the scrollable Attitude. Turn on HUD if required. /// public void ShowAttitude() { ShowOrHideAttitude(true); } #endregion #region Public API Methods - Flickering /// /// Flicker the HUD on/off. Override the Flicker Default Duration. /// Show the HUD when flickering finishes. /// /// public void FlickerOn (float duration) { if (duration > 0f) { flickerDuration = duration; flickerDurationTimer = 0f; isFlickerWaiting = false; isFlickeringEndStateOn = true; // If the HUD is flickering to the Shown state, it cannot also be flickering off. isFlickeringEndStateOff = false; if (flickerHistory != null) { flickerHistory.Clear(); } // Remember the current overall HUD brightness setting flickerStartIntensity = brightness; if (!flickerVariableIntensity) { flickerIntensity = flickerStartIntensity; } } } /// /// Flicker the HUD on/off. Override the Flicker Default Duration. /// Hide the HUD when flickering finishers. /// /// public void FlickerOff (float duration) { if (duration > 0f) { flickerDuration = duration; flickerDurationTimer = 0f; isFlickerWaiting = false; isFlickeringEndStateOff = true; // If the HUD is flickering to the Hidden state, it cannot also be flickering on. isFlickeringEndStateOn = false; if (flickerHistory != null) { flickerHistory.Clear(); } // Remember the current overall HUD brightness setting flickerStartIntensity = brightness; if (!flickerVariableIntensity) { flickerIntensity = flickerStartIntensity; } } } public void StopFlickering() { isFlickeringEndStateOn = false; isFlickeringEndStateOff = false; } #endregion #region Public API Methods - Gauges /// /// Add a new gauge to the HUD. By design, they are not visible at runtime when first added. /// /// /// /// public DisplayGauge AddGauge(string gaugeName, string gaugeText) { DisplayGauge displayGauge = null; if (GetHUDPanel() == null) { #if UNITY_EDITOR Debug.LogWarning("ERROR: ShipDisplayModule.AddGauge() - could not find HUDPanel for the HUD. Did you use the prefab from Prefabs/Visuals folder?"); #endif } else { displayGauge = new DisplayGauge(); if (displayGauge != null) { displayGauge.gaugeName = gaugeName; displayGauge.gaugeString = gaugeText; ValidateGaugeList(); displayGaugeList.Add(displayGauge); numDisplayGauges = displayGaugeList.Count; RectTransform gaugePanel = CreateGaugePanel(displayGauge); if (gaugePanel != null) { #if UNITY_EDITOR // Make sure we can rollback the creation of the new gauge panel for this slot UnityEditor.Undo.RegisterCreatedObjectUndo(gaugePanel.gameObject, string.Empty); #endif } } } return displayGauge; } /// /// Add a gauge to the HUD using a displayGauge instance. Typically, this is used /// with CopyDisplayGauge(..). /// /// public void AddGauge(DisplayGauge displayGauge) { if (displayGauge != null) { if (GetHUDPanel() == null) { #if UNITY_EDITOR Debug.LogWarning("ERROR: ShipDisplayModule.AddGauge() - could not find HUDPanel for the HUD. Did you use the prefab from Prefabs/Visuals folder?"); #endif } else { ValidateGaugeList(); displayGaugeList.Add(displayGauge); numDisplayGauges = displayGaugeList.Count; RectTransform gaugePanel = CreateGaugePanel(displayGauge); if (gaugePanel != null) { #if UNITY_EDITOR // Make sure we can rollback the creation of the new gauge panel for this slot UnityEditor.Undo.RegisterCreatedObjectUndo(gaugePanel.gameObject, string.Empty); #endif } } } } /// /// Delete a gauge from the HUD. /// NOTE: It is much cheaper to HideDisplayGauge(..) than completely remove it. /// /// public void DeleteGauge(int guidHash) { #if UNITY_EDITOR bool isRecordUndo = !Application.isPlaying; int undoGroup=0; #else bool isRecordUndo = false; #endif RectTransform gaugeRectTfrm = GetGaugePanel(guidHash); int _dgIndex = GetDisplayGaugeIndex(guidHash); // Only record undo if in the editor AND is not playing AND something needs to be recorded isRecordUndo = isRecordUndo && (gaugeRectTfrm != null || _dgIndex >= 0); #if UNITY_EDITOR if (isRecordUndo) { UnityEditor.Undo.SetCurrentGroupName("Delete Display Gauge " + (_dgIndex + 1).ToString()); undoGroup = UnityEditor.Undo.GetCurrentGroup(); if (_dgIndex >= 0) { UnityEditor.Undo.RecordObject(this, string.Empty); } } #endif // Check that the gauge exists and it is within the list constraints // NOTE: The script element must be modified BEFORE the Undo.DestroyObjectImmediate is called // in order for Undo to correctly undo both the gauge change AND the destroy of the panel. if (_dgIndex >= 0 && _dgIndex < (displayGaugeList == null ? 0 : displayGaugeList.Count)) { displayGaugeList.RemoveAt(_dgIndex); numDisplayGauges = displayGaugeList.Count; } if (gaugeRectTfrm != null) { if (Application.isPlaying) { Destroy(gaugeRectTfrm.gameObject); } else { #if UNITY_EDITOR UnityEditor.Undo.DestroyObjectImmediate(gaugeRectTfrm.gameObject); #else DestroyImmediate(gaugeRectTfrm.gameObject); #endif } } #if UNITY_EDITOR if (isRecordUndo) { UnityEditor.Undo.CollapseUndoOperations(undoGroup); } #endif } /// /// Create a copy of an existing DisplayGauge, and give it a new name. /// Call AddGauge(newDisplayGauge) to make it useable in the game. /// /// /// /// public DisplayGauge CopyDisplayGauge(DisplayGauge displayGauge, string NameOfCopy) { DisplayGauge dgCopy = new DisplayGauge(displayGauge); if (dgCopy != null) { // make it unique dgCopy.guidHash = SSCMath.GetHashCodeFromGuid(); dgCopy.gaugeName = NameOfCopy; } return dgCopy; } /// /// Returns the guidHash of the Gauge in the list given the index or zero-based position in the list. /// Will return 0 if no matching Gauge is found. /// /// /// public int GetDisplayGaugeGuidHash(int index) { int guidHash = 0; if (index >= 0 && index < GetNumberDisplayGauges) { if (displayGaugeList[index] != null) { guidHash = displayGaugeList[index].guidHash; } } return guidHash; } /// /// Get the zero-based index of the Gauge in the list. Will return -1 if not found. /// /// /// public int GetDisplayGaugeIndex(int guidHash) { int index = -1; int _numDisplayGauges = GetNumberDisplayGauges; for (int dgIdx = 0; dgIdx < _numDisplayGauges; dgIdx++) { if (displayGaugeList[dgIdx] != null && displayGaugeList[dgIdx].guidHash == guidHash) { index = dgIdx; break; } } return index; } /// /// Get a DisplayGauge given its guidHash. /// See also GetDisplayGaugeGuidHash(..). /// Will return null if guidHash parameter is 0, it cannot be found. /// /// /// public DisplayGauge GetDisplayGauge(int guidHash) { DisplayGauge displayGauge = null; DisplayGauge _tempDG = null; int _numDisplayGauges = GetNumberDisplayGauges; for (int dgIdx = 0; dgIdx < _numDisplayGauges; dgIdx++) { _tempDG = displayGaugeList[dgIdx]; if (_tempDG != null && _tempDG.guidHash == guidHash) { displayGauge = _tempDG; break; } } return displayGauge; } /// /// Get the display gauge give the description title of the gauge. /// WARNING: This will increase Garbage Collection (GC). Where possible /// use GetDisplayGauge(guidHash) and/or GetDisplayGaugeGuidHash(index) /// /// /// public DisplayGauge GetDisplayGauge(string displayGaugeName) { DisplayGauge displayGauge = null; DisplayGauge _tempDG = null; if (!string.IsNullOrEmpty(displayGaugeName)) { int _numDisplayGauges = GetNumberDisplayGauges; for (int dgIdx = 0; dgIdx < _numDisplayGauges; dgIdx++) { _tempDG = displayGaugeList[dgIdx]; if (_tempDG != null && !string.IsNullOrEmpty(_tempDG.gaugeName)) { if (_tempDG.gaugeName.ToLower() == displayGaugeName.ToLower()) { displayGauge = _tempDG; break; } } } } return displayGauge; } /// /// Hide or turn off the Display Gauge /// /// public void HideDisplayGauge(int guidHash) { DisplayGauge displayGauge = GetDisplayGauge(guidHash); if (displayGauge != null) { ShowOrHideGauge(displayGauge, false); } #if UNITY_EDITOR else { Debug.LogWarning("WARNING: ShowDisplayGauge - Could not find gauge with guidHash: " + guidHash); } #endif } /// /// Hide or turn off the Display Gauge /// /// public void HideDisplayGauge(DisplayGauge displayGauge) { if (displayGauge != null) { ShowOrHideGauge(displayGauge, false); } #if UNITY_EDITOR else { Debug.LogWarning("WARNING: ShowDisplayGauge - Could not find gauge - parameter was null"); } #endif } /// /// Hide or turn off all Display Gauges. ShipDisplayModule must be initialised. /// public void HideDisplayGauges() { ShowOrHideGauges(false); } /// /// After adding or moving DisplayGauges, they may need to be sorted to /// have the correct z-order in on the HUD. /// public void RefreshGaugesSortOrder() { if (gaugesRectTfrm != null) { // Gauges should begin at index 0. int zIndex = -1; // Update number of DisplayGauges. This can help issues in the Editor, like moving DislayGauges // up or down in the list while in play mode. numDisplayGauges = displayGaugeList == null ? 0 : displayGaugeList.Count; for (int dtIdx = 0; dtIdx < numDisplayGauges; dtIdx++) { DisplayGauge displayGauge = displayGaugeList[dtIdx]; if (displayGauge != null) { RectTransform _rt = displayGauge.CachedGaugePanel; if (_rt != null) { _rt.SetSiblingIndex(++zIndex); } } } } } /// /// Import a json file from disk and return as DisplayGauge /// /// /// /// public DisplayGauge ImportGaugeFromJson (string folderPath, string fileName) { DisplayGauge displayGauge = null; if (!string.IsNullOrEmpty(folderPath) && !string.IsNullOrEmpty(fileName)) { try { string filePath = System.IO.Path.Combine(folderPath, fileName); if (System.IO.File.Exists(filePath)) { string jsonText = System.IO.File.ReadAllText(filePath); displayGauge = new DisplayGauge(); int displayGaugeGuidHash = displayGauge.guidHash; JsonUtility.FromJsonOverwrite(jsonText, displayGauge); if (displayGauge != null) { // make hash code unique displayGauge.guidHash = displayGaugeGuidHash; } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: Import Gauge. Could not find file at " + filePath); } #endif } catch (System.Exception ex) { #if UNITY_EDITOR Debug.LogWarning("ERROR: ShipDisplayModule - could not import gauge from: " + folderPath + " PLEASE REPORT - " + ex.Message); #else // Keep compiler happy if (ex != null) { } #endif } } return displayGauge; } /// /// Save the Gauge to a json file on disk. /// /// /// public bool SaveGaugeAsJson (DisplayGauge displayGauge, string filePath) { bool isSuccessful = false; if (displayGauge != null && !string.IsNullOrEmpty(filePath)) { try { string jsonGaugeData = JsonUtility.ToJson(displayGauge); if (!string.IsNullOrEmpty(jsonGaugeData) && !string.IsNullOrEmpty(filePath)) { System.IO.File.WriteAllText(filePath, jsonGaugeData); isSuccessful = true; } } catch (System.Exception ex) { #if UNITY_EDITOR Debug.LogWarning("ERROR: ShipDisplayModule SaveGaugeAsJson - could not export: " + displayGauge.gaugeName + " PLEASE REPORT - " + ex.Message); #else // Keep compiler happy if (ex != null) { } #endif } } return isSuccessful; } /// /// The foreground colour of the gauge will be determined by the gauge value and the low, medium and high colours. /// LowColour = value of 0, MediumColour when value is 0.5, and HighColour when value is 1.0 /// /// /// /// /// public void SetDisplayGaugeValueAffectsColourOn(DisplayGauge displayGauge, Color lowColour, Color mediumColour, Color highColour) { if (displayGauge != null) { SSCUtils.ColortoColorNoAlloc(ref lowColour, ref displayGauge.foregroundLowColour); SSCUtils.ColortoColorNoAlloc(ref mediumColour, ref displayGauge.foregroundMediumColour); SSCUtils.ColortoColorNoAlloc(ref highColour, ref displayGauge.foregroundHighColour); displayGauge.isColourAffectByValue = true; if (IsInitialised || editorMode) { SetDisplayGaugeValue(displayGauge, displayGauge.gaugeValue); } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayGaugeValueAffectsColour - displayGauge is null"); } #endif } /// /// The value of the gauge does not affect the foreground colour. /// When turning off this feature the new foreground colour would typically be the old foregroundHighColour. /// /// /// public void SetDisplayGaugeValueAffectsColourOff(DisplayGauge displayGauge, Color newForegroundColour) { if (displayGauge != null) { displayGauge.isColourAffectByValue = false; if (IsInitialised || editorMode) { SetDisplayGaugeForegroundColour(displayGauge, newForegroundColour); } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayGaugeValueAffectsColour - displayGauge is null"); } #endif } /// /// Set the gauge value at which the foreground medium colour should be set. /// The default value is 0.5. /// /// /// public void SetDisplayGaugeMediumColourValue (DisplayGauge displayGauge, float newValue) { if (displayGauge != null) { if (newValue > 0f && newValue < 1f) { displayGauge.mediumColourValue = newValue; } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayGaugeMediumColourValue - displayGauge is null"); } #endif } /// /// Set the offset (position) of the Display Gauge on the HUD. /// If the module has been initialised, this will also re-position the Display Gauge. /// /// Horizontal offset from centre. Range between -1 and 1 /// Vertical offset from centre. Range between -1 and 1 public void SetDisplayGaugeOffset(DisplayGauge displayGauge, float offsetX, float offsetY) { // Verify the x,y values are within range -1 to 1. if (offsetX >= -1f && offsetX <= 1f) { displayGauge.offsetX = offsetX; } if (offsetY >= -1f && offsetY <= 1f) { displayGauge.offsetY = offsetY; } if (IsInitialised || editorMode) { Transform dgTrfm = displayGauge.CachedGaugePanel.transform; #if UNITY_EDITOR if (editorMode) { CheckHUDResize(false); UnityEditor.Undo.RecordObject(dgTrfm, string.Empty); } #endif // Use the scaled HUD size. This works in and out of play mode tempGaugeOffset.x = displayGauge.offsetX * cvsResolutionHalf.x; tempGaugeOffset.y = displayGauge.offsetY * cvsResolutionHalf.y; tempGaugeOffset.z = dgTrfm.localPosition.z; dgTrfm.localPosition = tempGaugeOffset; } } /// /// Set the size of the Gauge Panel. /// If the module has been initialised, this will also resize the Gauge Panel. /// The values are only updated if they are outside the range 0.0 to 1.0 or have changed. /// Also updates the Text direction /// /// /// Range between 0.0 and 1.0 /// Range between 0.0 and 1.0 public void SetDisplayGaugeSize(DisplayGauge displayGauge, float width, float height) { // Clamp the x,y values 0.0 to 1.0 if (width < 0f) { displayGauge.displayWidth = 0f; } else if (width > 1f) { displayGauge.displayWidth = 1f; } else if (width != displayGauge.displayWidth) { displayGauge.displayWidth = width; } if (height < 0f) { displayGauge.displayHeight = 0f; } else if (height > 1f) { displayGauge.displayHeight = 1f; } else if (height != displayGauge.displayHeight) { displayGauge.displayHeight = height; } if (IsInitialised || editorMode) { #if UNITY_EDITOR if (editorMode) { CheckHUDResize(false); } #endif RectTransform dgRectTfrm = displayGauge.CachedGaugePanel; float pixelWidth = displayGauge.displayWidth * cvsResolutionFull.x; float pixelHeight = displayGauge.displayHeight * cvsResolutionFull.y; if (dgRectTfrm != null) { // Here we use the original (reference) HUD size as it doesn't need to be scaled in any way. // The parent HUD canvas is correctly scaled and sized to the actual monitor or device display. dgRectTfrm.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, pixelWidth); dgRectTfrm.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, pixelHeight); } if (displayGauge.CachedFgImgComponent != null) { RectTransform dgFgImgRectTfrm = displayGauge.CachedFgImgComponent.rectTransform; dgFgImgRectTfrm.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, pixelWidth); dgFgImgRectTfrm.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, pixelHeight); } SetDisplayGaugeTextDirection(displayGauge, displayGauge.textDirection); } } /// /// Set the type or style of the gauge. /// /// /// public void SetDisplayGaugeType (DisplayGauge displayGauge, DisplayGauge.DGType dgType) { if (displayGauge != null) { displayGauge.gaugeType = dgType; GetOrCreateOrRemoveGaugeLabel(displayGauge); if (displayGauge.HasLabel) { // Refresh the label SetDisplayGaugeTextBrightness(displayGauge); SetDisplayGaugeTextFontStyle(displayGauge, (UnityEngine.FontStyle)displayGauge.fontStyle); SetDisplayGaugeTextFontSize(displayGauge, displayGauge.isBestFit, displayGauge.fontMinSize, displayGauge.fontMaxSize); SetDisplayGaugeLabelAlignment(displayGauge, displayGauge.labelAlignment); } // Refresh the Text direction which also does the sizing for gauges with and without labels SetDisplayGaugeTextDirection(displayGauge, displayGauge.textDirection); SetDisplayGaugeValue(displayGauge, displayGauge.gaugeValue); } } /// /// Update the value or reading of the gauge. If Value Affects Colour (isColourAffectByValue) /// is enabled, the foreground colour of the gauge will also be updated. /// Values should be in range 0.0 to 1.0. /// NOTE: Numeric gauges can increase GC at runtime. Where possible, only call this method when /// the value changes. /// /// /// public void SetDisplayGaugeValue (DisplayGauge displayGauge, float gaugeValue) { if (displayGauge != null) { // Clamp 0.0 to 1.0 float _gaugeValue = gaugeValue < 0f ? 0f : gaugeValue > 1f ? 1f : gaugeValue; displayGauge.gaugeValue = _gaugeValue; UnityEngine.UI.Image fgImg = displayGauge.CachedFgImgComponent; if (fgImg != null) { fgImg.fillAmount = displayGauge.isFillMethodNone ? 0f : _gaugeValue; if (displayGauge.isColourAffectByValue) { if (_gaugeValue > displayGauge.mediumColourValue) { Color _newColour = Color.Lerp(displayGauge.foregroundMediumColour, displayGauge.foregroundHighColour, SSCMath.Normalise(_gaugeValue, displayGauge.mediumColourValue, 1f)); SSCUtils.UpdateColour(ref _newColour, ref displayGauge.foregroundColour, ref displayGauge.baseForegroundColour, false); SetDisplayGaugeForegroundBrightness(displayGauge); } else { Color _newColour = Color.Lerp(displayGauge.foregroundLowColour, displayGauge.foregroundMediumColour, SSCMath.Normalise(_gaugeValue, 0f, displayGauge.mediumColourValue)); SSCUtils.UpdateColour(ref _newColour, ref displayGauge.foregroundColour, ref displayGauge.baseForegroundColour, true); SetDisplayGaugeForegroundBrightness(displayGauge); } } } int gaugeTypeInt = (int)displayGauge.gaugeType; // If this is a numeric gauge, update the text with the numeric value if (gaugeTypeInt == DisplayGauge.DGTypeFilledNumber1Int || gaugeTypeInt == DisplayGauge.DGTypeNumberWithLabel1Int) { float _displayTxtValue = displayGauge.isNumericPercentage ? _gaugeValue * 100f : _gaugeValue * displayGauge.gaugeMaxValue; displayGauge.gaugeString = SSCUtils.GetNumericString(_displayTxtValue, displayGauge.gaugeDecimalPlaces, displayGauge.isNumericPercentage); // Update the text field for the gauge on the HUD UI Text uiText = displayGauge.CachedTextComponent; if (uiText != null) { uiText.text = displayGauge.gaugeString; } } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayGaugeValue - displayGauge is null"); } #endif } /// /// Update the text of the gauge label. This only applies to numeric gauges with labels. /// For non-numeric gauges see SetDisplayGaugeText(..). /// /// /// public void SetDisplayGaugeLabel (DisplayGauge displayGauge, string gaugeLabel) { if (displayGauge != null) { displayGauge.gaugeLabel = gaugeLabel; Text uiText = displayGauge.CachedLabelTextComponent; if (uiText != null) { uiText.text = gaugeLabel; } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayGaugeLabel - displayGauge is null"); } #endif } /// /// Update the position of the label within the gauge panel. /// Only applies to numeric gauges with a label. /// See also SetDisplayGaugeTextAlignment(..) /// /// /// public void SetDisplayGaugeLabelAlignment (DisplayGauge displayGauge, TextAnchor textAlignment) { if (displayGauge != null) { if (!editorMode) { displayGauge.labelAlignment = textAlignment; } // If this is not a numeric gauge with a label, this will return null. Text uiText = displayGauge.CachedLabelTextComponent; if (uiText != null) { uiText.alignment = textAlignment; } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayGaugeLabelAlignment - displayGauge is null"); } #endif } /// /// Update the text of the gauge. For numeric gauges with labels, see /// SetDisplayGaugeLabel(..). /// /// /// public void SetDisplayGaugeText (DisplayGauge displayGauge, string gaugeText) { if (displayGauge != null) { displayGauge.gaugeString = gaugeText; Text uiText = displayGauge.CachedTextComponent; if (uiText != null) { uiText.text = gaugeText; } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayGaugeText - displayGauge is null"); } #endif } /// /// Update the position of the text within the gauge panel /// /// /// public void SetDisplayGaugeTextAlignment(DisplayGauge displayGauge, TextAnchor textAlignment) { if (displayGauge != null) { if (!editorMode) { displayGauge.textAlignment = textAlignment; } Text uiText = displayGauge.CachedTextComponent; if (uiText != null) { uiText.alignment = textAlignment; } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayGaugeTextAlignment - displayGauge is null"); } #endif } /// /// Set the font of the DisplayGauge Text component /// /// /// public void SetDisplayGaugeTextFont(DisplayGauge displayGauge, Font font) { if (displayGauge != null) { Text uiText = displayGauge.CachedTextComponent; if (uiText != null) { uiText.font = font; } // If this is numeric gauge with a label, also set the label if (displayGauge.HasLabel && displayGauge.CachedLabelTextComponent != null) { displayGauge.CachedLabelTextComponent.font = font; } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayGaugeTextFont - displayGauge is null"); } #endif } /// /// Set the font style of the DisplayGauge Text component /// /// /// public void SetDisplayGaugeTextFontStyle(DisplayGauge displayGauge, FontStyle fontStyle) { if (displayGauge != null) { displayGauge.fontStyle = (DisplayGauge.DGFontStyle)fontStyle; Text uiText = displayGauge.CachedTextComponent; if (uiText != null) { uiText.fontStyle = fontStyle; } // If this is numeric gauge with a label, also set the label if (displayGauge.HasLabel && displayGauge.CachedLabelTextComponent != null) { displayGauge.CachedLabelTextComponent.fontStyle = fontStyle; } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayGaugeTextFontStyle - displayGauge is null"); } #endif } /// /// Set the font size of the display gauge text. If isBestFit is false, maxSize is the font size set. /// /// /// /// /// public void SetDisplayGaugeTextFontSize(DisplayGauge displayGauge, bool isBestFit, int minSize, int maxSize) { if (displayGauge != null) { if (!editorMode) { displayGauge.isBestFit = isBestFit; displayGauge.fontMinSize = minSize; displayGauge.fontMaxSize = maxSize; } Text uiText = displayGauge.CachedTextComponent; if (uiText != null) { uiText.resizeTextForBestFit = isBestFit; uiText.resizeTextMinSize = minSize; uiText.resizeTextMaxSize = maxSize; uiText.fontSize = maxSize; } // If this is numeric gauge with a label, also set the label if (displayGauge.HasLabel && displayGauge.CachedLabelTextComponent != null) { Text uiLabelText = displayGauge.CachedLabelTextComponent; uiLabelText.resizeTextForBestFit = isBestFit; uiLabelText.resizeTextMinSize = minSize; uiLabelText.resizeTextMaxSize = maxSize; uiLabelText.fontSize = maxSize; } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayGaugeTextFontSize - displayGauge is null"); } #endif } /// /// Set the Display Gauge text colour. Only update the colour if it has actually changed. /// If the module has been initialised, this will also re-colour the gauge text with the appropriate brightness. /// /// public void SetDisplayGaugeTextColour(DisplayGauge displayGauge, Color newColour) { if (displayGauge != null) { SSCUtils.UpdateColour(ref newColour, ref displayGauge.textColour, ref displayGauge.baseTextColour, true); SetDisplayGaugeTextBrightness(displayGauge); } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayGaugeTextColour - displayGauge is null"); } #endif } /// /// Update the direction of the text within the gauge panel /// /// /// public void SetDisplayGaugeTextDirection(DisplayGauge displayGauge, DisplayGauge.DGTextDirection textDirection) { if (displayGauge != null) { if (!editorMode) { displayGauge.textDirection = textDirection; } Text uiText = displayGauge.CachedTextComponent; Text uiLabelText = displayGauge.CachedLabelTextComponent; bool hasLabel = uiLabelText != null && displayGauge.HasLabel; if (uiText != null) { #if UNITY_EDITOR if (editorMode) { CheckHUDResize(false); } #endif float pixelWidth = displayGauge.displayWidth * cvsResolutionFull.x; float pixelHeight = displayGauge.displayHeight * cvsResolutionFull.y; float textRotation = 0f; RectTransform dgTextRectTfrm = displayGauge.CachedTextComponent.rectTransform; RectTransform dgLabelRectTfrm = hasLabel ? displayGauge.CachedLabelTextComponent.rectTransform : null; if (textDirection == DisplayGauge.DGTextDirection.Horizontal) { if (hasLabel) { pixelHeight *= 0.5f; dgLabelRectTfrm.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, pixelWidth); dgLabelRectTfrm.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, pixelHeight); } dgTextRectTfrm.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, pixelWidth); dgTextRectTfrm.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, pixelHeight); } else { if (textDirection == DisplayGauge.DGTextDirection.BottomTop) { textRotation = 90f; } else { textRotation = 270f; } if (hasLabel) { pixelWidth *= 0.5f; dgLabelRectTfrm.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, pixelHeight); dgLabelRectTfrm.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, pixelWidth); } dgTextRectTfrm.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, pixelHeight); dgTextRectTfrm.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, pixelWidth); } uiText.transform.localRotation = Quaternion.Euler(uiText.transform.localRotation.x, uiText.transform.localRotation.y, textRotation); if (hasLabel) { uiLabelText.transform.localRotation = Quaternion.Euler(uiLabelText.transform.localRotation.x, uiLabelText.transform.localRotation.y, textRotation); if (textDirection == DisplayGauge.DGTextDirection.Horizontal) { uiLabelText.transform.localPosition = new Vector3(0f, pixelHeight / 2f, 0f); uiText.transform.localPosition = new Vector3(0f, -pixelHeight / 2f, 0f); } else if (textDirection == DisplayGauge.DGTextDirection.BottomTop) { uiLabelText.transform.localPosition = new Vector3(-pixelWidth / 2f, 0f, 0f); uiText.transform.localPosition = new Vector3(pixelWidth / 2f, 0f, 0f); } else { uiLabelText.transform.localPosition = new Vector3(pixelWidth / 2f, 0f, 0f); uiText.transform.localPosition = new Vector3(-pixelWidth / 2f, 0f, 0f); } } else { // This will correct the position if the dev has (incorrectly) moved the panel manually in the scene uiText.transform.localPosition = Vector3.zero; } } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayGaugeTextRotation - displayGauge is null"); } #endif } /// /// Set the Display Gauge foreground colour. Only update the colour if it has actually changed. /// If the module has been initialised, this will also re-colour the gauge foreground with the appropriate brightness. /// /// public void SetDisplayGaugeForegroundColour(DisplayGauge displayGauge, Color newColour) { if (displayGauge != null) { SSCUtils.UpdateColour(ref newColour, ref displayGauge.foregroundColour, ref displayGauge.baseForegroundColour, true); SetDisplayGaugeForegroundBrightness(displayGauge); } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayGaugeForegroundColour - displayGauge is null"); } #endif } /// /// Set the Display Gauge foreground sprite. This is used to render the gauge value by partially filling it. /// /// /// public void SetDisplayGaugeForegroundSprite(DisplayGauge displayGauge, Sprite newSprite) { if (displayGauge != null) { displayGauge.foregroundSprite = newSprite; UnityEngine.UI.Image fgImg = displayGauge.CachedFgImgComponent; if (fgImg != null) { fgImg.sprite = newSprite; } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayGaugeForegroundSprite - displayGauge is null"); } #endif } /// /// Set the Display Gauge background colour. Only update the colour if it has actually changed. /// If the module has been initialised, this will also re-colour the gauge background with the appropriate brightness. /// /// public void SetDisplayGaugeBackgroundColour(DisplayGauge displayGauge, Color newColour) { if (displayGauge != null) { SSCUtils.UpdateColour(ref newColour, ref displayGauge.backgroundColour, ref displayGauge.baseBackgroundColour, true); SetDisplayGaugeBackgroundBrightness(displayGauge); } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayGaugeBackgroundColour - displayGauge is null"); } #endif } /// /// Set the Display Gauge background sprite. This is used to render the background image of the gauge. /// /// /// public void SetDisplayGaugeBackgroundSprite(DisplayGauge displayGauge, Sprite newSprite) { if (displayGauge != null) { displayGauge.backgroundSprite = newSprite; UnityEngine.UI.Image bgImg = displayGauge.CachedBgImgComponent; if (bgImg != null) { bgImg.sprite = newSprite; } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayGaugeBackgroundSprite - displayGauge is null"); } #endif } /// /// Set the Display Gauge fill method. This determines how the gauge is filled /// /// /// public void SetDisplayGaugeFillMethod(DisplayGauge displayGauge, DisplayGauge.DGFillMethod fillMethod) { if (displayGauge != null) { UnityEngine.UI.Image fgImg = displayGauge.CachedFgImgComponent; if (fgImg != null) { displayGauge.isFillMethodNone = displayGauge.fillMethod == DisplayGauge.DGFillMethod.None; fgImg.fillMethod = displayGauge.isFillMethodNone ? Image.FillMethod.Horizontal : (UnityEngine.UI.Image.FillMethod)fillMethod; // Refresh the gauge if case changing from/to Fill Method None. SetDisplayGaugeValue(displayGauge, displayGauge.gaugeValue); } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayGaugeFillMethod - displayGauge is null"); } #endif } /// /// Sets whether or not the foreground and background sprites keep their original texture aspect ratio. /// This can be useful when creating circular gauges. /// /// /// public void SetDisplayGaugeKeepAspectRatio(DisplayGauge displayGauge, bool isKeepAspectRatio) { if (displayGauge != null) { UnityEngine.UI.Image fgImg = displayGauge.CachedFgImgComponent; UnityEngine.UI.Image bgImg = displayGauge.CachedBgImgComponent; if (fgImg != null) { fgImg.preserveAspect = isKeepAspectRatio; } if (bgImg != null) { bgImg.preserveAspect = isKeepAspectRatio; } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayGaugeKeepAspectRatio - displayGauge is null"); } #endif } /// /// Show the Display Gauge on the HUD. The HUD will automatically be shown if it is not already visible. /// /// public void ShowDisplayGauge(int guidHash) { DisplayGauge displayGauge = GetDisplayGauge(guidHash); if (displayGauge != null) { ShowOrHideGauge(displayGauge, true); } #if UNITY_EDITOR else { Debug.LogWarning("WARNING: ShowDisplayGauge - Could not find gauge with guidHash: " + guidHash); } #endif } /// /// Show the Display Gauge on the HUD. The HUD will automatically be shown if it is not already visible. /// /// public void ShowDisplayGauge(DisplayGauge displayGauge) { if (displayGauge != null) { ShowOrHideGauge(displayGauge, true); } #if UNITY_EDITOR else { Debug.LogWarning("WARNING: ShowDisplayGauge - Could not find gauge - parameter was null"); } #endif } /// /// Show or turn on all Display Gauges. ShipDisplayModule must be initialised. /// The HUD will automatically be shown if it is not already visible. /// public void ShowDisplayGauges() { ShowOrHideGauges(true); } /// /// Create a new list if required /// public void ValidateGaugeList() { if (displayGaugeList == null) { displayGaugeList = new List(1); } } #endregion #region Public API Methods - Heading /// /// Set the small sprite that will indicate or point to the heading on the HUD /// /// public void SetDisplayHeadingIndicatorSprite (Sprite newSprite) { headingIndicatorSprite = newSprite; if (headingIndicatorImg != null) { headingIndicatorImg.sprite = headingIndicatorSprite; } } /// /// Set the sprite that will mask the Display Heading scrollable sprite /// /// public void SetDisplayHeadingMaskSprite (Sprite newSprite) { headingMaskSprite = newSprite; if (headingMaskImg != null) { headingMaskImg.sprite = headingMaskSprite; } } /// /// Set the Display Heading small indicator colour. Only update the colour if it has actually changed. /// If the module has been initialised, this will also re-colour the heading with the appropriate brightness. /// /// public void SetDisplayHeadingIndicatorColour (Color newColour) { SSCUtils.UpdateColour(ref newColour, ref headingIndicatorColour, ref baseHeadingIndicatorColour, true); SetHeadingBrightness(); } /// /// Set the Display Heading primary colour. Only update the colour if it has actually changed. /// If the module has been initialised, this will also re-colour the heading with the appropriate brightness. /// /// public void SetDisplayHeadingPrimaryColour (Color newColour) { SSCUtils.UpdateColour(ref newColour, ref headingPrimaryColour, ref baseHeadingPrimaryColour, true); SetHeadingBrightness(); } /// /// Set the Display Heading scroll sprite. /// /// public void SetDisplayHeadingScrollSprite (Sprite newSprite) { headingScrollSprite = newSprite; if (headingScrollImg != null) { headingScrollImg.sprite = headingScrollSprite; } } /// /// Set the normalised size of the scrollable Display Heading. /// Currently height is always 1.0 /// /// /// public void SetDisplayHeadingSize (float width, float height) { // Verify the width,height values are within range 0.1 to 1. if (width >= 0.1f && width <= 1f) { headingWidth = width; } headingHeight = 1f; if (IsInitialised || editorMode) { #if UNITY_EDITOR if (editorMode) { CheckHUDResize(false); UnityEditor.Undo.RecordObject(headingRectTfrm.transform, string.Empty); } #endif // Here we use the original (reference) HUD size as it doesn't need to be scaled in any way. // The parent HUD canvas is correctly scaled and sized to the actual monitor or device display. headingRectTfrm.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, headingWidth * cvsResolutionFull.x); headingRectTfrm.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, headingHeight * cvsResolutionFull.y); } } /// /// Set the normalised offset (position) of the Display Heading on the HUD. /// If the module has been initialised, this will also re-position the Display Heading. /// /// Horizontal offset from centre. Range between -1 and 1 /// Vertical offset from centre. Range between -1 and 1 public void SetDisplayHeadingOffset (float offsetX, float offsetY) { // Verify the x,y values are within range -1 to 1. if (offsetX >= -1f && offsetX <= 1f) { headingOffsetX = offsetX; } if (offsetY >= -1f && offsetY <= 1f) { headingOffsetY = offsetY; } if (IsInitialised || editorMode) { Transform hdgTrfm = headingRectTfrm.transform; #if UNITY_EDITOR if (editorMode) { CheckHUDResize(false); UnityEditor.Undo.RecordObject(hdgTrfm, string.Empty); } #endif // Use the scaled HUD size. This works in and out of play mode tempHeadingOffset.x = headingOffsetX * cvsResolutionHalf.x; tempHeadingOffset.y = headingOffsetY * cvsResolutionHalf.y; tempHeadingOffset.z = hdgTrfm.localPosition.z; hdgTrfm.localPosition = tempHeadingOffset; // Reset the position of the scrollable image if (headingScrollPanel != null) { headingScrollPanel.localPosition = Vector3.zero; headingInitPosition = headingScrollPanel.transform.position; if (showHeadingIndicator && headingIndicatorPanel != null) { headingIndicatorPanel.localPosition = new Vector3(headingScrollPanel.localPosition.x, headingScrollPanel.localPosition.y - 20f, headingScrollPanel.localPosition.z); } } } } /// /// Attempt to turn off the scrollable Heading /// public void HideHeading() { ShowOrHideHeading(false); } /// /// Attempt to turn off the Heading indicator /// public void HideHeadingIndicator() { ShowOrHideHeadingIndicator(false); } /// /// Attempt to show the scrollable Heading. Turn on HUD if required. /// public void ShowHeading() { ShowOrHideHeading(true); } /// /// Attempt to turn on the Heading indicator /// public void ShowHeadingIndicator() { ShowOrHideHeadingIndicator(true); } #endregion #region Public API Methods - Messages /// /// Add a new message to the HUD. By design, they are not visible at runtime when first added. /// /// /// /// public DisplayMessage AddMessage(string messageName, string messageText) { DisplayMessage displayMessage = null; Canvas canvas = GetCanvas; hudRectTfrm = GetHUDPanel(); if (canvas == null) { #if UNITY_EDITOR Debug.LogWarning("ERROR: ShipDisplayModule.AddMessage() - could not find canvas for the HUD. Did you use the prefab from Prefabs/Visuals folder?"); #endif } else if (hudRectTfrm == null) { #if UNITY_EDITOR Debug.LogWarning("ERROR: ShipDisplayModule.AddMessage() - could not find HUDPanel for the HUD. Did you use the prefab from Prefabs/Visuals folder?"); #endif } else { displayMessage = new DisplayMessage(); if (displayMessage != null) { displayMessage.messageName = messageName; displayMessage.messageString = messageText; ValidateMessageList(); displayMessageList.Add(displayMessage); CreateMessagePanel(displayMessage, hudRectTfrm); numDisplayMessages = displayMessageList.Count; } } return displayMessage; } /// /// Add a message to the HUD using a displayMessage instance. Typically, this is used /// with CopyDisplayMessage(..). /// /// public void AddMessage(DisplayMessage displayMessage) { if (displayMessage != null) { RectTransform hudRectTfrm = SSCUtils.GetChildRectTransform(transform, hudPanelName, this.name); if (hudRectTfrm == null) { #if UNITY_EDITOR Debug.LogWarning("ERROR: ShipDisplayModule.AddMessage() - could not find HUDPanel for the HUD. Did you use the prefab from Prefabs/Visuals folder?"); #endif } else { ValidateMessageList(); displayMessageList.Add(displayMessage); CreateMessagePanel(displayMessage, hudRectTfrm); numDisplayMessages = displayMessageList.Count; } } } /// /// Delete a message from the HUD. /// NOTE: It is much cheaper to HideDisplayMessage(..) than completely remove it. /// /// public void DeleteMessage(int guidHash) { #if UNITY_EDITOR bool isRecordUndo = !Application.isPlaying; int undoGroup=0; #else bool isRecordUndo = false; #endif RectTransform msgRectTfrm = GetMessagePanel(guidHash, null); int _msgIndex = GetDisplayMessageIndex(guidHash); // Only record undo if in the editor AND is not playing AND something needs to be recorded isRecordUndo = isRecordUndo && (msgRectTfrm != null || _msgIndex >= 0); #if UNITY_EDITOR if (isRecordUndo) { UnityEditor.Undo.SetCurrentGroupName("Delete Display Message " + (_msgIndex + 1).ToString()); undoGroup = UnityEditor.Undo.GetCurrentGroup(); if (_msgIndex >= 0) { UnityEditor.Undo.RecordObject(this, string.Empty); } } #endif // Check that the message exists and it is within the list constraints // NOTE: The script element must be modified BEFORE the Undo.DestroyObjectImmediate is called // in order for Undo to correctly undo both the message change AND the destroy of the panel. if (_msgIndex >= 0 && _msgIndex < (displayMessageList == null ? 0 : displayMessageList.Count)) { displayMessageList.RemoveAt(_msgIndex); numDisplayMessages = displayMessageList.Count; } if (msgRectTfrm != null) { if (Application.isPlaying) { Destroy(msgRectTfrm.gameObject); } else { #if UNITY_EDITOR UnityEditor.Undo.DestroyObjectImmediate(msgRectTfrm.gameObject); #else DestroyImmediate(msgRectTfrm.gameObject); #endif } } #if UNITY_EDITOR if (isRecordUndo) { UnityEditor.Undo.CollapseUndoOperations(undoGroup); } #endif } /// /// Create a copy of an existing DisplayMessage, and give it a new name. /// Call AddMessage(newDisplayMessage) to make it useable in the game. /// /// /// /// public DisplayMessage CopyDisplayMessage(DisplayMessage displayMessage, string NameOfCopy) { DisplayMessage dmCopy = new DisplayMessage(displayMessage); if (dmCopy != null) { // make it unique dmCopy.guidHash = SSCMath.GetHashCodeFromGuid(); dmCopy.messageName = NameOfCopy; } return dmCopy; } /// /// Returns the guidHash of the Message in the list given the index or /// zero-based position in the list. Will return 0 if no matching Message /// is found. /// /// /// public int GetDisplayMessageGuidHash(int index) { int guidHash = 0; if (index >= 0 && index < GetNumberDisplayMessages) { if (displayMessageList[index] != null) { guidHash = displayMessageList[index].guidHash; } } return guidHash; } /// /// Get the zero-based index of the Message in the list. Will return -1 if not found. /// /// /// public int GetDisplayMessageIndex(int guidHash) { int index = -1; int _numDisplayMessages = GetNumberDisplayMessages; for (int dmIdx = 0; dmIdx < _numDisplayMessages; dmIdx++) { if (displayMessageList[dmIdx] != null && displayMessageList[dmIdx].guidHash == guidHash) { index = dmIdx; break; } } return index; } /// /// Get a DisplayMessage given its guidHash. /// See also GetDisplayMessageGuidHash(..). /// Will return null if guidHash parameter is 0, it cannot be found. /// /// /// public DisplayMessage GetDisplayMessage(int guidHash) { DisplayMessage displayMessage = null; DisplayMessage _tempDM = null; int _numDisplayMessages = GetNumberDisplayMessages; for (int dmIdx = 0; dmIdx < _numDisplayMessages; dmIdx++) { _tempDM = displayMessageList[dmIdx]; if (_tempDM != null && _tempDM.guidHash == guidHash) { displayMessage = _tempDM; break; } } return displayMessage; } /// /// Get the display message give the description title of the message. /// WARNING: This will increase Garbage Collection (GC). Where possible /// use GetDisplayMessage(guidHash) and/or GetDisplayMessageGuidHash(index) /// /// /// public DisplayMessage GetDisplayMessage(string displayMessageName) { DisplayMessage displayMessage = null; DisplayMessage _tempDM = null; if (!string.IsNullOrEmpty(displayMessageName)) { int _numDisplayMessages = GetNumberDisplayMessages; for (int dmIdx = 0; dmIdx < _numDisplayMessages; dmIdx++) { _tempDM = displayMessageList[dmIdx]; if (_tempDM != null && !string.IsNullOrEmpty(_tempDM.messageName)) { if (_tempDM.messageName.ToLower() == displayMessageName.ToLower()) { displayMessage = _tempDM; break; } } } } return displayMessage; } /// /// Is the Display Message currently being shown on the HUD? /// /// /// public bool IsDisplayMessageShown (DisplayMessage displayMessage) { if (displayMessage != null) { return IsHUDShown && displayMessage.showMessage; } else { return false; } } /// /// After adding or moving DisplayMessages, they may need to be sorted to /// have the correct z-order in on the HUD. /// public void RefreshMessagesSortOrder() { if (hudPanel != null) { // Messages should begin after index 1. int zIndex = 1; // Start messages after Altitude in hierarchy. if (overlayPanel != null) { zIndex = overlayPanel.GetSiblingIndex(); } int _numDisplayMessages = GetNumberDisplayMessages; for (int dmIdx = 0; dmIdx < _numDisplayMessages; dmIdx++) { DisplayMessage displayMessage = displayMessageList[dmIdx]; if (displayMessage != null) { RectTransform _rt = displayMessage.CachedMessagePanel; if (_rt != null) { _rt.SetSiblingIndex(++zIndex); } } } } } /// /// Import a json file from disk and return as DisplayMessage /// /// /// /// public DisplayMessage ImportMessageFromJson (string folderPath, string fileName) { DisplayMessage displayMessage = null; if (!string.IsNullOrEmpty(folderPath) && !string.IsNullOrEmpty(fileName)) { try { string filePath = System.IO.Path.Combine(folderPath, fileName); if (System.IO.File.Exists(filePath)) { string jsonText = System.IO.File.ReadAllText(filePath); displayMessage = new DisplayMessage(); int displayGaugeGuidHash = displayMessage.guidHash; JsonUtility.FromJsonOverwrite(jsonText, displayMessage); if (displayMessage != null) { // make hash code unique displayMessage.guidHash = displayGaugeGuidHash; } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: Import Message. Could not find file at " + filePath); } #endif } catch (System.Exception ex) { #if UNITY_EDITOR Debug.LogWarning("ERROR: ShipDisplayModule - could not import message from: " + folderPath + " PLEASE REPORT - " + ex.Message); #else // Keep compiler happy if (ex != null) { } #endif } } return displayMessage; } /// /// Save the Message to a json file on disk. /// /// /// public bool SaveMessageAsJson (DisplayMessage displayMessage, string filePath) { bool isSuccessful = false; if (displayMessage != null && !string.IsNullOrEmpty(filePath)) { try { string jsonMessageData = JsonUtility.ToJson(displayMessage); if (!string.IsNullOrEmpty(jsonMessageData) && !string.IsNullOrEmpty(filePath)) { System.IO.File.WriteAllText(filePath, jsonMessageData); isSuccessful = true; } } catch (System.Exception ex) { #if UNITY_EDITOR Debug.LogWarning("ERROR: ShipDisplayModule SaveMessageAsJson - could not export: " + displayMessage.messageName + " PLEASE REPORT - " + ex.Message); #else // Keep compiler happy if (ex != null) { } #endif } } return isSuccessful; } /// /// Set the offset (position) of the Display Message on the HUD. /// If the module has been initialised, this will also re-position the Display Message. /// /// Horizontal offset from centre. Range between -1 and 1 /// Vertical offset from centre. Range between -1 and 1 public void SetDisplayMessageOffset(DisplayMessage displayMessage, float offsetX, float offsetY) { // Verify the x,y values are within range -1 to 1. if (offsetX >= -1f && offsetX <= 1f) { displayMessage.offsetX = offsetX; } if (offsetY >= -1f && offsetY <= 1f) { displayMessage.offsetY = offsetY; } if (IsInitialised || editorMode) { Transform dmTrfm = displayMessage.CachedMessagePanel.transform; #if UNITY_EDITOR if (editorMode) { CheckHUDResize(false); UnityEditor.Undo.RecordObject(dmTrfm, string.Empty); } #endif // Use original refResolution rather than the scaled size of the HUD. This works in and out of play mode //tempMessageOffset.x = displayMessage.offsetX * (refResolutionHalf.x - (displayMessage.displayWidth / 2f)); //tempMessageOffset.y = displayMessage.offsetY * (refResolutionHalf.y - (displayMessage.displayHeight / 2f)); // Use the scaled HUD size. This works in and out of play mode tempMessageOffset.x = displayMessage.offsetX * cvsResolutionHalf.x; tempMessageOffset.y = displayMessage.offsetY * cvsResolutionHalf.y; tempMessageOffset.z = dmTrfm.localPosition.z; dmTrfm.localPosition = tempMessageOffset; } } /// /// Set the size of the Message Panel. /// If the module has been initialised, this will also resize the Message Panel. /// The values are only updated if they are outside the range 0.0 to 1.0 or have changed. /// /// /// Range between 0.0 and 1.0 /// Range between 0.0 and 1.0 public void SetDisplayMessageSize(DisplayMessage displayMessage, float width, float height) { // Clamp the x,y values 0.0 to 1.0 if (width < 0f) { displayMessage.displayWidth = 0f; } else if (width > 1f) { displayMessage.displayWidth = 1f; } else if (width != displayMessage.displayWidth) { displayMessage.displayWidth = width; } if (height < 0f) { displayMessage.displayHeight = 0f; } else if (height > 1f) { displayMessage.displayHeight = 1f; } else if (height != displayMessage.displayHeight) { displayMessage.displayHeight = height; } if (IsInitialised || editorMode) { #if UNITY_EDITOR if (editorMode) { CheckHUDResize(false); } #endif RectTransform dmRectTfrm = displayMessage.CachedMessagePanel; if (dmRectTfrm != null) { // Here we use the original (reference) HUD size as it doesn't need to be scaled in any way. // The parent HUD canvas is correctly scaled and sized to the actual monitor or device display. dmRectTfrm.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, displayMessage.displayWidth * cvsResolutionFull.x); dmRectTfrm.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, displayMessage.displayHeight * cvsResolutionFull.y); } if (displayMessage.CachedTextComponent != null) { RectTransform dmTextRectTfrm = displayMessage.CachedTextComponent.rectTransform; dmTextRectTfrm.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, displayMessage.displayWidth * cvsResolutionFull.x); dmTextRectTfrm.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, displayMessage.displayHeight * cvsResolutionFull.y); } } } /// /// Update the text of the message /// /// /// public void SetDisplayMessageText(DisplayMessage displayMessage, string messageText) { if (displayMessage != null) { displayMessage.messageString = messageText; Text uiText = displayMessage.CachedTextComponent; if (uiText != null) { uiText.text = messageText; } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayMessageText - displayMessage is null"); } #endif } /// /// Update the position of the text within the message panel /// /// /// public void SetDisplayMessageTextAlignment(DisplayMessage displayMessage, TextAnchor textAlignment) { if (displayMessage != null) { if (!editorMode) { displayMessage.textAlignment = textAlignment; } Text uiText = displayMessage.CachedTextComponent; if (uiText != null) { uiText.alignment = textAlignment; } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayMessageTextAlignment - displayMessage is null"); } #endif } /// /// Set the font of the DisplayMessage Text component /// /// /// public void SetDisplayMessageTextFont(DisplayMessage displayMessage, Font font) { if (displayMessage != null) { Text uiText = displayMessage.CachedTextComponent; if (uiText != null) { uiText.font = font; } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayMessageTextFont - displayMessage is null"); } #endif } /// /// Set the font size of the display message text. If isBestFit is false, maxSize is the font size set. /// /// /// /// /// public void SetDisplayMessageTextFontSize(DisplayMessage displayMessage, bool isBestFit, int minSize, int maxSize) { if (displayMessage != null) { if (!editorMode) { displayMessage.isBestFit = isBestFit; displayMessage.fontMinSize = minSize; displayMessage.fontMaxSize = maxSize; } Text uiText = displayMessage.CachedTextComponent; if (uiText != null) { uiText.resizeTextForBestFit = isBestFit; uiText.resizeTextMinSize = minSize; uiText.resizeTextMaxSize = maxSize; uiText.fontSize = maxSize; } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayMessageTextFontSize - displayMessage is null"); } #endif } /// /// Set the Display Message background colour. Only update the colour if it has actually changed. /// If the module has been initialised, this will also re-colour the message background with the appropriate brightness. /// /// public void SetDisplayMessageBackgroundColour(DisplayMessage displayMessage, Color newColour) { if (displayMessage != null) { SSCUtils.UpdateColour(ref newColour, ref displayMessage.backgroundColour, ref displayMessage.baseBackgroundColour, true); SetDisplayMessageBackgroundBrightness(displayMessage); } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayMessageBackgroundColour - displayMessage is null"); } #endif } /// /// Set the Display Message scroll direction. /// USAGE: SetDisplayMessageScrollDirection(displayMessage, DisplayMessage.ScrollDirectionLR) /// /// /// public void SetDisplayMessageScrollDirection(DisplayMessage displayMessage, int scrollDirection) { if (displayMessage != null) { if (scrollDirection >= 0 && scrollDirection < 5) { displayMessage.scrollDirectionInt = scrollDirection; displayMessage.scrollDirection = (DisplayMessage.ScrollDirection)scrollDirection; if (scrollDirection == DisplayMessage.ScrollDirectionNone) { displayMessage.scrollOffsetX = 0f; displayMessage.scrollOffsetY = 0f; SetDisplayMessageOffset(displayMessage, displayMessage.offsetX, displayMessage.offsetY); } } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayMessageScrollDirection - displayMessage is null"); } #endif } /// /// Set the Display Message to scroll across or up/down the full screen regardless of the message width and height. /// Can also be set directly with displayMessage.isScrollFullscreen = true; /// /// /// public void SetDisplayMessageScrollFullscreen(DisplayMessage displayMessage, bool isScrollFullscreen) { if (displayMessage != null) { displayMessage.isScrollFullscreen = isScrollFullscreen; } } /// /// Set the Display Message scroll speed. /// Can also be set directly with displayMessage.scrollSpeed = scrollSpeed; /// /// /// public void SetDisplayMessageScrollSpeed(DisplayMessage displayMessage, float scrollSpeed) { if (displayMessage != null) { displayMessage.scrollSpeed = scrollSpeed; } } /// /// Set the Display Message text colour. Only update the colour if it has actually changed. /// If the module has been initialised, this will also re-colour the message text with the appropriate brightness. /// /// public void SetDisplayMessageTextColour(DisplayMessage displayMessage, Color newColour) { if (displayMessage != null) { SSCUtils.UpdateColour(ref newColour, ref displayMessage.textColour, ref displayMessage.baseTextColour, true); SetDisplayMessageTextBrightness(displayMessage); } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayMessageTextColour - displayMessage is null"); } #endif } /// /// Show the Display Message on the HUD. The HUD will automatically be shown if it is not already visible. /// /// public void ShowDisplayMessage (int guidHash) { DisplayMessage displayMessage = GetDisplayMessage(guidHash); if (displayMessage != null) { ShowOrHideMessage(displayMessage, true, false); } #if UNITY_EDITOR else { Debug.LogWarning("WARNING: ShowDisplayMessage - Could not find message with guidHash: " + guidHash); } #endif } /// /// Show the Display Message on the HUD. The HUD will automatically be shown if it is not already visible. /// /// public void ShowDisplayMessage (DisplayMessage displayMessage) { if (displayMessage != null) { ShowOrHideMessage(displayMessage, true, false); } #if UNITY_EDITOR else { Debug.LogWarning("WARNING: ShowDisplayMessage - Could not find message - parameter was null"); } #endif } /// /// Show the Display Message on the HUD instantly by ignoring fade settings. The HUD will automatically be shown if it is not already visible. /// /// public void ShowDisplayMessageInstant (int guidHash) { DisplayMessage displayMessage = GetDisplayMessage(guidHash); if (displayMessage != null) { ShowOrHideMessage(displayMessage, true, true); } #if UNITY_EDITOR else { Debug.LogWarning("WARNING: ShowDisplayMessageInstant - Could not find message with guidHash: " + guidHash); } #endif } /// /// Show the Display Message on the HUD instantly by ignoring fade settings. The HUD will automatically be shown if it is not already visible. /// /// public void ShowDisplayMessageInstant (DisplayMessage displayMessage) { if (displayMessage != null) { ShowOrHideMessage(displayMessage, true, true); } #if UNITY_EDITOR else { Debug.LogWarning("WARNING: ShowDisplayMessageInstant - Could not find message - parameter was null"); } #endif } /// /// Show or turn on all Display Messages. ShipDisplayModule must be initialised. /// The HUD will automatically be shown if it is not already visible. /// public void ShowDisplayMessages() { ShowOrHideMessages(true, false); } /// /// Show the Display Message background on the HUD. The display the actual message, you would /// need to call ShowDisplayMessage(..). /// /// public void ShowDisplayMessageBackground(DisplayMessage displayMessage) { if (displayMessage != null) { ShowOrHideDisplayMessageBackground(displayMessage, true); } #if UNITY_EDITOR else { Debug.LogWarning("WARNING: ShowDisplayMessageBackground - Could not find message - parameter was null"); } #endif } /// /// Hide or turn off the Display Message /// /// public void HideDisplayMessage(int guidHash) { DisplayMessage displayMessage = GetDisplayMessage(guidHash); if (displayMessage != null) { ShowOrHideMessage(displayMessage, false, false); } #if UNITY_EDITOR else { Debug.LogWarning("WARNING: ShowDisplayMessage - Could not find message with guidHash: " + guidHash); } #endif } /// /// Hide or turn off the Display Message /// /// public void HideDisplayMessage (DisplayMessage displayMessage) { if (displayMessage != null) { ShowOrHideMessage(displayMessage, false, false); } #if UNITY_EDITOR else { Debug.LogWarning("WARNING: ShowDisplayMessage - Could not find message - parameter was null"); } #endif } /// /// Hide or turn off the Display Message instantly and ignore any fade-out settings. /// /// public void HideDisplayMessageInstant (int guidHash) { DisplayMessage displayMessage = GetDisplayMessage(guidHash); if (displayMessage != null) { ShowOrHideMessage(displayMessage, false, true); } #if UNITY_EDITOR else { Debug.LogWarning("WARNING: ShowDisplayMessageInstant - Could not find message with guidHash: " + guidHash); } #endif } /// /// Hide or turn off the Display Message instantly and ignore any fade-out settings. /// /// public void HideDisplayMessageInstant (DisplayMessage displayMessage) { if (displayMessage != null) { ShowOrHideMessage(displayMessage, false, true); } #if UNITY_EDITOR else { Debug.LogWarning("WARNING: ShowDisplayMessageInstant - Could not find message - parameter was null"); } #endif } /// /// Hide or turn off all Display Messages. /// ShipDisplayModule must be initialised. /// public void HideDisplayMessages() { ShowOrHideMessages(false, false); } /// /// Hide the Display Message background on the HUD. The hide the actual message, you would /// need to call HideDisplayMessage(..). /// /// public void HideDisplayMessageBackground(DisplayMessage displayMessage) { if (displayMessage != null) { ShowOrHideDisplayMessageBackground(displayMessage, false); } #if UNITY_EDITOR else { Debug.LogWarning("WARNING: HideDisplayMessageBackground - Could not find message - parameter was null"); } #endif } /// /// Create a new list if required /// public void ValidateMessageList() { if (displayMessageList == null) { displayMessageList = new List(1); } } #endregion #region Public API Methods - Targets /// /// Add a new Display Target to the HUD. By design, they are not visible at runtime when first added. /// /// /// public DisplayTarget AddTarget(int guidHashDisplayReticle) { DisplayTarget displayTarget = null; if (GetHUDPanel() == null) { #if UNITY_EDITOR Debug.LogWarning("ERROR: ShipDisplayModule.AddTarget() - could not find HUDPanel for the HUD. Did you use the prefab from Prefabs/Visuals folder?"); #endif } else { displayTarget = new DisplayTarget(); if (displayTarget != null) { displayTarget.guidHashDisplayReticle = guidHashDisplayReticle; ValidateTargetList(); displayTargetList.Add(displayTarget); numDisplayTargets = displayTargetList.Count; RectTransform targetSlotPanel = CreateTargetPanel(displayTarget, 0); if (targetSlotPanel != null) { #if UNITY_EDITOR // Make sure we can rollback the creation of the new target panel for this slot UnityEditor.Undo.RegisterCreatedObjectUndo(targetSlotPanel.gameObject, string.Empty); #endif } } } return displayTarget; } /// /// Add another DisplayTarget slot to a DisplayTarget. This allows you to display another /// copy of the target on the HUD. /// If the DisplayTarget has not been initialised, the new slot panel will be added to the /// scene but this method will return null. /// This automatically updates displayTarget.maxNumberOfTargets. /// /// /// /// public DisplayTargetSlot AddTargetSlots(DisplayTarget displayTarget, int numberToAdd) { DisplayTargetSlot displayTargetSlot = null; if (displayTarget != null) { if (displayTarget.maxNumberOfTargets + numberToAdd <= DisplayTarget.MAX_DISPLAYTARGET_SLOTS) { int numberAdded = 0; // slotIndex is zero-based so start to 1 beyond the end of the existing list for (int slotIndex = displayTarget.maxNumberOfTargets; slotIndex < displayTarget.maxNumberOfTargets + numberToAdd; slotIndex++) { RectTransform targetSlotPanel = CreateTargetPanel(displayTarget, slotIndex); // If the DisplayTarget has already been initialised, this slot will need to be initialised if (targetSlotPanel != null) { numberAdded++; #if UNITY_EDITOR // Make sure we can rollback the creation of the new target panel for this slot UnityEditor.Undo.RegisterCreatedObjectUndo(targetSlotPanel.gameObject, string.Empty); #endif if (displayTarget.isInitialised) { displayTargetSlot = InitialiseTargetSlot(displayTarget, slotIndex); displayTarget.displayTargetSlotList.Add(displayTargetSlot); // Refresh the size of the slot reticle panel #if UNITY_EDITOR if (editorMode) { CheckHUDResize(false); } #endif RectTransform dtRectTfrm = displayTargetSlot.CachedTargetPanel; if (dtRectTfrm != null) { float pixelWidth = (float)displayTarget.width * screenResolution.x / refResolution.x; float pixelHeight = (float)displayTarget.height * screenResolution.y / refResolution.y; // Target size is in pixels - this will result in non-square scaling. // (Future) provide an option to keep aspect ratio of original 64x64 reticle dtRectTfrm.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, pixelWidth); dtRectTfrm.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, pixelHeight); } // Refresh brightness of this slot if (displayTargetSlot.CachedImgComponent != null) { if (brightness == 1f) { displayTargetSlot.CachedImgComponent.color = displayTarget.reticleColour; } else { displayTargetSlot.CachedImgComponent.color = displayTarget.baseReticleColour.GetColorWithBrightness(brightness); } } } } } displayTarget.maxNumberOfTargets += numberAdded; } #if UNITY_EDITOR else { Debug.LogWarning("WARNING: shipDisplayModule.AddTargetSlot - not enough empty DisplayTarget slots"); } #endif } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.AddTargetSlot - displayTarget is null"); } #endif return displayTargetSlot; } /// /// Delete a target from the HUD. /// NOTE: It is much cheaper to HideDisplayTarget(..) than completely remove it. /// /// public void DeleteTarget(int guidHash) { #if UNITY_EDITOR bool isRecordUndo = !Application.isPlaying; int undoGroup=0; #else bool isRecordUndo = false; #endif DisplayTarget displayTarget = GetDisplayTarget(guidHash); List tgtRectTfrmList = null; // Get a list of DisplayTarget slot panels if (displayTarget != null) { tgtRectTfrmList = new List(displayTarget.maxNumberOfTargets); for (int sIdx = 0; sIdx < displayTarget.maxNumberOfTargets; sIdx++) { RectTransform tgtRectTfrm = GetTargetPanel(guidHash, sIdx); if (tgtRectTfrm != null) { tgtRectTfrmList.Add(tgtRectTfrm); } } } int _numPanels = tgtRectTfrmList == null ? 0 : tgtRectTfrmList.Count; int _tgtIndex = GetDisplayTargetIndex(guidHash); // Only record undo if in the editor AND is not playing AND something needs to be recorded isRecordUndo = isRecordUndo && (_numPanels > 0 || _tgtIndex >= 0); #if UNITY_EDITOR if (isRecordUndo) { UnityEditor.Undo.SetCurrentGroupName("Delete Display Target " + (_tgtIndex + 1).ToString()); undoGroup = UnityEditor.Undo.GetCurrentGroup(); if (_tgtIndex >= 0) { UnityEditor.Undo.RecordObject(this, string.Empty); } } #endif // Check that the display target exists and it is within the list constraints // NOTE: The script element must be modified BEFORE the Undo.DestroyObjectImmediate is called // in order for Undo to correctly undo both the target change AND the destroy of the panels. if (_tgtIndex >= 0 && _tgtIndex < (displayTargetList == null ? 0 : displayTargetList.Count)) { displayTargetList.RemoveAt(_tgtIndex); numDisplayTargets = displayTargetList.Count; } if (_numPanels > 0) { if (Application.isPlaying) { // Destroy all the slot panels for (int sIdx = _numPanels - 1; sIdx >= 0; sIdx--) { Destroy(tgtRectTfrmList[sIdx].gameObject); tgtRectTfrmList.RemoveAt(sIdx); } } else { // Destroy all the slot panels for (int sIdx = _numPanels - 1; sIdx >= 0; sIdx--) { #if UNITY_EDITOR UnityEditor.Undo.DestroyObjectImmediate(tgtRectTfrmList[sIdx].gameObject); #else DestroyImmediate(tgtRectTfrmList[sIdx].gameObject); #endif tgtRectTfrmList.RemoveAt(sIdx); } } } #if UNITY_EDITOR if (isRecordUndo) { UnityEditor.Undo.CollapseUndoOperations(undoGroup); } #endif } /// /// Delete or remove a DisplayTarget slot from the HUD. This is an expensive operation. /// It is much cheaper to HideDisplayTargetSlot(..) than completely remove it. /// Automatically updates displayTarget.maxNumberOfTargets. /// NOTE: You cannot remove slot 0. /// /// guidHash of the DisplayTarget /// public void DeleteTargetSlots(int guidHash, int numberToDelete) { DisplayTarget displayTarget = GetDisplayTarget(guidHash); if (displayTarget != null) { int remainingTargets = displayTarget.maxNumberOfTargets - numberToDelete; if (remainingTargets > 0) { #if UNITY_EDITOR bool isRecordUndo = !Application.isPlaying; int undoGroup=0; #else bool isRecordUndo = false; #endif List tgtRectTfrmList = null; // Get a list of DisplayTarget slot panels to remove if (displayTarget != null) { tgtRectTfrmList = new List(displayTarget.maxNumberOfTargets); for (int sIdx = remainingTargets; sIdx < displayTarget.maxNumberOfTargets; sIdx++) { RectTransform tgtRectTfrm = GetTargetPanel(guidHash, sIdx); if (tgtRectTfrm != null) { tgtRectTfrmList.Add(tgtRectTfrm); } } } int _numPanels = tgtRectTfrmList == null ? 0 : tgtRectTfrmList.Count; int _tgtIndex = GetDisplayTargetIndex(guidHash); // Only record undo if in the editor AND is not playing AND something needs to be recorded isRecordUndo = isRecordUndo && (_numPanels > 0 || _tgtIndex >= 0); #if UNITY_EDITOR if (isRecordUndo) { UnityEditor.Undo.SetCurrentGroupName("Delete Display Target " + (_tgtIndex + 1).ToString() + " slots"); undoGroup = UnityEditor.Undo.GetCurrentGroup(); if (_tgtIndex >= 0) { UnityEditor.Undo.RecordObject(this, string.Empty); } } #endif // NOTE: The script element must be modified BEFORE the Undo.DestroyObjectImmediate is called // in order for Undo to correctly undo both the target change AND the destroy of the panels. if (displayTarget.isInitialised) { for (int sIdx = displayTarget.maxNumberOfTargets - 1; sIdx >= remainingTargets; sIdx--) { if (sIdx < displayTarget.displayTargetSlotList.Count) { displayTarget.displayTargetSlotList[sIdx] = null; displayTarget.displayTargetSlotList.RemoveAt(sIdx); } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.DeleteTargetSlot - tried to remove initialised slot " + sIdx + " but only " + displayTarget.displayTargetSlotList.Count + " exist."); } #endif } } displayTarget.maxNumberOfTargets = remainingTargets; if (_numPanels > 0) { if (Application.isPlaying) { // Destroy the slot panels for (int sIdx = _numPanels - 1; sIdx >= 0; sIdx--) { Destroy(tgtRectTfrmList[sIdx].gameObject); tgtRectTfrmList.RemoveAt(sIdx); } } else { // Destroy the slot panels for (int sIdx = _numPanels - 1; sIdx >= 0; sIdx--) { #if UNITY_EDITOR UnityEditor.Undo.DestroyObjectImmediate(tgtRectTfrmList[sIdx].gameObject); #else DestroyImmediate(tgtRectTfrmList[sIdx].gameObject); #endif tgtRectTfrmList.RemoveAt(sIdx); } } } #if UNITY_EDITOR if (isRecordUndo) { UnityEditor.Undo.CollapseUndoOperations(undoGroup); } #endif } #if UNITY_EDITOR else { Debug.LogWarning("WARNING: shipDisplayModule.DeleteTargetSlot - cannot delete that many DisplayTarget slots"); } #endif } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.DeleteTargetSlot - displayTarget cannot be found with guidHash: " + guidHash); } #endif } /// /// Assign a radar target to a DisplayTarget's slot. /// See also GetAssignedDisplayTarget(..) /// /// /// /// /// public void AssignDisplayTargetSlot(int displayTargetIndex, int slotIndex, int radarItemIndex, uint radarItemSequenceNumber, bool isAutoShow) { if (displayTargetIndex >= 0 && displayTargetIndex < GetNumberDisplayTargets) { DisplayTarget displayTarget = displayTargetList[displayTargetIndex]; if (displayTarget != null && slotIndex >= 0 && slotIndex < displayTarget.maxNumberOfTargets) { DisplayTargetSlot displayTargetSlot = displayTarget.displayTargetSlotList[slotIndex]; displayTargetSlot.radarItemKey.radarItemIndex = radarItemIndex; displayTargetSlot.radarItemKey.radarItemSequenceNumber = radarItemSequenceNumber; if (isAutoShow) { // Should the DisplayTarget be shown? if (radarItemIndex >= 0) { // If not already shown, turn it on. if (!displayTargetSlot.showTargetSlot) { ShowOrHideTargetSlot(displayTargetSlot, true); // If one slot is on, mark the displayTarget as shown also. displayTarget.showTarget = true; } } // If should not be shown, but currently is, turn it off else if (displayTargetSlot.showTargetSlot) { ShowOrHideTargetSlot(displayTargetSlot, false); } } } } } /// /// Get the radarItemKey for a DisplayTarget's slot. /// SSCRadarItemKey.radarItemIndex will be -1 if there is no target. /// /// /// /// public SSCRadarItemKey GetAssignedDisplayTarget(int displayTargetIndex, int slotIndex) { if (displayTargetIndex >= 0 && displayTargetIndex < GetNumberDisplayTargets) { DisplayTarget displayTarget = displayTargetList[displayTargetIndex]; if (displayTarget != null && slotIndex >= 0 && slotIndex < displayTarget.maxNumberOfTargets) { return displayTarget.displayTargetSlotList[slotIndex].radarItemKey; } else { return new SSCRadarItemKey() { radarItemIndex = -1, radarItemSequenceNumber = 0 }; } } else { return new SSCRadarItemKey() { radarItemIndex = -1, radarItemSequenceNumber = 0 }; } } /// /// Get the SSCRadarItem currently assigned to a DisplayTarget slot (instance). /// /// /// public SSCRadarItem GetAssignedDisplayTargetRadarItem (DisplayTargetSlot displayTargetSlot) { if (displayTargetSlot != null && displayTargetSlot.radarItemKey.radarItemIndex >= 0 && sscRadar != null) { return sscRadar.GetRadarItem(displayTargetSlot.radarItemKey.radarItemIndex); } else { return null; } } /// /// Returns the guidHash of the Target in the list given the index or /// zero-based position in the list. Will return 0 if no matching Target /// is found. /// /// /// public int GetDisplayTargetGuidHash(int index) { int guidHash = 0; if (index >= 0 && index < GetNumberDisplayTargets) { if (displayTargetList[index] != null) { guidHash = displayTargetList[index].guidHash; } } return guidHash; } /// /// Get the zero-based index of the Target in the list. Will return -1 if not found. /// /// /// public int GetDisplayTargetIndex(int guidHash) { int index = -1; int _numDisplayTargets = GetNumberDisplayTargets; for (int dmIdx = 0; dmIdx < _numDisplayTargets; dmIdx++) { if (displayTargetList[dmIdx] != null && displayTargetList[dmIdx].guidHash == guidHash) { index = dmIdx; break; } } return index; } /// /// Get a DisplayTarget given its guidHash. /// See also GetDisplayTargetGuidHash(..). /// Will return null if guidHash parameter is 0, it cannot be found. /// /// /// public DisplayTarget GetDisplayTarget(int guidHash) { DisplayTarget displayTarget = null; DisplayTarget _tempDM = null; int _numDisplayTargets = GetNumberDisplayTargets; for (int dmIdx = 0; dmIdx < _numDisplayTargets; dmIdx++) { _tempDM = displayTargetList[dmIdx]; if (_tempDM != null && _tempDM.guidHash == guidHash) { displayTarget = _tempDM; break; } } return displayTarget; } /// /// Get a DisplayTarget given a zero-based index in the list. /// /// /// public DisplayTarget GetDisplayTargetByIndex(int index) { if (index >= 0 && index < GetNumberDisplayTargets) { return displayTargetList[index]; } else { return null; } } /// /// Get the (sprite) name of the DisplayTarget. /// WARNING: This will create GC, so not recommended to be called each frame. /// This is typically used for debugging purposes only. /// /// /// public string GetDisplayTargetName(DisplayTarget displayTarget) { if (displayTarget != null && displayReticleList != null) { int _selectedIdx = displayReticleList.FindIndex(dr => dr.guidHash == displayTarget.guidHashDisplayReticle); if (_selectedIdx >= 0) { string tempDisplayReticleName = displayReticleList[_selectedIdx].primarySprite == null ? "no texture in reticle" : displayReticleList[_selectedIdx].primarySprite.name; return string.IsNullOrEmpty(tempDisplayReticleName) ? "no texture name" : tempDisplayReticleName; } else { return "--"; } } else { return "--"; } } /// /// Get the (sprite) name of the DisplayTarget. /// WARNING: This will create GC, so not recommended to be called each frame. /// This is typically used for debugging purposes only. /// /// /// public string GetDisplayTargetName(int index) { return GetDisplayTargetName(GetDisplayTargetByIndex(index)); } /// /// Get a DisplayTargetSlot given the zero-based slot index. This is an instance of a DisplayTarget. /// /// /// /// public DisplayTargetSlot GetDisplayTargetSlotByIndex (DisplayTarget displayTarget, int slotIndex) { if (IsInitialised && displayTarget != null && slotIndex > -1 && slotIndex < displayTarget.maxNumberOfTargets) { return displayTarget.displayTargetSlotList[slotIndex]; } else { return null; } } /// /// Show the Display Target slots on the HUD. By design, if the HUD is not shown, the Targets will not be show. /// /// public void ShowDisplayTarget(int guidHash) { DisplayTarget displayTarget = GetDisplayTarget(guidHash); if (displayTarget != null) { ShowOrHideTarget(displayTarget, true); } #if UNITY_EDITOR else { Debug.LogWarning("WARNING: ShowDisplayTarget - Could not find displayTarget with guidHash: " + guidHash); } #endif } /// /// Show the Display Target slots on the HUD. By design, if the HUD is not shown, the Targets will not be show. /// /// public void ShowDisplayTarget(DisplayTarget displayTarget) { if (displayTarget != null) { ShowOrHideTarget(displayTarget, true); } #if UNITY_EDITOR else { Debug.LogWarning("WARNING: ShowDisplayTarget - displayTarget parameter was null"); } #endif } /// /// Show the Display Target slot on the HUD. By design, if the HUD is not shown, the Target in this slot will not be show. /// /// public void ShowDisplayTargetSlot(DisplayTargetSlot displayTargetSlot) { if (displayTargetSlot != null) { ShowOrHideTargetSlot(displayTargetSlot, true); } #if UNITY_EDITOR else { Debug.LogWarning("WARNING: ShowDisplayTargetSlot - displayTargetSlot parameter was null"); } #endif } /// /// Show or turn on all Display Targets. /// ShipDisplayModule must be initialised. /// public void ShowDisplayTargets() { ShowOrHideTargets(true); } /// /// Hide or turn off all slots the Display Target /// /// public void HideDisplayTarget(int guidHash) { DisplayTarget displayTarget = GetDisplayTarget(guidHash); if (displayTarget != null) { ShowOrHideTarget(displayTarget, false); } #if UNITY_EDITOR else { Debug.LogWarning("WARNING: ShowDisplayTarget - Could not find message with guidHash: " + guidHash); } #endif } /// /// Hide or turn off all slots of the Display Target /// /// public void HideDisplayTarget(DisplayTarget displayTarget) { if (displayTarget != null) { ShowOrHideTarget(displayTarget, false); } #if UNITY_EDITOR else { Debug.LogWarning("WARNING: ShowDisplayTarget - Could not find message - parameter was null"); } #endif } /// /// Hide the Display Target slot on the HUD. By design, if the HUD is not shown, the Target in this slot will not be show. /// /// public void HideDisplayTargetSlot(DisplayTargetSlot displayTargetSlot) { if (displayTargetSlot != null) { ShowOrHideTargetSlot(displayTargetSlot, false); } #if UNITY_EDITOR else { Debug.LogWarning("WARNING: HideDisplayTargetSlot - displayTargetSlot parameter was null"); } #endif } /// /// Hide or turn off all all slots of all Display Targets. /// ShipDisplayModule must be initialised. /// public void HideDisplayTargets() { ShowOrHideTargets(false); } /// /// Given a radar query, add rules based on the current DisplayTargets. /// TODO - WARNING - NEED TO PERFORMANCE TEST THIS /// public void PopulateTargetingRadarQuery(SSCRadarQuery sscRadarQuery) { // Build a common list of factions and squadrons to include if (IsInitialised) { int _numDisplayTargets = GetNumberDisplayTargets; if (_numDisplayTargets > 0) { tempIncludeFactionList.Clear(); tempIncludeSquadronList.Clear(); for (int dtIdx = 0; dtIdx < _numDisplayTargets; dtIdx++) { DisplayTarget _tempDT = displayTargetList[dtIdx]; if (_tempDT != null) { int _numFactionsToInclude = _tempDT.factionsToInclude == null ? 0 : _tempDT.factionsToInclude.Length; int _numSquadronsToInclude = _tempDT.squadronsToInclude == null ? 0 : _tempDT.squadronsToInclude.Length; // By default there is no exclude sscRadarQuery.factionsToExclude = null; sscRadarQuery.squadronsToExclude = null; // Determine which Factions (if any) to include as a filter for (int fIdx = 0; fIdx < _numFactionsToInclude; fIdx++) { // If faction is not already in list add it. int _factionId = _tempDT.factionsToInclude[fIdx]; if (tempIncludeFactionList.FindIndex(f => f == _factionId) < 0) { tempIncludeFactionList.Add(_factionId); } } // Determine which Squadrons (if any) to include as a filter for (int sqIdx = 0; sqIdx < _numSquadronsToInclude; sqIdx++) { // If squadron is not already in list add it. int _squadronId = _tempDT.squadronsToInclude[sqIdx]; if (tempIncludeSquadronList.FindIndex(sq => sq == _squadronId) < 0) { tempIncludeSquadronList.Add(_squadronId); } } if (tempIncludeFactionList.Count > 0) { // Check if this generates work for GC sscRadarQuery.factionsToInclude = tempIncludeFactionList.ToArray(); } else { sscRadarQuery.factionsToInclude = null; } if (tempIncludeSquadronList.Count > 0) { // Check if this generates work for GC sscRadarQuery.squadronsToInclude = tempIncludeSquadronList.ToArray(); } else { sscRadarQuery.squadronsToInclude = null; } } } } sscRadarQuery.range = targetingRange; } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: PopulateTargetingRadarQuery - shipDisplayModule is not initialised"); } #endif } /// /// Set or change the Reticle assigned to a DisplayTarget. /// The Reticle must belong to the list of available reticles for the HUD. /// /// /// public void SetDisplayTargetReticle(int guidHash, int guidHashDisplayReticle) { if (IsInitialised || editorMode) { DisplayTarget displayTarget = GetDisplayTarget(guidHash); if (displayTarget != null) { displayTarget.guidHashDisplayReticle = guidHashDisplayReticle; DisplayReticle _displayReticle = GetDisplayReticle(guidHashDisplayReticle); // Set the reticle for all slots of this DisplayTarget for (int sIdx = 0; sIdx < displayTarget.maxNumberOfTargets; sIdx++) { if (displayTarget.displayTargetSlotList[sIdx].CachedImgComponent != null) { displayTarget.displayTargetSlotList[sIdx].CachedImgComponent.sprite = _displayReticle == null ? null : _displayReticle.primarySprite; } } } } } /// /// Set the Display Target Reticle colour. Only update the colour if it has actually changed. /// If the module has been initialised, this will also re-colour the reticle with the appropriate brightness. /// /// public void SetDisplayTargetReticleColour(DisplayTarget displayTarget, Color newColour) { if (displayTarget != null) { SSCUtils.UpdateColour(ref newColour, ref displayTarget.reticleColour, ref displayTarget.baseReticleColour, true); SetDisplayTargetBrightness(displayTarget); } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayTargetReticleColour - displayTarget is null"); } #endif } /// /// Set the offset (position) of the Display Target slot on the HUD. /// If the module has been initialised, this will also re-position the Display Target. /// /// /// /// /// public void SetDisplayTargetOffset(DisplayTarget displayTarget, int slotIndex, float offsetX, float offsetY) { if (displayTarget != null) { int numSlots = displayTarget.displayTargetSlotList == null ? 0 : displayTarget.displayTargetSlotList.Count; if (slotIndex >= 0 & slotIndex < numSlots) { SetDisplayTargetOffset(displayTarget.displayTargetSlotList[slotIndex], offsetX, offsetY); } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayTargetOffset - the displayTarget slot is out-of-range (" + slotIndex + ")"); } #endif } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayTargetOffset - displayTarget is null"); } #endif } /// /// Set the offset (position) of the Display Target slot on the HUD. /// If the module has been initialised, this will also re-position the Display Target. /// /// /// Horizontal offset from centre. Range between -1 and 1 /// Vertical offset from centre. Range between -1 and 1 public void SetDisplayTargetOffset(DisplayTargetSlot displayTargetSlot, float offsetX, float offsetY) { if (displayTargetSlot != null) { // Verify the x,y values are within range -1 to 1. if (offsetX < -1f) { displayTargetSlot.offsetX = -1f; } else if (offsetX > 1f) { displayTargetSlot.offsetX = 1f; } else { displayTargetSlot.offsetX = offsetX; } if (offsetY < -1f) { displayTargetSlot.offsetY = -1f; } else if (offsetY > 1f) { displayTargetSlot.offsetY = 1f; } else { displayTargetSlot.offsetY = offsetY; } if (IsInitialised || editorMode) { Transform dtgtTrfm = displayTargetSlot.CachedTargetPanel.transform; #if UNITY_EDITOR if (editorMode) { CheckHUDResize(false); UnityEditor.Undo.RecordObject(dtgtTrfm, string.Empty); } #endif // Use the scaled HUD size. This works in and out of play mode tempTargetOffset.x = displayTargetSlot.offsetX * cvsResolutionHalf.x; tempTargetOffset.y = displayTargetSlot.offsetY * cvsResolutionHalf.y; tempTargetOffset.z = dtgtTrfm.localPosition.z; dtgtTrfm.localPosition = tempTargetOffset; } } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayTargetOffset - displayTargetSlot is null"); } #endif } /// /// Move the DisplayTarget Slot to the correct 2D position on the HUD, based on a 3D world space position. /// If the camera has not been automatically or manually assigned, the DisplayTarget will not be moved. /// /// /// /// public void SetDisplayTargetPosition(DisplayTarget displayTarget, int slotIndex, Vector3 wsPosition) { if (isMainCameraAssigned) { if (displayTarget != null) { Vector3 screenPosition = mainCamera.WorldToScreenPoint(wsPosition); // NOTE: cvsResolutionFull.x / cvsScaleFactor.x is NOT the same as Screen.width // x 2 and subtracting 1 moves it into our HUD -1 to 1 space. SetDisplayTargetOffset(displayTarget, slotIndex, 2f * screenPosition.x / cvsResolutionFull.x / cvsScaleFactor.x - 1f, 2f * screenPosition.y / cvsResolutionFull.y / cvsScaleFactor.y - 1f); } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: shipDisplayModule.SetDisplayTargetPosition - displayTarget is null"); } #endif } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: ShipDisplayModule.SetDisplayTargetPosition - Could not find mainCamera."); } #endif } /// /// Set the size of the Target Reticle in each slot. /// If the module has been initialised, this will also resize the Target Reticle. /// The values are only updated if they are outside the range 8 to 256 or have changed. /// The size is based on a screen resolution of 1920x1080 /// /// /// Range between 8 and 256 /// Range between 8 and 256 public void SetDisplayTargetSize(DisplayTarget displayTarget, int width, int height) { // Clamp the x,y values 8 to 256 if (width < 8) { displayTarget.width = 8; } else if (width > 256) { displayTarget.width = 256; } else if (width != displayTarget.width) { displayTarget.width = width; } if (height < 8) { displayTarget.height = 8; } else if (height > 256) { displayTarget.height = 256; } else if (height != displayTarget.height) { displayTarget.height = height; } if (IsInitialised || editorMode) { #if UNITY_EDITOR if (editorMode) { CheckHUDResize(false); } #endif float pixelWidth = (float)displayTarget.width * screenResolution.x / refResolution.x; float pixelHeight = (float)displayTarget.height * screenResolution.y / refResolution.y; // Set the size of all the reticles in the DisplayTarget slots // There can be multiple copies or slots of the same DisplayTarget. for (int sIdx = 0; sIdx < displayTarget.maxNumberOfTargets; sIdx++) { RectTransform dtRectTfrm = displayTarget.displayTargetSlotList[sIdx].CachedTargetPanel; if (dtRectTfrm != null) { // Target size is in pixels - this will result in non-square scaling. // (Future) provide an option to keep aspect ratio of original 64x64 reticle dtRectTfrm.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, pixelWidth); dtRectTfrm.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, pixelHeight); } } } } /// /// Sets the clipped viewable area of the screen that DisplayTargets can be shown. When Targets /// are outside this area, they will be hidden. /// See also SetTargetsViewportOffset(..). /// /// Range between 0.1 and 1.0 /// Range between 0.1 and 1.0 public void SetTargetsViewportSize(float width, float height) { // Currently we just set the values after basic validation. This may change in future versions // Perform basic validation if (width >= 0.1f && width <= 1.0f) { targetsViewWidth = width; } if (height >= 0.1f && height <= 1.0f) { targetsViewHeight = height; } } /// /// Sets the Targets viewport offset from the centre of the screen. /// Values can be between -1.0 and 1.0 with 0,0 depicting the centre of the screen. /// See also SetTargetsViewportSize(..). /// /// Range between -1.0 and 1.0 /// Range between -1.0 and 1.0 public void SetTargetsViewportOffset(float offsetX, float offsetY) { // Currently we just set the values after basic validation. This may change in future versions if (offsetX >= -1f && offsetX <= 1.0f) { targetsViewOffsetX = offsetX; } if (offsetY >= -1f && offsetY <= 1.0f) { targetsViewOffsetY = offsetY; } } /// /// After adding or moving DisplayTargets, they may need to be sorted to /// have the correct z-order in on the HUD. /// public void RefreshTargetsSortOrder() { if (targetsRectTfrm != null) { // Targets should begin at index 0. int zIndex = -1; // Update number of DisplayTargets. This can help issues in the Editor, like moving DislayTargets // up or down in the list while in play mode. numDisplayTargets = displayTargetList == null ? 0 : displayTargetList.Count; for (int dtIdx = 0; dtIdx < numDisplayTargets; dtIdx++) { DisplayTarget displayTarget = displayTargetList[dtIdx]; if (displayTarget != null) { //Debug.Log("[DEBUG] dt: " + dtIdx + " maxNum: " + displayTarget.maxNumberOfTargets + " actual: " + displayTarget.displayTargetSlotList.Count); // Loop through all the slots for this DisplayTarget for (int sIdx = 0; sIdx < displayTarget.maxNumberOfTargets; sIdx++) { RectTransform _rt = displayTarget.displayTargetSlotList[sIdx].CachedTargetPanel; if (_rt != null) { _rt.SetSiblingIndex(++zIndex); } } } } } } /// /// Create a new list if required /// public void ValidateTargetList() { if (displayTargetList == null) { displayTargetList = new List(1); } } #endregion } }