< Summary

Class:TacticalPanel
Assembly:bamlab.micromissiles
File(s):/github/workspace/Assets/Scripts/UI/TacticalPanel.cs
Covered lines:0
Uncovered lines:167
Coverable lines:167
Total lines:266
Line coverage:0% (0 of 167)
Covered branches:0
Total branches:0
Covered methods:0
Total methods:28
Method coverage:0% (0 of 28)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
TacticalPanel()0%2100%
Pan(...)0%2100%
PanWithKeyboard(...)0%2100%
ZoomIn(...)0%2100%
ZoomOut(...)0%2100%
CycleRangeUp()0%2100%
CycleRangeDown()0%2100%
Awake()0%12300%
Start()0%2100%
OnDestroy()0%6200%
OnEnable()0%6200%
OnDisable()0%6200%
RegisterSimulationEnded()0%2100%
RegisterNewAgent(...)0%2100%
SymbolsManager()0%12300%
InitializeOrigin()0%2100%
InitializeAgents(...)0%12300%
CreateSymbolPrefab()0%2100%
CreateSymbol(...)0%90900%
DestroySymbol(...)0%6200%
DestroyAllSymbols()0%6200%
UpdateSymbols()0%6200%
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")]
 017  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.
 027  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
 033  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
 062  private void Awake() {
 063    if (Instance != null && Instance != this) {
 064      Destroy(gameObject);
 065      return;
 66    }
 067    Instance = this;
 68
 069    _radarUIGroupRectTransform = _radarUIGroup.GetComponent<RectTransform>();
 070    _polarGridGraphic = _radarUIGroup.GetComponent<TacticalPolarGridGraphic>();
 071  }
 72
 073  private void Start() {
 074    _radarUIGroupRectTransform.localScale = Vector3.one;
 075    _radarUIGroupRectTransform.localPosition = Vector3.zero;
 76
 077    SimManager.Instance.OnSimulationEnded += RegisterSimulationEnded;
 078    SimManager.Instance.OnNewInterceptor += RegisterNewAgent;
 079    SimManager.Instance.OnNewThreat += RegisterNewAgent;
 80
 081    InitializeOrigin();
 082  }
 83
 084  private void OnDestroy() {
 085    if (_symbolsCoroutine != null) {
 086      StopCoroutine(_symbolsCoroutine);
 087      _symbolsCoroutine = null;
 088    }
 089  }
 90
 091  private void OnEnable() {
 092    if (SimManager.Instance != null) {
 093      InitializeAgents(SimManager.Instance.Interceptors);
 094      InitializeAgents(SimManager.Instance.Threats);
 095    }
 096    _symbolsCoroutine = StartCoroutine(SymbolsManager(_symbolsUpdatePeriod));
 097  }
 98
 099  private void OnDisable() {
 0100    if (_symbolsCoroutine != null) {
 0101      StopCoroutine(_symbolsCoroutine);
 0102      _symbolsCoroutine = null;
 0103    }
 0104    DestroyAllSymbols();
 0105  }
 106
 0107  private void RegisterSimulationEnded() {
 0108    DestroyAllSymbols();
 0109  }
 110
 0111  public void RegisterNewAgent(IAgent agent) {
 0112    agent.OnTerminated += DestroySymbol;
 0113    CreateSymbol(agent);
 0114  }
 115
 0116  private IEnumerator SymbolsManager(float period) {
 0117    while (true) {
 0118      UpdateSymbols();
 0119      yield return new WaitForSeconds(period);
 0120    }
 121  }
 122
 0123  private void InitializeOrigin() {
 0124    GameObject origin = CreateSymbolPrefab();
 0125    origin.GetComponent<TacticalSymbol>().SetSprite("friendly_carrier_present");
 0126    origin.GetComponent<TacticalSymbol>().DisableDirectionArrow();
 0127    _origin = origin;
 0128  }
 129
 0130  private void InitializeAgents(IEnumerable<IAgent> agents) {
 0131    foreach (var agent in agents) {
 0132      agent.OnTerminated += DestroySymbol;
 0133      CreateSymbol(agent);
 0134    }
 0135  }
 136
 0137  private GameObject CreateSymbolPrefab() {
 0138    return Instantiate(Resources.Load<GameObject>("Prefabs/Symbols/Symbol"),
 139                       _radarUIGroupRectTransform);
 0140  }
 141
 0142  private void CreateSymbol(IAgent agent) {
 0143    GameObject symbol = CreateSymbolPrefab();
 0144    TacticalSymbol tacticalSymbol = symbol.GetComponent<TacticalSymbol>();
 145
 146    // Set common properties.
 0147    tacticalSymbol.SetSprite(agent.StaticConfig.VisualizationConfig?.SymbolPresent);
 0148    tacticalSymbol.SetDirectionArrowRotation(Mathf.Atan2(agent.Velocity.z, agent.Velocity.x) *
 149                                             Mathf.Rad2Deg);
 150
 151    // Set type-specific properties.
 0152    switch (agent.StaticConfig.AgentType) {
 153      case Configs.AgentType.Vessel:
 0154      case Configs.AgentType.ShoreBattery: {
 0155        tacticalSymbol.SetType("Launcher");
 0156        break;
 157      }
 0158      case Configs.AgentType.CarrierInterceptor: {
 0159        tacticalSymbol.SetType("Carrier");
 0160        break;
 161      }
 0162      case Configs.AgentType.MissileInterceptor: {
 0163        tacticalSymbol.SetType("Interceptor");
 0164        break;
 165      }
 0166      case Configs.AgentType.FixedWingThreat: {
 0167        tacticalSymbol.SetType("Fixed-Wing Threat");
 0168        break;
 169      }
 0170      case Configs.AgentType.RotaryWingThreat: {
 0171        tacticalSymbol.SetType("Rotary-Wing Threat");
 0172        break;
 173      }
 0174      default: {
 0175        Debug.LogError(
 176            $"Unknown agent type {agent.StaticConfig.AgentType} for agent {agent.gameObject.name}.");
 0177        tacticalSymbol.SetType("Unknown");
 0178        break;
 179      }
 180    }
 0181    tacticalSymbol.SetUniqueDesignator(agent.gameObject.name);
 0182    UpdateAgentSymbol(symbol, agent);
 0183    _symbols.Add(agent, symbol);
 0184  }
 185
 0186  private void DestroySymbol(IAgent agent) {
 0187    if (_symbols.TryGetValue(agent, out GameObject symbol)) {
 0188      Destroy(symbol);
 0189      _symbols.Remove(agent);
 0190    }
 0191  }
 192
 0193  private void DestroyAllSymbols() {
 0194    foreach (var symbol in _symbols.Values) {
 0195      Destroy(symbol);
 0196    }
 0197    _symbols.Clear();
 0198  }
 199
 0200  private void UpdateSymbols() {
 0201    if (SimManager.Instance != null) {
 0202      UpdateAgentSymbols(SimManager.Instance.Interceptors);
 0203      UpdateAgentSymbols(SimManager.Instance.Threats);
 0204    }
 0205  }
 206
 0207  private void UpdateAgentSymbols(IEnumerable<IAgent> agents) {
 0208    foreach (var agent in agents) {
 0209      if (_symbols.TryGetValue(agent, out GameObject symbol)) {
 0210        UpdateAgentSymbol(symbol, agent);
 0211      }
 0212    }
 0213  }
 214
 0215  private void UpdateAgentSymbol(GameObject symbol, IAgent agent) {
 216    // Division factor for real-world scaling, e.g., 1000 meters = 1 unit on the grid.
 217    const float scaleDivisionFactor = 1000f;
 218
 0219    if (_polarGridGraphic == null) {
 0220      Debug.LogError("TacticalPolarGridGraphic component is missing.");
 0221      return;
 222    }
 223
 224    // Update the symbol position.
 0225    Vector3 position = agent.Position;
 0226    float scaleFactor = _polarGridGraphic.CurrentScaleFactor;
 0227    Vector3 scaledPosition =
 228        new Vector3(position.z / scaleDivisionFactor, position.x / scaleDivisionFactor, 0f);
 0229    symbol.transform.localPosition = scaledPosition * scaleFactor;
 230
 231    // Update the symbol scale.
 0232    UpdateSymbolScale(symbol);
 233
 234    // Update the symbol rotation.
 0235    Vector3 forward = agent.Forward;
 0236    symbol.GetComponent<TacticalSymbol>().SetDirectionArrowRotation(
 237        -1 * Mathf.Atan2(forward.z, forward.x) * Mathf.Rad2Deg);
 238
 239    // Update the symbol's speed alternate text.
 0240    symbol.GetComponent<TacticalSymbol>().SetSpeedAlt(
 241        $"{Utilities.ConvertMpsToKnots(agent.Speed):F0} kts/" +
 242        $"{Utilities.ConvertMetersToFeet(agent.Position.y):F0} ft");
 0243  }
 244
 0245  private void UpdateSymbolScale(GameObject symbol) {
 246    // Calculate the inverse scale to maintain constant visual size.
 0247    float inverseScale = 2f / _radarUIGroupRectTransform.localScale.x;
 0248    symbol.transform.localScale = new Vector3(inverseScale, inverseScale, 1f);
 0249  }
 250
 251  // Adjusts the radar scale by the specified amount.
 0252  private void AdjustRadarScale(float amount) {
 0253    Vector3 newScale = _radarUIGroupRectTransform.localScale + new Vector3(amount, amount, 0f);
 254    // Prevent negative or too small scaling.
 0255    if (newScale.x < 0.01f || newScale.y < 0.01f) {
 0256      return;
 257    }
 0258    _radarUIGroupRectTransform.localScale = newScale;
 259
 260    // Update all existing symbols' scales.
 0261    foreach (var symbol in _symbols.Values) {
 0262      UpdateSymbolScale(symbol);
 0263    }
 0264    UpdateSymbolScale(_origin);
 0265  }
 266}