< Summary

Class:SimManager
Assembly:bamlab.micromissiles
File(s):/github/workspace/Assets/Scripts/SimManager.cs
Covered lines:193
Uncovered lines:199
Coverable lines:392
Total lines:614
Line coverage:49.2% (193 of 392)
Covered branches:0
Total branches:0
Covered methods:22
Total methods:47
Method coverage:46.8% (22 of 47)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
SimManager()0%110100%
GetElapsedSimulationTime()0%110100%
GetCostLaunchedInterceptors()0%110100%
GetCostDestroyedThreats()0%110100%
GetActiveInterceptors()0%2100%
GetActiveThreats()0%6200%
GetActiveAgents()0%12300%
GenerateSwarmTitle(...)0%2100%
GenerateInterceptorSwarmTitle(...)0%2100%
GenerateSubmunitionsSwarmTitle(...)0%2100%
GenerateThreatSwarmTitle(...)0%2100%
Awake()0%2.052076.92%
Start()0%220100%
SetTimeScale(...)0%110100%
StartSimulation()0%220100%
PauseSimulation()0%2100%
ResumeSimulation()0%110100%
IsSimulationPaused()0%110100%
InitializeSimulation()0%770100%
AddInterceptorSwarm(...)0%440100%
AddSubmunitionsSwarm(...)0%20400%
LookupSubmunitionSwarnIndexInInterceptorSwarm(...)0%6200%
AddThreatSwarm(...)0%440100%
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%4.034088%
CreateThreat(...)0%33095.24%
CreateAgent(...)0%2.012085%
LoadNewConfig(...)0%6200%
RestartSimulation()0%1321100%
Update()0%5.335076.47%
FixedUpdate()0%5.54054.55%

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>
 52016  public static SimManager Instance { get; set; }
 17
 18  /// <summary>
 19  /// Configuration settings for the simulation.
 20  /// </summary>
 21  [SerializeField]
 22  public SimulationConfig simulationConfig;
 23
 224  public string defaultConfig = "1_salvo_1_hydra_7_drones.json";
 25
 226  private List<Interceptor> _activeInterceptors = new List<Interceptor>();
 27
 228  private List<Interceptor> _interceptorObjects = new List<Interceptor>();
 229  private List<Threat> _threatObjects = new List<Threat>();
 30
 231  private List<GameObject> _dummyAgentObjects = new List<GameObject>();
 232  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)
 237  private List<List<(Agent, bool)>> _interceptorSwarms = new List<List<(Agent, bool)>>();
 238  private List<List<(Agent, bool)>> _submunitionsSwarms = new List<List<(Agent, bool)>>();
 239  private List<List<(Agent, bool)>> _threatSwarms = new List<List<(Agent, bool)>>();
 40
 241  private Dictionary<Agent, List<(Agent, bool)>> _interceptorSwarmMap =
 42      new Dictionary<Agent, List<(Agent, bool)>>();
 43
 244  private Dictionary<Agent, List<(Agent, bool)>> _submunitionsSwarmMap =
 45      new Dictionary<Agent, List<(Agent, bool)>>();
 246  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
 250  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
 260  private float _elapsedSimulationTime = 0f;
 261  private bool _isSimulationPaused = false;
 62
 263  private float _costLaunchedInterceptors = 0f;
 264  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>
 47180  public double GetElapsedSimulationTime() {
 47181    return _elapsedSimulationTime;
 47182  }
 83
 84  /// <summary>
 85  /// Gets the total cost of launched interceptors.
 86  /// </summary>
 87  /// <returns>The total cost of launched interceptors.</returns>
 588  public double GetCostLaunchedInterceptors() {
 589    return _costLaunchedInterceptors;
 590  }
 91
 92  /// <summary>
 93  /// Gets the total cost of destroyed threats.
 94  /// </summary>
 95  /// <returns>The total cost of destroyed threats.</returns>
 596  public double GetCostDestroyedThreats() {
 597    return _costDestroyedThreats;
 598  }
 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
 1133  void Awake() {
 134    // Ensure only one instance of SimManager exists
 2135    if (Instance == null) {
 1136      Instance = this;
 1137      DontDestroyOnLoad(gameObject);
 1138    } else {
 0139      Destroy(gameObject);
 0140    }
 1141    simulationConfig = ConfigLoader.LoadSimulationConfig("1_salvo_1_hydra_7_drones.json");
 1142    simulatorConfig = ConfigLoader.LoadSimulatorConfig();
 1143    Debug.Log(simulationConfig);
 1144  }
 145
 1146  void Start() {
 147    // Slow down time by simulationConfig.timeScale
 2148    if (Instance == this) {
 1149      _isSimulationPaused = false;
 1150      StartSimulation();
 1151      ResumeSimulation();
 1152    }
 1153  }
 154
 2155  public void SetTimeScale(float timeScale) {
 2156    Time.timeScale = timeScale;
 2157    Time.maximumDeltaTime = Time.fixedDeltaTime * 3;
 158
 159    // Time.fixedDeltaTime is set in the simulator.json
 2160  }
 161
 1162  public void StartSimulation() {
 1163    OnSimulationStarted?.Invoke();
 1164    InitializeSimulation();
 1165  }
 166
 0167  public void PauseSimulation() {
 0168    SetTimeScale(0);
 0169    Time.fixedDeltaTime = 0;
 0170    _isSimulationPaused = true;
 0171  }
 172
 1173  public void ResumeSimulation() {
 1174    Time.fixedDeltaTime = (float)(1.0f / simulatorConfig.physicsUpdateRate);
 1175    SetTimeScale(simulationConfig.timeScale);
 1176    _isSimulationPaused = false;
 1177  }
 178
 1179  public bool IsSimulationPaused() {
 1180    return _isSimulationPaused;
 1181  }
 182
 1183  private void InitializeSimulation() {
 2184    if (!IsSimulationPaused()) {
 185      // If it wasn't paused, we need to update the time scales NOW
 1186      SetTimeScale(simulationConfig.timeScale);
 1187      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
 1190    }
 191
 192    // Invoke the simulation started event to let listeners
 193    // know to invoke their own handler behavior
 1194    OnSimulationStarted?.Invoke();
 1195    List<Interceptor> missiles = new List<Interceptor>();
 196    // Create missiles based on config
 6197    foreach (var swarmConfig in simulationConfig.interceptor_swarm_configs) {
 1198      List<Agent> swarm = new List<Agent>();
 5199      for (int i = 0; i < swarmConfig.num_agents; i++) {
 1200        Interceptor interceptor = CreateInterceptor(swarmConfig.dynamic_agent_config);
 1201        swarm.Add(interceptor);
 1202      }
 1203      AddInterceptorSwarm(swarm);
 1204    }
 1205    IADS.Instance.RequestThreatAssignment(_interceptorObjects);
 206
 1207    List<Agent> targets = new List<Agent>();
 208    // Create targets based on config
 6209    foreach (var swarmConfig in simulationConfig.threat_swarm_configs) {
 1210      List<Agent> swarm = new List<Agent>();
 23211      for (int i = 0; i < swarmConfig.num_agents; i++) {
 7212        Threat threat = CreateThreat(swarmConfig.dynamic_agent_config);
 7213        swarm.Add(threat);
 7214      }
 1215      AddThreatSwarm(swarm);
 1216    }
 1217  }
 218
 1219  public void AddInterceptorSwarm(List<Agent> swarm) {
 2220    List<(Agent, bool)> swarmTuple = swarm.ConvertAll(agent => (agent, true));
 1221    _interceptorSwarms.Add(swarmTuple);
 6222    foreach (var interceptor in swarm) {
 1223      _interceptorSwarmMap[interceptor] = swarmTuple;
 1224    }
 1225    OnInterceptorSwarmChanged?.Invoke(_interceptorSwarms);
 1226  }
 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
 1246  public void AddThreatSwarm(List<Agent> swarm) {
 8247    List<(Agent, bool)> swarmTuple = swarm.ConvertAll(agent => (agent, true));
 1248    _threatSwarms.Add(swarmTuple);
 24249    foreach (var threat in swarm) {
 7250      _threatSwarmMap[threat] = swarmTuple;
 7251    }
 1252    OnThreatSwarmChanged?.Invoke(_threatSwarms);
 1253  }
 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
 7369  private AttackBehavior LoadAttackBehavior(DynamicAgentConfig config) {
 7370    string threatBehaviorFile = config.attack_behavior;
 7371    AttackBehavior attackBehavior = AttackBehavior.FromJson(threatBehaviorFile);
 7372    switch (attackBehavior.attackBehaviorType) {
 373      case AttackBehavior.AttackBehaviorType.DIRECT_ATTACK:
 7374        return DirectAttackBehavior.FromJson(threatBehaviorFile);
 375      default:
 0376        Debug.LogError($"Attack behavior type '{attackBehavior.attackBehaviorType}' not found.");
 0377        return null;
 378    }
 7379  }
 380
 9381  public Agent CreateDummyAgent(Vector3 position, Vector3 velocity) {
 16382    if (_dummyAgentTable.ContainsKey((position, velocity))) {
 7383      return _dummyAgentTable[(position, velocity)].GetComponent<Agent>();
 384    }
 2385    GameObject dummyAgentPrefab = Resources.Load<GameObject>($"Prefabs/DummyAgent");
 2386    GameObject dummyAgentObject = Instantiate(dummyAgentPrefab, position, Quaternion.identity);
 4387    if (!dummyAgentObject.TryGetComponent<Agent>(out _)) {
 2388      dummyAgentObject.AddComponent<DummyAgent>();
 2389    }
 2390    Rigidbody dummyRigidbody = dummyAgentObject.GetComponent<Rigidbody>();
 2391    dummyRigidbody.linearVelocity = velocity;
 2392    _dummyAgentObjects.Add(dummyAgentObject);
 2393    _dummyAgentTable[(position, velocity)] = dummyAgentObject;
 2394    return dummyAgentObject.GetComponent<Agent>();
 9395  }
 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>
 1402  public Interceptor CreateInterceptor(DynamicAgentConfig config) {
 1403    string interceptorModelFile = config.agent_model;
 1404    interceptorModelFile = "Interceptors/" + interceptorModelFile;
 1405    StaticAgentConfig interceptorStaticAgentConfig =
 406        ConfigLoader.LoadStaticAgentConfig(interceptorModelFile);
 1407    string agentClass = interceptorStaticAgentConfig.agentClass;
 408    // The interceptor class corresponds to the Prefab that must
 409    // exist in the Resources/Prefabs folder
 1410    GameObject interceptorObject = CreateAgent(config, agentClass);
 411
 1412    if (interceptorObject == null)
 0413      return null;
 414
 415    // Interceptor-specific logic
 1416    switch (config.dynamic_config.sensor_config.type) {
 417      case SensorType.IDEAL:
 1418        interceptorObject.AddComponent<IdealSensor>();
 1419        break;
 420      default:
 0421        Debug.LogError($"Sensor type '{config.dynamic_config.sensor_config.type}' not found.");
 0422        break;
 423    }
 424
 1425    Interceptor interceptor = interceptorObject.GetComponent<Interceptor>();
 1426    _interceptorObjects.Add(interceptor);
 1427    _activeInterceptors.Add(interceptor);
 428
 429    // Set the static agent config
 1430    interceptor.SetStaticAgentConfig(interceptorStaticAgentConfig);
 431
 432    // Subscribe events
 1433    interceptor.OnInterceptHit += RegisterInterceptorHit;
 1434    interceptor.OnInterceptMiss += RegisterInterceptorMiss;
 435
 436    // Assign a unique and simple ID
 1437    int interceptorId = _interceptorObjects.Count;
 1438    interceptorObject.name = $"{interceptorStaticAgentConfig.name}_Interceptor_{interceptorId}";
 439
 440    // Add the interceptor's unit cost to the total cost
 1441    _costLaunchedInterceptors += interceptorStaticAgentConfig.unitCost;
 442
 443    // Let listeners know a new interceptor has been created
 1444    OnNewInterceptor?.Invoke(interceptor);
 445
 1446    return interceptor;
 1447  }
 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>
 7454  private Threat CreateThreat(DynamicAgentConfig config) {
 7455    string threatModelFile = config.agent_model;
 7456    threatModelFile = "Threats/" + threatModelFile;
 7457    StaticAgentConfig threatStaticAgentConfig = ConfigLoader.LoadStaticAgentConfig(threatModelFile);
 7458    string agentClass = threatStaticAgentConfig.agentClass;
 459    // The threat class corresponds to the Prefab that must
 460    // exist in the Resources/Prefabs folder
 7461    GameObject threatObject = CreateAgent(config, agentClass);
 462
 7463    if (threatObject == null)
 0464      return null;
 465
 7466    Threat threat = threatObject.GetComponent<Threat>();
 7467    _threatObjects.Add(threat);
 468
 469    // Set the static agent config
 7470    threat.SetStaticAgentConfig(threatStaticAgentConfig);
 471
 472    // Set the attack behavior
 7473    AttackBehavior attackBehavior = LoadAttackBehavior(config);
 7474    threat.SetAttackBehavior(attackBehavior);
 475
 476    // Subscribe events
 7477    threat.OnThreatHit += RegisterThreatHit;
 7478    threat.OnThreatMiss += RegisterThreatMiss;
 479
 480    // Assign a unique and simple ID
 7481    int threatId = _threatObjects.Count;
 7482    threatObject.name = $"{threatStaticAgentConfig.name}_Threat_{threatId}";
 483
 484    // Threats always start in midcourse
 7485    threat.SetFlightPhase(Agent.FlightPhase.MIDCOURSE);
 486
 487    // Let listeners know a new threat has been created
 7488    OnNewThreat?.Invoke(threat);
 489
 7490    return threatObject.GetComponent<Threat>();
 7491  }
 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>
 8499  public GameObject CreateAgent(DynamicAgentConfig config, string prefabName) {
 8500    GameObject prefab = Resources.Load<GameObject>($"Prefabs/{prefabName}");
 8501    if (prefab == null) {
 0502      Debug.LogError($"Prefab '{prefabName}' not found in Resources/Prefabs folder.");
 0503      return null;
 504    }
 505
 8506    Vector3 noiseOffset = Utilities.GenerateRandomNoise(config.standard_deviation.position);
 8507    Vector3 noisyPosition = config.initial_state.position + noiseOffset;
 508
 8509    GameObject agentObject = Instantiate(prefab, noisyPosition, Quaternion.identity);
 510
 8511    Rigidbody agentRigidbody = agentObject.GetComponent<Rigidbody>();
 8512    Vector3 velocityNoise = Utilities.GenerateRandomNoise(config.standard_deviation.velocity);
 8513    Vector3 noisyVelocity = config.initial_state.velocity + velocityNoise;
 8514    agentRigidbody.linearVelocity = noisyVelocity;
 8515    agentObject.GetComponent<Agent>().SetInitialVelocity(noisyVelocity);
 516    // Set rotation to face the initial velocity with noise
 8517    Vector3 velocityDirection = noisyVelocity.normalized;
 8518    Quaternion targetRotation = Quaternion.LookRotation(velocityDirection, Vector3.up);
 8519    agentObject.transform.rotation = targetRotation;
 520
 8521    agentObject.GetComponent<Agent>().SetDynamicAgentConfig(config);
 522
 8523    return agentObject;
 8524  }
 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
 5581  void Update() {
 582    // Check if all missiles have terminated
 5583    bool allInterceptorsTerminated = true;
 25584    foreach (var interceptor in _interceptorObjects) {
 10585      if (interceptor != null && interceptor.GetFlightPhase() != Agent.FlightPhase.TERMINATED) {
 5586        allInterceptorsTerminated = false;
 5587        break;
 588      }
 0589    }
 590    // If all missiles have terminated, restart the simulation
 5591    if (allInterceptorsTerminated) {
 0592      RestartSimulation();
 0593    }
 5594  }
 595
 51596  void FixedUpdate() {
 102597    if (!_isSimulationPaused && _elapsedSimulationTime < simulationConfig.endTime) {
 51598      _elapsedSimulationTime += Time.deltaTime;
 51599    } else if (_elapsedSimulationTime >= simulationConfig.endTime) {
 0600      RestartSimulation();
 0601      Debug.Log("Simulation completed.");
 0602    }
 51603  }
 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()