< Summary

Class:TacticalPanel
Assembly:bamlab.micromissiles
File(s):/github/workspace/Assets/Scripts/UI/TacticalPanel.cs
Covered lines:37
Uncovered lines:143
Coverable lines:180
Total lines:280
Line coverage:20.5% (37 of 180)
Covered branches:0
Total branches:0
Covered methods:9
Total methods:28
Method coverage:32.1% (9 of 28)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
TacticalPanel()0%110100%
Pan(...)0%2100%
PanWithKeyboard(...)0%2100%
ZoomIn(...)0%2100%
ZoomOut(...)0%2100%
CycleRangeUp()0%2100%
CycleRangeDown()0%2100%
Awake()0%3.333066.67%
Start()0%2100%
OnDestroy()0%6200%
OnEnable()0%3.032036.36%
OnDisable()0%4.114080.95%
RegisterSimulationEnded()0%2100%
RegisterNewAgent(...)0%6200%
SymbolsManager()0%3.043083.33%
InitializeOrigin()0%2100%
InitializeAgents(...)0%12300%
CreateSymbolPrefab()0%2100%
CreateSymbol(...)0%1101000%
DestroySymbol(...)0%6200%
DestroyAllSymbols()0%2.262060%
UpdateSymbols()0%2.752042.86%
UpdateAgentSymbols(...)0%20400%
UpdateAgentSymbol(...)0%6200%
UpdateSymbolScale(...)0%2100%
AdjustRadarScale(...)0%20400%

File(s)

/github/workspace/Assets/Scripts/UI/TacticalPanel.cs

