< Summary

Class:SimManager
Assembly:bamlab.micromissiles
File(s):/github/workspace/Assets/Scripts/SimManager.cs
Covered lines:75
Uncovered lines:317
Coverable lines:392
Total lines:614
Line coverage:19.1% (75 of 392)
Covered branches:0
Total branches:0
Covered methods:7
Total methods:47
Method coverage:14.8% (7 of 47)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
SimManager()0%110100%
GetElapsedSimulationTime()0%2100%
GetCostLaunchedInterceptors()0%2100%
GetCostDestroyedThreats()0%2100%
GetActiveInterceptors()0%2100%
GetActiveThreats()0%6200%
GetActiveAgents()0%12300%
GenerateSwarmTitle(...)0%2100%
GenerateInterceptorSwarmTitle(...)0%2100%
GenerateSubmunitionsSwarmTitle(...)0%2100%
GenerateThreatSwarmTitle(...)0%2100%
Awake()0%6200%
Start()0%6200%
SetTimeScale(...)0%2100%
StartSimulation()0%6200%
PauseSimulation()0%2100%
ResumeSimulation()0%2100%
IsSimulationPaused()0%2100%
InitializeSimulation()0%56700%
AddInterceptorSwarm(...)0%20400%
AddSubmunitionsSwarm(...)0%20400%
LookupSubmunitionSwarnIndexInInterceptorSwarm(...)0%6200%
AddThreatSwarm(...)0%20400%
GetAllAgentsCenter()0%12300%
GetSwarmCenter(...)0%30500%
GetInterceptorSwarms()0%2100%
GetSubmunitionsSwarms()0%2100%
GetThreatSwarms()0%2100%
AssignInterceptorsToThreats()0%2100%
DestroyInterceptorInSwarm(...)0%56700%
DestroySubmunitionInSwarm(...)0%12300%
DestroyThreatInSwarm(...)0%42600%
RegisterInterceptorHit(...)0%20400%
RegisterInterceptorMiss(...)0%20400%
RegisterThreatHit(...)0%2100%
RegisterThreatMiss(...)0%2100%
LoadAttackBehavior(...)0%2.062075%
CreateDummyAgent(...)0%330100%
CreateInterceptor(...)0%20400%
CreateThreat(...)0%33095.24%
CreateAgent(...)0%2.012085%
LoadNewConfig(...)0%6200%
RestartSimulation()0%1321100%
Update()0%30500%
FixedUpdate()0%20400%

File(s)

/github/workspace/Assets/Scripts/SimManager.cs