#LineLine coverage
 1using System.Collections;
 2using System.Collections.Generic;
 3using UnityEngine;
 4
 5public class TacticalPanel : MonoBehaviour {
 6  // Symbol position update period in seconds.
 7  private const float _symbolsUpdatePeriod = 0.1f;  // 10 Hz
 8
 9  // Keyboard pan speed.
 10  private const float _keyboardPanSpeed = 500f;
 11
 12  // Zoom speed.
 13  private const float _zoomSpeed = 10f;
 14
 15  [SerializeField]
 16  [Tooltip("The UI group that contains the radar symbology elements")]
 117  private GameObject _radarUIGroup = null!;
 18
 19  private RectTransform _radarUIGroupRectTransform;
 20
 21  private TacticalPolarGridGraphic _polarGridGraphic;
 22
 23  // Coroutine to update the symbols.
 24  private Coroutine _symbolsCoroutine;
 25
 26  // Dictionary from each agent to its tactical symbol.
 127  private Dictionary<IAgent, GameObject> _symbols = new Dictionary<IAgent, GameObject>();
 28
 29  // Origin symbol.
 30  // TODO(titan): This origin symbol is needed until an asset agent is implemented.
 31  private GameObject _origin;
 32
 233  public static TacticalPanel Instance { get; private set; }
 34
 035  public void Pan(in Vector2 delta) {
 036    Vector3 currentPosition = _radarUIGroupRectTransform.localPosition;
 037    _radarUIGroupRectTransform.localPosition =
 38        new Vector3(currentPosition.x + delta.x, currentPosition.y + delta.y, currentPosition.z);
 039  }
 40
 041  public void PanWithKeyboard(in Vector2 direction) {
 042    Vector2 delta = direction * _keyboardPanSpeed * Time.unscaledDeltaTime;
 043    Pan(delta);
 044  }
 45
 046  public void ZoomIn(float amount) {
 047    AdjustRadarScale(amount * _zoomSpeed);
 048  }
 49
 050  public void ZoomOut(float amount) {
 051    AdjustRadarScale(-amount * _zoomSpeed);
 052  }
 53
 054  public void CycleRangeUp() {
 055    _polarGridGraphic.CycleRangeUp();
 056  }
 57
 058  public void CycleRangeDown() {
 059    _polarGridGraphic.CycleRangeDown();
 060  }
 61
 162  private void Awake() {
 163    if (Instance != null && Instance != this) {
 064      Destroy(gameObject);
 065      return;
 66    }
 167    Instance = this;
 68
 169    _radarUIGroupRectTransform = _radarUIGroup.GetComponent<RectTransform>();
 170    _polarGridGraphic = _radarUIGroup.GetComponent<TacticalPolarGridGraphic>();
 171  }
 72
 073  private void Start() {
 074    _radarUIGroupRectTransform.localScale = Vector3.one;
 075    _radarUIGroupRectTransform.localPosition = Vector3.zero;
 76
 077    InitializeOrigin();
 078  }
 79
 080  private void OnDestroy() {
 081    if (_symbolsCoroutine != null) {
 082      StopCoroutine(_symbolsCoroutine);
 083      _symbolsCoroutine = null;
 084    }
 085  }
 86
 187  private void OnEnable() {
 188    if (SimManager.Instance != null) {
 089      SimManager.Instance.OnSimulationEnded += RegisterSimulationEnded;
 090      SimManager.Instance.OnNewInterceptor += RegisterNewAgent;
 091      SimManager.Instance.OnNewThreat += RegisterNewAgent;
 092      InitializeAgents(SimManager.Instance.Interceptors);
 093      InitializeAgents(SimManager.Instance.Threats);
 094    }
 195    _symbolsCoroutine = StartCoroutine(SymbolsManager(_symbolsUpdatePeriod));
 196  }
 97
 198  private void OnDisable() {
 299    if (SimManager.Instance != null) {
 1100      SimManager.Instance.OnSimulationEnded -= RegisterSimulationEnded;
 1101      SimManager.Instance.OnNewInterceptor -= RegisterNewAgent;
 1102      SimManager.Instance.OnNewThreat -= RegisterNewAgent;
 1103    }
 3104    foreach (var agent in _symbols.Keys) {
 0105      agent.OnTerminated -= DestroySymbol;
 0106    }
 2107    if (_symbolsCoroutine != null) {
 1108      StopCoroutine(_symbolsCoroutine);
 1109      _symbolsCoroutine = null;
 1110    }
 1111    DestroyAllSymbols();
 1112  }
 113
 0114  private void RegisterSimulationEnded() {
 0115    DestroyAllSymbols();
 0116  }
 117
 0118  public void RegisterNewAgent(IAgent agent) {
 0119    if (!_symbols.ContainsKey(agent)) {
 0120      agent.OnTerminated += DestroySymbol;
 0121    }
 0122    CreateSymbol(agent);
 0123  }
 124
 1125  private IEnumerator SymbolsManager(float period) {
 2126    while (true) {
 1127      UpdateSymbols();
 1128      yield return new WaitForSeconds(period);
 0129    }
 130  }
 131
 0132  private void InitializeOrigin() {
 0133    GameObject origin = CreateSymbolPrefab();
 0134    origin.GetComponent<TacticalSymbol>().SetSprite("friendly_carrier_present");
 0135    origin.GetComponent<TacticalSymbol>().DisableDirectionArrow();
 0136    _origin = origin;
 0137  }
 138
 0139  private void InitializeAgents(IEnumerable<IAgent> agents) {
 0140    foreach (var agent in agents) {
 0141      agent.OnTerminated += DestroySymbol;
 0142      CreateSymbol(agent);
 0143    }
 0144  }
 145
 0146  private GameObject CreateSymbolPrefab() {
 0147    return Instantiate(Resources.Load<GameObject>("Prefabs/Symbols/Symbol"),
 148                       _radarUIGroupRectTransform);
 0149  }
 150
 0151  private void CreateSymbol(IAgent agent) {
 0152    if (_symbols.TryGetValue(agent, out GameObject existingSymbol)) {
 0153      UpdateAgentSymbol(existingSymbol, agent);
 0154      return;
 155    }
 156
 0157    GameObject symbol = CreateSymbolPrefab();
 0158    TacticalSymbol tacticalSymbol = symbol.GetComponent<TacticalSymbol>();
 159
 160    // Set common properties.
 0161    tacticalSymbol.SetSprite(agent.StaticConfig.VisualizationConfig?.SymbolPresent);
 0162    tacticalSymbol.SetDirectionArrowRotation(Mathf.Atan2(agent.Velocity.z, agent.Velocity.x) *
 163                                             Mathf.Rad2Deg);
 164
 165    // Set type-specific properties.
 0166    switch (agent.StaticConfig.AgentType) {
 167      case Configs.AgentType.Vessel:
 0168      case Configs.AgentType.ShoreBattery: {
 0169        tacticalSymbol.SetType("Launcher");
 0170        break;
 171      }
 0172      case Configs.AgentType.CarrierInterceptor: {
 0173        tacticalSymbol.SetType("Carrier");
 0174        break;
 175      }
 0176      case Configs.AgentType.MissileInterceptor: {
 0177        tacticalSymbol.SetType("Interceptor");
 0178        break;
 179      }
 0180      case Configs.AgentType.FixedWingThreat: {
 0181        tacticalSymbol.SetType("Fixed-Wing Threat");
 0182        break;
 183      }
 0184      case Configs.AgentType.RotaryWingThreat: {
 0185        tacticalSymbol.SetType("Rotary-Wing Threat");
 0186        break;
 187      }
 0188      default: {
 0189        Debug.LogError(
 190            $"Unknown agent type {agent.StaticConfig.AgentType} for agent {agent.gameObject.name}.");
 0191        tacticalSymbol.SetType("Unknown");
 0192        break;
 193      }
 194    }
 0195    tacticalSymbol.SetUniqueDesignator(agent.gameObject.name);
 0196    UpdateAgentSymbol(symbol, agent);
 0197    _symbols.Add(agent, symbol);
 0198  }
 199
 0200  private void DestroySymbol(IAgent agent) {
 0201    if (_symbols.TryGetValue(agent, out GameObject symbol)) {
 0202      Destroy(symbol);
 0203      _symbols.Remove(agent);
 0204    }
 0205  }
 206
 1207  private void DestroyAllSymbols() {
 3208    foreach (var symbol in _symbols.Values) {
 0209      Destroy(symbol);
 0210    }
 1211    _symbols.Clear();
 1212  }
 213
 1214  private void UpdateSymbols() {
 1215    if (SimManager.Instance != null) {
 0216      UpdateAgentSymbols(SimManager.Instance.Interceptors);
 0217      UpdateAgentSymbols(SimManager.Instance.Threats);
 0218    }
 1219  }
 220
 0221  private void UpdateAgentSymbols(IEnumerable<IAgent> agents) {
 0222    foreach (var agent in agents) {
 0223      if (_symbols.TryGetValue(agent, out GameObject symbol)) {
 0224        UpdateAgentSymbol(symbol, agent);
 0225      }
 0226    }
 0227  }
 228
 0229  private void UpdateAgentSymbol(GameObject symbol, IAgent agent) {
 230    // Division factor for real-world scaling, e.g., 1000 meters = 1 unit on the grid.
 231    const float scaleDivisionFactor = 1000f;
 232
 0233    if (_polarGridGraphic == null) {
 0234      Debug.LogError("TacticalPolarGridGraphic component is missing.");
 0235      return;
 236    }
 237
 238    // Update the symbol position.
 0239    Vector3 position = agent.Position;
 0240    float scaleFactor = _polarGridGraphic.CurrentScaleFactor;
 0241    Vector3 scaledPosition =
 242        new Vector3(position.z / scaleDivisionFactor, position.x / scaleDivisionFactor, 0f);
 0243    symbol.transform.localPosition = scaledPosition * scaleFactor;
 244
 245    // Update the symbol scale.
 0246    UpdateSymbolScale(symbol);
 247
 248    // Update the symbol rotation.
 0249    Vector3 forward = agent.Forward;
 0250    symbol.GetComponent<TacticalSymbol>().SetDirectionArrowRotation(
 251        -1 * Mathf.Atan2(forward.z, forward.x) * Mathf.Rad2Deg);
 252
 253    // Update the symbol's speed alternate text.
 0254    symbol.GetComponent<TacticalSymbol>().SetSpeedAlt(
 255        $"{Utilities.ConvertMpsToKnots(agent.Speed):F0} kts/" +
 256        $"{Utilities.ConvertMetersToFeet(agent.Position.y):F0} ft");
 0257  }
 258
 0259  private void UpdateSymbolScale(GameObject symbol) {
 260    // Calculate the inverse scale to maintain constant visual size.
 0261    float inverseScale = 2f / _radarUIGroupRectTransform.localScale.x;
 0262    symbol.transform.localScale = new Vector3(inverseScale, inverseScale, 1f);
 0263  }
 264
 265  // Adjusts the radar scale by the specified amount.
 0266  private void AdjustRadarScale(float amount) {
 0267    Vector3 newScale = _radarUIGroupRectTransform.localScale + new Vector3(amount, amount, 0f);
 268    // Prevent negative or too small scaling.
 0269    if (newScale.x < 0.01f || newScale.y < 0.01f) {
 0270      return;
 271    }
 0272    _radarUIGroupRectTransform.localScale = newScale;
 273
 274    // Update all existing symbols' scales.
 0275    foreach (var symbol in _symbols.Values) {
 0276      UpdateSymbolScale(symbol);
 0277    }
 0278    UpdateSymbolScale(_origin);
 0279  }
 280}