#LineLine coverage
 1using System.Collections;
 2using System.Collections.Generic;
 3using System.Linq;
 4using UnityEngine;
 5
 6/// <summary>
 7/// Manages the simulation by handling missiles, targets, and their assignments.
 8/// Implements the Singleton pattern to ensure only one instance exists.
 9/// </summary>
 10public class SimManager : MonoBehaviour {
 11  public SimulatorConfig simulatorConfig;
 12
 13  /// <summary>
 14  /// Singleton instance of SimManager.
 15  /// </summary>
 2516  public static SimManager Instance { get; set; }
 17
 18  /// <summary>
 19  /// Configuration settings for the simulation.
 20  /// </summary>
 21  [SerializeField]
 22  public SimulationConfig simulationConfig;
 23
 1024  public string defaultConfig = "1_salvo_1_hydra_7_drones.json";
 25
 1026  private List<Interceptor> _activeInterceptors = new List<Interceptor>();
 27
 1028  private List<Interceptor> _interceptorObjects = new List<Interceptor>();
 1029  private List<Threat> _threatObjects = new List<Threat>();
 30
 1031  private List<GameObject> _dummyAgentObjects = new List<GameObject>();
 1032  private Dictionary<(Vector3, Vector3), GameObject> _dummyAgentTable =
 33      new Dictionary<(Vector3, Vector3), GameObject>();
 34
 35  // Inclusive of ALL, including SUBMUNITIONS SWARMS
 36  // The bool indicates whether the agent is ACTIVE (true) or INACTIVE (false)
 1037  private List<List<(Agent, bool)>> _interceptorSwarms = new List<List<(Agent, bool)>>();
 1038  private List<List<(Agent, bool)>> _submunitionsSwarms = new List<List<(Agent, bool)>>();
 1039  private List<List<(Agent, bool)>> _threatSwarms = new List<List<(Agent, bool)>>();
 40
 1041  private Dictionary<Agent, List<(Agent, bool)>> _interceptorSwarmMap =
 42      new Dictionary<Agent, List<(Agent, bool)>>();
 43
 1044  private Dictionary<Agent, List<(Agent, bool)>> _submunitionsSwarmMap =
 45      new Dictionary<Agent, List<(Agent, bool)>>();
 1046  private Dictionary<Agent, List<(Agent, bool)>> _threatSwarmMap =
 47      new Dictionary<Agent, List<(Agent, bool)>>();
 48
 49  // Maps a submunition swarm to its corresponding interceptor swarm
 1050  private Dictionary<List<(Agent, bool)>, List<(Agent, bool)>> _submunitionInterceptorSwarmMap =
 51      new Dictionary<List<(Agent, bool)>, List<(Agent, bool)>>();
 52
 53  // Events so people can subscribe to changes in each of the swarm tables
 54  public delegate void SwarmEventHandler(List<List<(Agent, bool)>> swarm);
 55  public event SwarmEventHandler OnInterceptorSwarmChanged;
 56  public event SwarmEventHandler OnSubmunitionsSwarmChanged;
 57  public event SwarmEventHandler OnThreatSwarmChanged;
 58  //////////////////////////////////////////////////////////////////////
 59
 1060  private float _elapsedSimulationTime = 0f;
 1061  private bool _isSimulationPaused = false;
 62
 1063  private float _costLaunchedInterceptors = 0f;
 1064  private float _costDestroyedThreats = 0f;
 65
 66  public delegate void SimulationEventHandler();
 67  public event SimulationEventHandler OnSimulationEnded;
 68  public event SimulationEventHandler OnSimulationStarted;
 69
 70  public delegate void NewThreatEventHandler(Threat threat);
 71  public event NewThreatEventHandler OnNewThreat;
 72
 73  public delegate void NewInterceptorEventHandler(Interceptor interceptor);
 74  public event NewInterceptorEventHandler OnNewInterceptor;
 75
 76  /// <summary>
 77  /// Gets the elapsed simulation time.
 78  /// </summary>
 79  /// <returns>The elapsed time in seconds.</returns>
 080  public double GetElapsedSimulationTime() {
 081    return _elapsedSimulationTime;
 082  }
 83
 84  /// <summary>
 85  /// Gets the total cost of launched interceptors.
 86  /// </summary>
 87  /// <returns>The total cost of launched interceptors.</returns>
 088  public double GetCostLaunchedInterceptors() {
 089    return _costLaunchedInterceptors;
 090  }
 91
 92  /// <summary>
 93  /// Gets the total cost of destroyed threats.
 94  /// </summary>
 95  /// <returns>The total cost of destroyed threats.</returns>
 096  public double GetCostDestroyedThreats() {
 097    return _costDestroyedThreats;
 098  }
 99
 0100  public List<Interceptor> GetActiveInterceptors() {
 0101    return _activeInterceptors;
 0102  }
 103
 0104  public List<Threat> GetActiveThreats() {
 0105    return _threatObjects.Where(threat => !threat.IsHit()).ToList();
 0106  }
 107
 0108  public List<Agent> GetActiveAgents() {
 0109    return _activeInterceptors.ConvertAll(interceptor => interceptor as Agent)
 0110        .Concat(GetActiveThreats().ConvertAll(threat => threat as Agent))
 111        .ToList();
 0112  }
 113
 0114  public string GenerateSwarmTitle(List<(Agent, bool)> swarm, int index) {
 0115    string swarmTitle = swarm[0].Item1.name;
 0116    swarmTitle = swarmTitle.Split('_')[0];
 0117    swarmTitle += $"_{index}";
 0118    return swarmTitle;
 0119  }
 120
 0121  public string GenerateInterceptorSwarmTitle(List<(Agent, bool)> swarm) {
 0122    return GenerateSwarmTitle(swarm, _interceptorSwarms.IndexOf(swarm));
 0123  }
 124
 0125  public string GenerateSubmunitionsSwarmTitle(List<(Agent, bool)> swarm) {
 0126    return GenerateSwarmTitle(swarm, LookupSubmunitionSwarnIndexInInterceptorSwarm(swarm));
 0127  }
 128
 0129  public string GenerateThreatSwarmTitle(List<(Agent, bool)> swarm) {
 0130    return GenerateSwarmTitle(swarm, _threatSwarms.IndexOf(swarm));
 0131  }
 132
 0133  void Awake() {
 134    // Ensure only one instance of SimManager exists
 0135    if (Instance == null) {
 0136      Instance = this;
 0137      DontDestroyOnLoad(gameObject);
 0138    } else {
 0139      Destroy(gameObject);
 0140    }
 0141    simulationConfig = ConfigLoader.LoadSimulationConfig("1_salvo_1_hydra_7_drones.json");
 0142    simulatorConfig = ConfigLoader.LoadSimulatorConfig();
 0143    Debug.Log(simulationConfig);
 0144  }
 145
 0146  void Start() {
 147    // Slow down time by simulationConfig.timeScale
 0148    if (Instance == this) {
 0149      _isSimulationPaused = false;
 0150      StartSimulation();
 0151      ResumeSimulation();
 0152    }
 0153  }
 154
 0155  public void SetTimeScale(float timeScale) {
 0156    Time.timeScale = timeScale;
 0157    Time.maximumDeltaTime = Time.fixedDeltaTime * 3;
 158
 159    // Time.fixedDeltaTime is set in the simulator.json
 0160  }
 161
 0162  public void StartSimulation() {
 0163    OnSimulationStarted?.Invoke();
 0164    InitializeSimulation();
 0165  }
 166
 0167  public void PauseSimulation() {
 0168    SetTimeScale(0);
 0169    Time.fixedDeltaTime = 0;
 0170    _isSimulationPaused = true;
 0171  }
 172
 0173  public void ResumeSimulation() {
 0174    Time.fixedDeltaTime = (float)(1.0f / simulatorConfig.physicsUpdateRate);
 0175    SetTimeScale(simulationConfig.timeScale);
 0176    _isSimulationPaused = false;
 0177  }
 178
 0179  public bool IsSimulationPaused() {
 0180    return _isSimulationPaused;
 0181  }
 182
 0183  private void InitializeSimulation() {
 0184    if (!IsSimulationPaused()) {
 185      // If it wasn't paused, we need to update the time scales NOW
 0186      SetTimeScale(simulationConfig.timeScale);
 0187      Time.fixedDeltaTime = (float)(1.0f / simulatorConfig.physicsUpdateRate);
 188      // If the simulation WAS paused, then ResumeSimulation will handle
 189      // updating the time scale and fixed delta time from the newly loaded config files
 0190    }
 191
 192    // Invoke the simulation started event to let listeners
 193    // know to invoke their own handler behavior
 0194    OnSimulationStarted?.Invoke();
 0195    List<Interceptor> missiles = new List<Interceptor>();
 196    // Create missiles based on config
 0197    foreach (var swarmConfig in simulationConfig.interceptor_swarm_configs) {
 0198      List<Agent> swarm = new List<Agent>();
 0199      for (int i = 0; i < swarmConfig.num_agents; i++) {
 0200        Interceptor interceptor = CreateInterceptor(swarmConfig.dynamic_agent_config);
 0201        swarm.Add(interceptor);
 0202      }
 0203      AddInterceptorSwarm(swarm);
 0204    }
 0205    IADS.Instance.RequestThreatAssignment(_interceptorObjects);
 206
 0207    List<Agent> targets = new List<Agent>();
 208    // Create targets based on config
 0209    foreach (var swarmConfig in simulationConfig.threat_swarm_configs) {
 0210      List<Agent> swarm = new List<Agent>();
 0211      for (int i = 0; i < swarmConfig.num_agents; i++) {
 0212        Threat threat = CreateThreat(swarmConfig.dynamic_agent_config);
 0213        swarm.Add(threat);
 0214      }
 0215      AddThreatSwarm(swarm);
 0216    }
 0217  }
 218
 0219  public void AddInterceptorSwarm(List<Agent> swarm) {
 0220    List<(Agent, bool)> swarmTuple = swarm.ConvertAll(agent => (agent, true));
 0221    _interceptorSwarms.Add(swarmTuple);
 0222    foreach (var interceptor in swarm) {
 0223      _interceptorSwarmMap[interceptor] = swarmTuple;
 0224    }
 0225    OnInterceptorSwarmChanged?.Invoke(_interceptorSwarms);
 0226  }
 227
 0228  public void AddSubmunitionsSwarm(List<Agent> swarm) {
 0229    List<(Agent, bool)> swarmTuple = swarm.ConvertAll(agent => (agent, true));
 0230    _submunitionsSwarms.Add(swarmTuple);
 0231    foreach (var submunition in swarm) {
 0232      _submunitionsSwarmMap[submunition] = swarmTuple;
 0233    }
 0234    AddInterceptorSwarm(swarm);
 0235    _submunitionInterceptorSwarmMap[swarmTuple] = _interceptorSwarms[_interceptorSwarms.Count - 1];
 0236    OnSubmunitionsSwarmChanged?.Invoke(_submunitionsSwarms);
 0237  }
 238
 0239  public int LookupSubmunitionSwarnIndexInInterceptorSwarm(List<(Agent, bool)> swarm) {
 0240    if (_submunitionInterceptorSwarmMap.TryGetValue(swarm, out var interceptorSwarm)) {
 0241      return _interceptorSwarms.IndexOf(interceptorSwarm);
 242    }
 0243    return -1;  // Return -1 if the swarm is not found
 0244  }
 245
 0246  public void AddThreatSwarm(List<Agent> swarm) {
 0247    List<(Agent, bool)> swarmTuple = swarm.ConvertAll(agent => (agent, true));
 0248    _threatSwarms.Add(swarmTuple);
 0249    foreach (var threat in swarm) {
 0250      _threatSwarmMap[threat] = swarmTuple;
 0251    }
 0252    OnThreatSwarmChanged?.Invoke(_threatSwarms);
 0253  }
 254
 0255  public Vector3 GetAllAgentsCenter() {
 0256    List<Agent> allAgents = _interceptorObjects.ConvertAll(interceptor => interceptor as Agent)
 0257                                .Concat(_threatObjects.ConvertAll(threat => threat as Agent))
 258                                .ToList();
 0259    return GetSwarmCenter(allAgents);
 0260  }
 261
 0262  public Vector3 GetSwarmCenter(List<Agent> swarm) {
 0263    if (swarm.Count == 0) {
 0264      return Vector3.zero;
 265    }
 266
 0267    Vector3 sum = Vector3.zero;
 0268    int count = 0;
 0269    int swarmCount = swarm.Count;
 270
 0271    for (int i = 0; i < swarmCount; i++) {
 0272      Agent agent = swarm[i];
 0273      if (!agent.IsHit()) {
 0274        sum += agent.transform.position;
 0275        count++;
 0276      }
 0277    }
 278
 0279    return count > 0 ? sum / count : Vector3.zero;
 0280  }
 281
 0282  public List<List<(Agent, bool)>> GetInterceptorSwarms() {
 0283    return _interceptorSwarms;
 0284  }
 285
 0286  public List<List<(Agent, bool)>> GetSubmunitionsSwarms() {
 0287    return _submunitionsSwarms;
 0288  }
 289
 0290  public List<List<(Agent, bool)>> GetThreatSwarms() {
 0291    return _threatSwarms;
 0292  }
 293
 0294  public void AssignInterceptorsToThreats() {
 0295    IADS.Instance.AssignInterceptorsToThreats(_interceptorObjects);
 0296  }
 297
 0298  public void DestroyInterceptorInSwarm(Interceptor interceptor) {
 0299    var swarm = _interceptorSwarmMap[interceptor];
 0300    int index = swarm.FindIndex(tuple => tuple.Item1 == interceptor);
 0301    if (index != -1) {
 0302      swarm[index] = (swarm[index].Item1, false);
 0303      OnInterceptorSwarmChanged?.Invoke(_interceptorSwarms);
 0304    } else {
 0305      Debug.LogError("Interceptor not found in swarm");
 0306    }
 0307    if (swarm.All(tuple => !tuple.Item2)) {
 308      // Need to give the CameraController a way to update
 309      // to the next swarm if it exists
 0310      if (CameraController.Instance.cameraMode == CameraMode.FOLLOW_INTERCEPTOR_SWARM) {
 0311        CameraController.Instance.FollowNextInterceptorSwarm();
 0312      }
 0313    }
 314
 315    // If this also happens to be a submunition, destroy it in the submunition swarm
 0316    if (_submunitionsSwarmMap.ContainsKey(interceptor)) {
 0317      DestroySubmunitionInSwarm(interceptor);
 0318    }
 0319  }
 320
 0321  public void DestroySubmunitionInSwarm(Interceptor submunition) {
 0322    var swarm = _submunitionsSwarmMap[submunition];
 0323    int index = swarm.FindIndex(tuple => tuple.Item1 == submunition);
 0324    if (index != -1) {
 0325      swarm[index] = (swarm[index].Item1, false);
 0326      OnSubmunitionsSwarmChanged?.Invoke(_submunitionsSwarms);
 0327    }
 0328  }
 329
 0330  public void DestroyThreatInSwarm(Threat threat) {
 0331    var swarm = _threatSwarmMap[threat];
 0332    int index = swarm.FindIndex(tuple => tuple.Item1 == threat);
 0333    if (index != -1) {
 0334      swarm[index] = (swarm[index].Item1, false);
 0335      OnThreatSwarmChanged?.Invoke(_threatSwarms);
 0336    }
 0337    if (swarm.All(tuple => !tuple.Item2)) {
 0338      _threatSwarms.Remove(swarm);
 0339      if (CameraController.Instance.cameraMode == CameraMode.FOLLOW_THREAT_SWARM) {
 0340        CameraController.Instance.FollowNextThreatSwarm();
 0341      }
 0342    }
 0343  }
 344
 0345  public void RegisterInterceptorHit(Interceptor interceptor, Threat threat) {
 0346    _costDestroyedThreats += threat.staticAgentConfig.unitCost;
 0347    if (interceptor is Interceptor missileComponent) {
 0348      _activeInterceptors.Remove(missileComponent);
 0349    }
 0350    DestroyInterceptorInSwarm(interceptor);
 0351    DestroyThreatInSwarm(threat);
 0352  }
 353
 0354  public void RegisterInterceptorMiss(Interceptor interceptor, Threat threat) {
 0355    if (interceptor is Interceptor missileComponent) {
 0356      _activeInterceptors.Remove(missileComponent);
 0357    }
 0358    DestroyInterceptorInSwarm(interceptor);
 0359  }
 360
 0361  public void RegisterThreatHit(Threat threat) {
 0362    DestroyThreatInSwarm(threat);
 0363  }
 364
 0365  public void RegisterThreatMiss(Threat threat) {
 0366    DestroyThreatInSwarm(threat);
 0367  }
 368
 15369  private AttackBehavior LoadAttackBehavior(DynamicAgentConfig config) {
 15370    string threatBehaviorFile = config.attack_behavior;
 15371    AttackBehavior attackBehavior = AttackBehavior.FromJson(threatBehaviorFile);
 15372    switch (attackBehavior.attackBehaviorType) {
 373      case AttackBehavior.AttackBehaviorType.DIRECT_ATTACK:
 15374        return DirectAttackBehavior.FromJson(threatBehaviorFile);
 375      default:
 0376        Debug.LogError($"Attack behavior type '{attackBehavior.attackBehaviorType}' not found.");
 0377        return null;
 378    }
 15379  }
 380
 15381  public Agent CreateDummyAgent(Vector3 position, Vector3 velocity) {
 23382    if (_dummyAgentTable.ContainsKey((position, velocity))) {
 8383      return _dummyAgentTable[(position, velocity)].GetComponent<Agent>();
 384    }
 7385    GameObject dummyAgentPrefab = Resources.Load<GameObject>($"Prefabs/DummyAgent");
 7386    GameObject dummyAgentObject = Instantiate(dummyAgentPrefab, position, Quaternion.identity);
 14387    if (!dummyAgentObject.TryGetComponent<Agent>(out _)) {
 7388      dummyAgentObject.AddComponent<DummyAgent>();
 7389    }
 7390    Rigidbody dummyRigidbody = dummyAgentObject.GetComponent<Rigidbody>();
 7391    dummyRigidbody.linearVelocity = velocity;
 7392    _dummyAgentObjects.Add(dummyAgentObject);
 7393    _dummyAgentTable[(position, velocity)] = dummyAgentObject;
 7394    return dummyAgentObject.GetComponent<Agent>();
 15395  }
 396
 397  /// <summary>
 398  /// Creates a interceptor based on the provided configuration.
 399  /// </summary>
 400  /// <param name="config">Configuration settings for the interceptor.</param>
 401  /// <returns>The created Interceptor instance, or null if creation failed.</returns>
 0402  public Interceptor CreateInterceptor(DynamicAgentConfig config) {
 0403    string interceptorModelFile = config.agent_model;
 0404    interceptorModelFile = "Interceptors/" + interceptorModelFile;
 0405    StaticAgentConfig interceptorStaticAgentConfig =
 406        ConfigLoader.LoadStaticAgentConfig(interceptorModelFile);
 0407    string agentClass = interceptorStaticAgentConfig.agentClass;
 408    // The interceptor class corresponds to the Prefab that must
 409    // exist in the Resources/Prefabs folder
 0410    GameObject interceptorObject = CreateAgent(config, agentClass);
 411
 0412    if (interceptorObject == null)
 0413      return null;
 414
 415    // Interceptor-specific logic
 0416    switch (config.dynamic_config.sensor_config.type) {
 417      case SensorType.IDEAL:
 0418        interceptorObject.AddComponent<IdealSensor>();
 0419        break;
 420      default:
 0421        Debug.LogError($"Sensor type '{config.dynamic_config.sensor_config.type}' not found.");
 0422        break;
 423    }
 424
 0425    Interceptor interceptor = interceptorObject.GetComponent<Interceptor>();
 0426    _interceptorObjects.Add(interceptor);
 0427    _activeInterceptors.Add(interceptor);
 428
 429    // Set the static agent config
 0430    interceptor.SetStaticAgentConfig(interceptorStaticAgentConfig);
 431
 432    // Subscribe events
 0433    interceptor.OnInterceptHit += RegisterInterceptorHit;
 0434    interceptor.OnInterceptMiss += RegisterInterceptorMiss;
 435
 436    // Assign a unique and simple ID
 0437    int interceptorId = _interceptorObjects.Count;
 0438    interceptorObject.name = $"{interceptorStaticAgentConfig.name}_Interceptor_{interceptorId}";
 439
 440    // Add the interceptor's unit cost to the total cost
 0441    _costLaunchedInterceptors += interceptorStaticAgentConfig.unitCost;
 442
 443    // Let listeners know a new interceptor has been created
 0444    OnNewInterceptor?.Invoke(interceptor);
 445
 0446    return interceptor;
 0447  }
 448
 449  /// <summary>
 450  /// Creates a threat based on the provided configuration.
 451  /// </summary>
 452  /// <param name="config">Configuration settings for the threat.</param>
 453  /// <returns>The created Threat instance, or null if creation failed.</returns>
 15454  private Threat CreateThreat(DynamicAgentConfig config) {
 15455    string threatModelFile = config.agent_model;
 15456    threatModelFile = "Threats/" + threatModelFile;
 15457    StaticAgentConfig threatStaticAgentConfig = ConfigLoader.LoadStaticAgentConfig(threatModelFile);
 15458    string agentClass = threatStaticAgentConfig.agentClass;
 459    // The threat class corresponds to the Prefab that must
 460    // exist in the Resources/Prefabs folder
 15461    GameObject threatObject = CreateAgent(config, agentClass);
 462
 15463    if (threatObject == null)
 0464      return null;
 465
 15466    Threat threat = threatObject.GetComponent<Threat>();
 15467    _threatObjects.Add(threat);
 468
 469    // Set the static agent config
 15470    threat.SetStaticAgentConfig(threatStaticAgentConfig);
 471
 472    // Set the attack behavior
 15473    AttackBehavior attackBehavior = LoadAttackBehavior(config);
 15474    threat.SetAttackBehavior(attackBehavior);
 475
 476    // Subscribe events
 15477    threat.OnThreatHit += RegisterThreatHit;
 15478    threat.OnThreatMiss += RegisterThreatMiss;
 479
 480    // Assign a unique and simple ID
 15481    int threatId = _threatObjects.Count;
 15482    threatObject.name = $"{threatStaticAgentConfig.name}_Threat_{threatId}";
 483
 484    // Threats always start in midcourse
 15485    threat.SetFlightPhase(Agent.FlightPhase.MIDCOURSE);
 486
 487    // Let listeners know a new threat has been created
 15488    OnNewThreat?.Invoke(threat);
 489
 15490    return threatObject.GetComponent<Threat>();
 15491  }
 492
 493  /// <summary>
 494  /// Creates an agent (interceptor or threat) based on the provided configuration and prefab name.
 495  /// </summary>
 496  /// <param name="config">Configuration settings for the agent.</param>
 497  /// <param name="prefabName">Name of the prefab to instantiate.</param>
 498  /// <returns>The created GameObject instance, or null if creation failed.</returns>
 15499  public GameObject CreateAgent(DynamicAgentConfig config, string prefabName) {
 15500    GameObject prefab = Resources.Load<GameObject>($"Prefabs/{prefabName}");
 15501    if (prefab == null) {
 0502      Debug.LogError($"Prefab '{prefabName}' not found in Resources/Prefabs folder.");
 0503      return null;
 504    }
 505
 15506    Vector3 noiseOffset = Utilities.GenerateRandomNoise(config.standard_deviation.position);
 15507    Vector3 noisyPosition = config.initial_state.position + noiseOffset;
 508
 15509    GameObject agentObject = Instantiate(prefab, noisyPosition, Quaternion.identity);
 510
 15511    Rigidbody agentRigidbody = agentObject.GetComponent<Rigidbody>();
 15512    Vector3 velocityNoise = Utilities.GenerateRandomNoise(config.standard_deviation.velocity);
 15513    Vector3 noisyVelocity = config.initial_state.velocity + velocityNoise;
 15514    agentRigidbody.linearVelocity = noisyVelocity;
 15515    agentObject.GetComponent<Agent>().SetInitialVelocity(noisyVelocity);
 516    // Set rotation to face the initial velocity with noise
 15517    Vector3 velocityDirection = noisyVelocity.normalized;
 15518    Quaternion targetRotation = Quaternion.LookRotation(velocityDirection, Vector3.up);
 15519    agentObject.transform.rotation = targetRotation;
 520
 15521    agentObject.GetComponent<Agent>().SetDynamicAgentConfig(config);
 522
 15523    return agentObject;
 15524  }
 525
 0526  public void LoadNewConfig(string configFileName) {
 0527    this.simulationConfig = ConfigLoader.LoadSimulationConfig(configFileName);
 528    // Reload the simulator config
 0529    this.simulatorConfig = ConfigLoader.LoadSimulatorConfig();
 0530    if (simulationConfig != null) {
 0531      Debug.Log($"Loaded new configuration: {configFileName}");
 0532      RestartSimulation();
 0533    } else {
 0534      Debug.LogError($"Failed to load configuration: {configFileName}");
 0535    }
 0536  }
 537
 0538  public void RestartSimulation() {
 0539    OnSimulationEnded?.Invoke();
 0540    Debug.Log("Simulation ended");
 0541    UIManager.Instance.LogActionMessage("[SIM] Simulation restarted");
 542    // Reset simulation time
 0543    _elapsedSimulationTime = 0f;
 0544    _isSimulationPaused = IsSimulationPaused();
 0545    _costLaunchedInterceptors = 0f;
 0546    _costDestroyedThreats = 0f;
 547
 548    // Clear existing interceptors and threats
 0549    foreach (var interceptor in _interceptorObjects) {
 0550      if (interceptor != null) {
 0551        Destroy(interceptor.gameObject);
 0552      }
 0553    }
 554
 0555    foreach (var threat in _threatObjects) {
 0556      if (threat != null) {
 0557        Destroy(threat.gameObject);
 0558      }
 0559    }
 560
 0561    foreach (var dummyAgent in _dummyAgentObjects) {
 0562      if (dummyAgent != null) {
 0563        Destroy(dummyAgent);
 0564      }
 0565    }
 566
 0567    _interceptorObjects.Clear();
 0568    _activeInterceptors.Clear();
 0569    _threatObjects.Clear();
 0570    _dummyAgentObjects.Clear();
 0571    _dummyAgentTable.Clear();
 0572    _interceptorSwarms.Clear();
 0573    _submunitionsSwarms.Clear();
 0574    _threatSwarms.Clear();
 0575    OnInterceptorSwarmChanged?.Invoke(_interceptorSwarms);
 0576    OnSubmunitionsSwarmChanged?.Invoke(_submunitionsSwarms);
 0577    OnThreatSwarmChanged?.Invoke(_threatSwarms);
 0578    StartSimulation();
 0579  }
 580
 0581  void Update() {
 582    // Check if all missiles have terminated
 0583    bool allInterceptorsTerminated = true;
 0584    foreach (var interceptor in _interceptorObjects) {
 0585      if (interceptor != null && interceptor.GetFlightPhase() != Agent.FlightPhase.TERMINATED) {
 0586        allInterceptorsTerminated = false;
 0587        break;
 588      }
 0589    }
 590    // If all missiles have terminated, restart the simulation
 0591    if (allInterceptorsTerminated) {
 0592      RestartSimulation();
 0593    }
 0594  }
 595
 0596  void FixedUpdate() {
 0597    if (!_isSimulationPaused && _elapsedSimulationTime < simulationConfig.endTime) {
 0598      _elapsedSimulationTime += Time.deltaTime;
 0599    } else if (_elapsedSimulationTime >= simulationConfig.endTime) {
 0600      RestartSimulation();
 0601      Debug.Log("Simulation completed.");
 0602    }
 0603  }
 604}
 605
 606[System.Serializable]
 607public class SimulatorConfig {
 608  public bool enableTelemetryLogging;
 609  public bool enableEventLogging;
 610  public bool enableMissileTrailEffect;
 611  public bool enableExplosionEffect;
 612  public int physicsUpdateRate;
 613  public bool persistentFlightTrails;
 614}

Methods/Properties

Instance()
Instance(SimManager)
SimManager()
GetElapsedSimulationTime()
GetCostLaunchedInterceptors()
GetCostDestroyedThreats()
GetActiveInterceptors()
GetActiveThreats()
GetActiveAgents()
GenerateSwarmTitle(System.Collections.Generic.List[ValueTuple`2], System.Int32)
GenerateInterceptorSwarmTitle(System.Collections.Generic.List[ValueTuple`2])
GenerateSubmunitionsSwarmTitle(System.Collections.Generic.List[ValueTuple`2])
GenerateThreatSwarmTitle(System.Collections.Generic.List[ValueTuple`2])
Awake()
Start()
SetTimeScale(System.Single)
StartSimulation()
PauseSimulation()
ResumeSimulation()
IsSimulationPaused()
InitializeSimulation()
AddInterceptorSwarm(System.Collections.Generic.List[Agent])
AddSubmunitionsSwarm(System.Collections.Generic.List[Agent])
LookupSubmunitionSwarnIndexInInterceptorSwarm(System.Collections.Generic.List[ValueTuple`2])
AddThreatSwarm(System.Collections.Generic.List[Agent])
GetAllAgentsCenter()
GetSwarmCenter(System.Collections.Generic.List[Agent])
GetInterceptorSwarms()
GetSubmunitionsSwarms()
GetThreatSwarms()
AssignInterceptorsToThreats()
DestroyInterceptorInSwarm(Interceptor)
DestroySubmunitionInSwarm(Interceptor)
DestroyThreatInSwarm(Threat)
RegisterInterceptorHit(Interceptor, Threat)
RegisterInterceptorMiss(Interceptor, Threat)
RegisterThreatHit(Threat)
RegisterThreatMiss(Threat)
LoadAttackBehavior(DynamicAgentConfig)
CreateDummyAgent(UnityEngine.Vector3, UnityEngine.Vector3)
CreateInterceptor(DynamicAgentConfig)
CreateThreat(DynamicAgentConfig)
CreateAgent(DynamicAgentConfig, System.String)
LoadNewConfig(System.String)
RestartSimulation()
Update()
FixedUpdate()