Format all files

more-targets
Titan Yuan 2024-09-13 22:45:25 -07:00
parent df4c8dfbfe
commit 9be43821ef
21 changed files with 1197 additions and 1415 deletions

View File

@ -2,3 +2,6 @@ BasedOnStyle: Google
--- ---
Language: CSharp Language: CSharp
ColumnLimit: 100 ColumnLimit: 100
---
Language: Json
DisableFormat: true

View File

@ -5,194 +5,174 @@ using Unity.PlasticSCM.Editor.UI;
using Unity.VisualScripting; using Unity.VisualScripting;
using UnityEngine; using UnityEngine;
public abstract class Agent : MonoBehaviour public abstract class Agent : MonoBehaviour {
{ public enum FlightPhase { INITIALIZED, READY, BOOST, MIDCOURSE, TERMINAL, TERMINATED }
public enum FlightPhase {
INITIALIZED, [SerializeField]
READY, private FlightPhase _flightPhase = FlightPhase.INITIALIZED;
BOOST,
MIDCOURSE, [SerializeField]
TERMINAL, protected Agent _target;
TERMINATED protected bool _isHit = false;
protected bool _isMiss = false;
protected AgentConfig _agentConfig;
protected double _timeSinceLaunch = 0;
protected double _timeInPhase = 0;
[SerializeField]
public StaticConfig StaticConfig;
public void SetFlightPhase(FlightPhase flightPhase) {
Debug.Log(
$"Setting flight phase to {flightPhase} at time {SimManager.Instance.GetElapsedSimulationTime()}");
_timeInPhase = 0;
_flightPhase = flightPhase;
}
public FlightPhase GetFlightPhase() {
return _flightPhase;
}
public bool HasLaunched() {
return (_flightPhase != FlightPhase.INITIALIZED) && (_flightPhase != FlightPhase.READY);
}
public bool HasTerminated() {
return _flightPhase == FlightPhase.TERMINATED;
}
public virtual void SetAgentConfig(AgentConfig config) {
_agentConfig = config;
}
public virtual bool IsAssignable() {
return true;
}
public virtual void AssignTarget(Agent target) {
_target = target;
}
public Agent GetAssignedTarget() {
return _target;
}
public bool HasAssignedTarget() {
return _target != null;
}
public void CheckTargetHit() {
if (HasAssignedTarget() && _target.IsHit()) {
UnassignTarget();
}
}
public virtual void UnassignTarget() {
_target = null;
}
// Return whether the agent has hit or been hit.
public bool IsHit() {
return _isHit;
}
public bool IsMiss() {
return _isMiss;
}
public void TerminateAgent() {
_flightPhase = FlightPhase.TERMINATED;
transform.position = new Vector3(0, 0, 0);
gameObject.SetActive(false);
}
// Mark the agent as having hit the target or been hit.
public void MarkAsHit() {
_isHit = true;
TerminateAgent();
}
public void MarkAsMiss() {
_isMiss = true;
if (_target != null) {
SimManager.Instance.RegisterTargetMiss(_target as Target);
_target = null;
}
TerminateAgent();
}
public double GetSpeed() {
return GetComponent<Rigidbody>().velocity.magnitude;
}
public Vector3 GetVelocity() {
return GetComponent<Rigidbody>().velocity;
}
public double GetDynamicPressure() {
var airDensity = Constants.CalculateAirDensityAtAltitude(transform.position.y);
var flowSpeed = GetSpeed();
return 0.5 * airDensity * (flowSpeed * flowSpeed);
}
protected abstract void UpdateReady(double deltaTime);
protected abstract void UpdateBoost(double deltaTime);
protected abstract void UpdateMidCourse(double deltaTime);
// Start is called before the first frame update
protected virtual void Start() {
_flightPhase = FlightPhase.READY;
}
// Update is called once per frame
protected virtual void Update() {
_timeSinceLaunch += Time.deltaTime;
_timeInPhase += Time.deltaTime;
var launch_time = _agentConfig.dynamic_config.launch_config.launch_time;
var boost_time = launch_time + StaticConfig.boostConfig.boostTime;
double elapsedSimulationTime = SimManager.Instance.GetElapsedSimulationTime();
if (_flightPhase == FlightPhase.TERMINATED) {
return;
} }
if (elapsedSimulationTime >= launch_time && _flightPhase == FlightPhase.READY) {
[SerializeField] SetFlightPhase(FlightPhase.BOOST);
private FlightPhase _flightPhase = FlightPhase.INITIALIZED;
[SerializeField]
protected Agent _target;
protected bool _isHit = false;
protected bool _isMiss = false;
protected AgentConfig _agentConfig;
protected double _timeSinceLaunch = 0;
protected double _timeInPhase = 0;
[SerializeField]
public StaticConfig StaticConfig;
public void SetFlightPhase(FlightPhase flightPhase) {
Debug.Log($"Setting flight phase to {flightPhase} at time {SimManager.Instance.GetElapsedSimulationTime()}");
_timeInPhase = 0;
_flightPhase = flightPhase;
} }
if (_timeSinceLaunch > boost_time && _flightPhase == FlightPhase.BOOST) {
public FlightPhase GetFlightPhase() { SetFlightPhase(FlightPhase.MIDCOURSE);
return _flightPhase;
} }
AlignWithVelocity();
switch (_flightPhase) {
public bool HasLaunched() { case FlightPhase.INITIALIZED:
return (_flightPhase != FlightPhase.INITIALIZED) && (_flightPhase != FlightPhase.READY); break;
case FlightPhase.READY:
UpdateReady(Time.deltaTime);
break;
case FlightPhase.BOOST:
UpdateBoost(Time.deltaTime);
break;
case FlightPhase.MIDCOURSE:
case FlightPhase.TERMINAL:
UpdateMidCourse(Time.deltaTime);
break;
case FlightPhase.TERMINATED:
break;
} }
}
public bool HasTerminated() { protected virtual void AlignWithVelocity() {
return _flightPhase == FlightPhase.TERMINATED; Vector3 velocity = GetVelocity();
} if (velocity.magnitude > 0.1f) // Only align if we have significant velocity
public virtual void SetAgentConfig(AgentConfig config) {
_agentConfig = config;
}
public virtual bool IsAssignable() {
return true;
}
public virtual void AssignTarget(Agent target)
{ {
_target = target; // Create a rotation with forward along velocity and up along world up
Quaternion targetRotation = Quaternion.LookRotation(velocity, Vector3.up);
// Smoothly rotate towards the target rotation
transform.rotation =
Quaternion.RotateTowards(transform.rotation, targetRotation, 1000f * Time.deltaTime);
} }
}
public Agent GetAssignedTarget() {
return _target;
}
public bool HasAssignedTarget() {
return _target != null;
}
public void CheckTargetHit() {
if (HasAssignedTarget() && _target.IsHit()) {
UnassignTarget();
}
}
public virtual void UnassignTarget()
{
_target = null;
}
// Return whether the agent has hit or been hit.
public bool IsHit() {
return _isHit;
}
public bool IsMiss() {
return _isMiss;
}
public void TerminateAgent() {
_flightPhase = FlightPhase.TERMINATED;
transform.position = new Vector3(0, 0, 0);
gameObject.SetActive(false);
}
// Mark the agent as having hit the target or been hit.
public void MarkAsHit() {
_isHit = true;
TerminateAgent();
}
public void MarkAsMiss() {
_isMiss = true;
if(_target != null) {
SimManager.Instance.RegisterTargetMiss(_target as Target);
_target = null;
}
TerminateAgent();
}
public double GetSpeed() {
return GetComponent<Rigidbody>().velocity.magnitude;
}
public Vector3 GetVelocity() {
return GetComponent<Rigidbody>().velocity;
}
public double GetDynamicPressure() {
var airDensity = Constants.CalculateAirDensityAtAltitude(transform.position.y);
var flowSpeed = GetSpeed();
return 0.5 * airDensity * (flowSpeed * flowSpeed);
}
protected abstract void UpdateReady(double deltaTime);
protected abstract void UpdateBoost(double deltaTime);
protected abstract void UpdateMidCourse(double deltaTime);
// Start is called before the first frame update
protected virtual void Start()
{
_flightPhase = FlightPhase.READY;
}
// Update is called once per frame
protected virtual void Update()
{
_timeSinceLaunch += Time.deltaTime;
_timeInPhase += Time.deltaTime;
var launch_time = _agentConfig.dynamic_config.launch_config.launch_time;
var boost_time = launch_time + StaticConfig.boostConfig.boostTime;
double elapsedSimulationTime = SimManager.Instance.GetElapsedSimulationTime();
if(_flightPhase == FlightPhase.TERMINATED) {
return;
}
if(elapsedSimulationTime >= launch_time && _flightPhase == FlightPhase.READY) {
SetFlightPhase(FlightPhase.BOOST);
}
if(_timeSinceLaunch > boost_time && _flightPhase == FlightPhase.BOOST) {
SetFlightPhase(FlightPhase.MIDCOURSE);
}
AlignWithVelocity();
switch (_flightPhase) {
case FlightPhase.INITIALIZED:
break;
case FlightPhase.READY:
UpdateReady(Time.deltaTime);
break;
case FlightPhase.BOOST:
UpdateBoost(Time.deltaTime);
break;
case FlightPhase.MIDCOURSE:
case FlightPhase.TERMINAL:
UpdateMidCourse(Time.deltaTime);
break;
case FlightPhase.TERMINATED:
break;
}
}
protected virtual void AlignWithVelocity()
{
Vector3 velocity = GetVelocity();
if (velocity.magnitude > 0.1f) // Only align if we have significant velocity
{
// Create a rotation with forward along velocity and up along world up
Quaternion targetRotation = Quaternion.LookRotation(velocity, Vector3.up);
// Smoothly rotate towards the target rotation
transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, 1000f * Time.deltaTime);
}
}
} }

View File

@ -3,53 +3,44 @@ using System.Collections.Generic;
using UnityEngine; using UnityEngine;
// The assignment class is an interface for assigning a target to each missile. // The assignment class is an interface for assigning a target to each missile.
public interface IAssignment public interface IAssignment {
{ // Assignment item type.
// Assignment item type. // The first element corresponds to the missile index, and the second element
// The first element corresponds to the missile index, and the second element // corresponds to the target index.
// corresponds to the target index. public struct AssignmentItem {
public struct AssignmentItem public int MissileIndex;
{ public int TargetIndex;
public int MissileIndex;
public int TargetIndex;
public AssignmentItem(int missileIndex, int targetIndex) public AssignmentItem(int missileIndex, int targetIndex) {
{ MissileIndex = missileIndex;
MissileIndex = missileIndex; TargetIndex = targetIndex;
TargetIndex = targetIndex;
}
} }
}
// A list containing the missile-target assignments. // A list containing the missile-target assignments.
// Assign a target to each missile that has not been assigned a target yet. // Assign a target to each missile that has not been assigned a target yet.
public abstract IEnumerable<AssignmentItem> Assign(List<Agent> missiles, List<Agent> targets); public abstract IEnumerable<AssignmentItem> Assign(List<Agent> missiles, List<Agent> targets);
// Get the list of assignable missile indices. // Get the list of assignable missile indices.
protected static List<int> GetAssignableMissileIndices(List<Agent> missiles) protected static List<int> GetAssignableMissileIndices(List<Agent> missiles) {
{ List<int> assignableMissileIndices = new List<int>();
List<int> assignableMissileIndices = new List<int>(); for (int missileIndex = 0; missileIndex < missiles.Count; missileIndex++) {
for (int missileIndex = 0; missileIndex < missiles.Count; missileIndex++) if (missiles[missileIndex].IsAssignable()) {
{ assignableMissileIndices.Add(missileIndex);
if (missiles[missileIndex].IsAssignable()) }
{
assignableMissileIndices.Add(missileIndex);
}
}
return assignableMissileIndices;
} }
return assignableMissileIndices;
}
// Get the list of active target indices. // Get the list of active target indices.
protected static List<int> GetActiveTargetIndices(List<Agent> targets) protected static List<int> GetActiveTargetIndices(List<Agent> targets) {
{ List<int> activeTargetIndices = new List<int>();
List<int> activeTargetIndices = new List<int>(); for (int targetIndex = 0; targetIndex < targets.Count; targetIndex++) {
for (int targetIndex = 0; targetIndex < targets.Count; targetIndex++) if (!targets[targetIndex].IsHit()) {
{ activeTargetIndices.Add(targetIndex);
if (!targets[targetIndex].IsHit()) }
{
activeTargetIndices.Add(targetIndex);
}
}
return activeTargetIndices;
} }
return activeTargetIndices;
}
} }

View File

@ -5,42 +5,35 @@ using UnityEngine;
// The round-robin assignment class assigns missiles to the targets in a // The round-robin assignment class assigns missiles to the targets in a
// round-robin order. // round-robin order.
public class RoundRobinAssignment : IAssignment public class RoundRobinAssignment : IAssignment {
{ // Previous target index that was assigned.
// Previous target index that was assigned. private int prevTargetIndex = -1;
private int prevTargetIndex = -1;
// Assign a target to each missile that has not been assigned a target yet. // Assign a target to each missile that has not been assigned a target yet.
public IEnumerable<IAssignment.AssignmentItem> Assign(List<Agent> missiles, List<Agent> targets) public IEnumerable<IAssignment.AssignmentItem> Assign(List<Agent> missiles, List<Agent> targets) {
{ List<IAssignment.AssignmentItem> assignments = new List<IAssignment.AssignmentItem>();
List<IAssignment.AssignmentItem> assignments = new List<IAssignment.AssignmentItem>(); List<int> assignableMissileIndices = IAssignment.GetAssignableMissileIndices(missiles);
List<int> assignableMissileIndices = IAssignment.GetAssignableMissileIndices(missiles); if (assignableMissileIndices.Count == 0) {
if (assignableMissileIndices.Count == 0) return assignments;
{
return assignments;
}
List<int> activeTargetIndices = IAssignment.GetActiveTargetIndices(targets);
if (activeTargetIndices.Count == 0)
{
return assignments;
}
foreach (int missileIndex in assignableMissileIndices)
{
int nextActiveTargetIndex = activeTargetIndices
.FindIndex(index => index > prevTargetIndex);
if (nextActiveTargetIndex == -1)
{
nextActiveTargetIndex = 0;
}
int nextTargetIndex = activeTargetIndices[nextActiveTargetIndex];
assignments.Add(new IAssignment.AssignmentItem(missileIndex, nextTargetIndex));
prevTargetIndex = nextTargetIndex;
}
return assignments;
} }
List<int> activeTargetIndices = IAssignment.GetActiveTargetIndices(targets);
if (activeTargetIndices.Count == 0) {
return assignments;
}
foreach (int missileIndex in assignableMissileIndices) {
int nextActiveTargetIndex = activeTargetIndices.FindIndex(index => index > prevTargetIndex);
if (nextActiveTargetIndex == -1) {
nextActiveTargetIndex = 0;
}
int nextTargetIndex = activeTargetIndices[nextActiveTargetIndex];
assignments.Add(new IAssignment.AssignmentItem(missileIndex, nextTargetIndex));
prevTargetIndex = nextTargetIndex;
}
return assignments;
}
} }

View File

@ -6,89 +6,80 @@ using UnityEngine;
// The threat assignment class assigns missiles to the targets based // The threat assignment class assigns missiles to the targets based
// on the threat level of the targets. // on the threat level of the targets.
public class ThreatAssignment : IAssignment public class ThreatAssignment : IAssignment {
{ // Assign a target to each missile that has not been assigned a target yet.
// Assign a target to each missile that has not been assigned a target yet. public IEnumerable<IAssignment.AssignmentItem> Assign(List<Agent> missiles, List<Agent> targets) {
public IEnumerable<IAssignment.AssignmentItem> Assign(List<Agent> missiles, List<Agent> targets) List<IAssignment.AssignmentItem> assignments = new List<IAssignment.AssignmentItem>();
{
List<IAssignment.AssignmentItem> assignments = new List<IAssignment.AssignmentItem>(); List<int> assignableMissileIndices = IAssignment.GetAssignableMissileIndices(missiles);
if (assignableMissileIndices.Count == 0) {
List<int> assignableMissileIndices = IAssignment.GetAssignableMissileIndices(missiles); return assignments;
if (assignableMissileIndices.Count == 0)
{
return assignments;
}
List<int> activeTargetIndices = IAssignment.GetActiveTargetIndices(targets);
if (activeTargetIndices.Count == 0)
{
return assignments;
}
Vector3 positionToDefend = Vector3.zero;
List<ThreatInfo> threatInfos = CalculateThreatLevels(targets, activeTargetIndices, positionToDefend);
foreach (int missileIndex in assignableMissileIndices)
{
if (missiles[missileIndex].HasAssignedTarget()) continue;
if (threatInfos.Count == 0) break;
// Find the optimal target for this missile based on distance and threat
ThreatInfo optimalTarget = null;
float optimalScore = float.MinValue;
foreach (ThreatInfo threat in threatInfos)
{
float distance = Vector3.Distance(missiles[missileIndex].transform.position, targets[threat.TargetIndex].transform.position);
float score = threat.ThreatLevel / distance; // Balance threat level with proximity
if (score > optimalScore)
{
optimalScore = score;
optimalTarget = threat;
}
}
if (optimalTarget != null)
{
assignments.Add(new IAssignment.AssignmentItem(missileIndex, optimalTarget.TargetIndex));
threatInfos.Remove(optimalTarget);
}
}
return assignments;
} }
List<int> activeTargetIndices = IAssignment.GetActiveTargetIndices(targets);
private List<ThreatInfo> CalculateThreatLevels(List<Agent> targets, List<int> activeTargetIndices, Vector3 missilesMeanPosition) if (activeTargetIndices.Count == 0) {
{ return assignments;
List<ThreatInfo> threatInfos = new List<ThreatInfo>();
foreach (int targetIndex in activeTargetIndices)
{
Agent target = targets[targetIndex];
float distanceToMean = Vector3.Distance(target.transform.position, missilesMeanPosition);
float velocityMagnitude = target.GetVelocity().magnitude;
// Calculate threat level based on proximity and velocity
float threatLevel = (1 / distanceToMean) * velocityMagnitude;
threatInfos.Add(new ThreatInfo(targetIndex, threatLevel));
}
// Sort threats in descending order
return threatInfos.OrderByDescending(t => t.ThreatLevel).ToList();
} }
private class ThreatInfo Vector3 positionToDefend = Vector3.zero;
{ List<ThreatInfo> threatInfos =
public int TargetIndex { get; } CalculateThreatLevels(targets, activeTargetIndices, positionToDefend);
public float ThreatLevel { get; }
public ThreatInfo(int targetIndex, float threatLevel) foreach (int missileIndex in assignableMissileIndices) {
{ if (missiles[missileIndex].HasAssignedTarget())
TargetIndex = targetIndex; continue;
ThreatLevel = threatLevel; if (threatInfos.Count == 0)
break;
// Find the optimal target for this missile based on distance and threat
ThreatInfo optimalTarget = null;
float optimalScore = float.MinValue;
foreach (ThreatInfo threat in threatInfos) {
float distance = Vector3.Distance(missiles[missileIndex].transform.position,
targets[threat.TargetIndex].transform.position);
float score = threat.ThreatLevel / distance; // Balance threat level with proximity
if (score > optimalScore) {
optimalScore = score;
optimalTarget = threat;
} }
}
if (optimalTarget != null) {
assignments.Add(new IAssignment.AssignmentItem(missileIndex, optimalTarget.TargetIndex));
threatInfos.Remove(optimalTarget);
}
} }
return assignments;
}
private List<ThreatInfo> CalculateThreatLevels(List<Agent> targets, List<int> activeTargetIndices,
Vector3 missilesMeanPosition) {
List<ThreatInfo> threatInfos = new List<ThreatInfo>();
foreach (int targetIndex in activeTargetIndices) {
Agent target = targets[targetIndex];
float distanceToMean = Vector3.Distance(target.transform.position, missilesMeanPosition);
float velocityMagnitude = target.GetVelocity().magnitude;
// Calculate threat level based on proximity and velocity
float threatLevel = (1 / distanceToMean) * velocityMagnitude;
threatInfos.Add(new ThreatInfo(targetIndex, threatLevel));
}
// Sort threats in descending order
return threatInfos.OrderByDescending(t => t.ThreatLevel).ToList();
}
private class ThreatInfo {
public int TargetIndex { get; }
public float ThreatLevel { get; }
public ThreatInfo(int targetIndex, float threatLevel) {
TargetIndex = targetIndex;
ThreatLevel = threatLevel;
}
}
} }

View File

@ -3,157 +3,117 @@ using System.Collections.Generic;
using UnityEngine; using UnityEngine;
[CreateAssetMenu(fileName = "SimulationConfig", menuName = "Simulation/Config", order = 1)] [CreateAssetMenu(fileName = "SimulationConfig", menuName = "Simulation/Config", order = 1)]
public class SimulationConfig : ScriptableObject public class SimulationConfig : ScriptableObject {
{ [Header("Simulation Settings")] [Header("Simulation Settings")]
public float timeScale = 0.05f; public float timeScale = 0.05f;
[Header("Missile Swarm Configurations")] [Header("Missile Swarm Configurations")]
public List<SwarmConfig> missile_swarm_configs = new List<SwarmConfig>(); public List<SwarmConfig> missile_swarm_configs = new List<SwarmConfig>();
[Header("Target Swarm Configurations")] [Header("Target Swarm Configurations")]
public List<SwarmConfig> target_swarm_configs = new List<SwarmConfig>(); public List<SwarmConfig> target_swarm_configs = new List<SwarmConfig>();
} }
[System.Serializable] [System.Serializable]
public class DynamicConfig public class DynamicConfig {
{ public LaunchConfig launch_config;
public LaunchConfig launch_config; public SensorConfig sensor_config;
public SensorConfig sensor_config;
} }
[System.Serializable] [System.Serializable]
public class SwarmConfig public class SwarmConfig {
{ public int num_agents;
public int num_agents; public AgentConfig agent_config;
public AgentConfig agent_config;
} }
[System.Serializable] [System.Serializable]
public class AgentConfig public class AgentConfig {
{ public MissileType missile_type;
public MissileType missile_type; public TargetType target_type;
public TargetType target_type; public InitialState initial_state;
public InitialState initial_state; public StandardDeviation standard_deviation;
public StandardDeviation standard_deviation; public DynamicConfig dynamic_config;
public DynamicConfig dynamic_config; public PlottingConfig plotting_config;
public PlottingConfig plotting_config; public SubmunitionsConfig submunitions_config;
public SubmunitionsConfig submunitions_config;
public static AgentConfig FromSubmunitionAgentConfig(SubmunitionAgentConfig submunitionConfig) public static AgentConfig FromSubmunitionAgentConfig(SubmunitionAgentConfig submunitionConfig) {
{ return new AgentConfig {
return new AgentConfig missile_type = submunitionConfig.missile_type,
{ initial_state = submunitionConfig.initial_state,
missile_type = submunitionConfig.missile_type, standard_deviation = submunitionConfig.standard_deviation,
initial_state = submunitionConfig.initial_state, dynamic_config = submunitionConfig.dynamic_config,
standard_deviation = submunitionConfig.standard_deviation, plotting_config = submunitionConfig.plotting_config,
dynamic_config = submunitionConfig.dynamic_config,
plotting_config = submunitionConfig.plotting_config, // Set other fields as needed, using default values if not present in SubmunitionAgentConfig
target_type = TargetType.DRONE, // Or another default value
// Set other fields as needed, using default values if not present in SubmunitionAgentConfig submunitions_config = null // Or a default value if needed
target_type = TargetType.DRONE, // Or another default value };
submunitions_config = null // Or a default value if needed }
};
}
} }
[System.Serializable] [System.Serializable]
public class InitialState public class InitialState {
{ public Vector3 position;
public Vector3 position; public Vector3 rotation;
public Vector3 rotation; public Vector3 velocity;
public Vector3 velocity;
} }
[System.Serializable] [System.Serializable]
public class StandardDeviation public class StandardDeviation {
{ public Vector3 position;
public Vector3 position; public Vector3 velocity;
public Vector3 velocity;
} }
[System.Serializable] [System.Serializable]
public class LaunchConfig public class LaunchConfig {
{ public float launch_time;
public float launch_time;
} }
[System.Serializable] [System.Serializable]
public class PlottingConfig public class PlottingConfig {
{ public Color color;
public Color color; public LineStyle linestyle;
public LineStyle linestyle; public Marker marker;
public Marker marker;
} }
[System.Serializable] [System.Serializable]
public class SubmunitionsConfig public class SubmunitionsConfig {
{ public int num_submunitions;
public int num_submunitions; public LaunchConfig launch_config;
public LaunchConfig launch_config; public SubmunitionAgentConfig agent_config;
public SubmunitionAgentConfig agent_config;
} }
[System.Serializable] [System.Serializable]
public class SubmunitionAgentConfig public class SubmunitionAgentConfig {
{ public MissileType missile_type;
public MissileType missile_type; public InitialState initial_state;
public InitialState initial_state; public StandardDeviation standard_deviation;
public StandardDeviation standard_deviation; public DynamicConfig dynamic_config;
public DynamicConfig dynamic_config; public PlottingConfig plotting_config;
public PlottingConfig plotting_config;
} }
[System.Serializable] [System.Serializable]
public class SensorConfig public class SensorConfig {
{ public SensorType type;
public SensorType type; public float frequency;
public float frequency;
} }
[System.Serializable] [System.Serializable]
public class TargetConfig public class TargetConfig {
{ public TargetType target_type;
public TargetType target_type; public InitialState initial_state;
public InitialState initial_state; public PlottingConfig plotting_config;
public PlottingConfig plotting_config; public string prefabName;
public string prefabName;
} }
public enum MissileType public enum MissileType { HYDRA_70, MICROMISSILE }
{
HYDRA_70,
MICROMISSILE
}
public enum TargetType public enum TargetType { DRONE, MISSILE }
{
DRONE,
MISSILE
}
public enum ConfigColor public enum ConfigColor { BLUE, GREEN, RED }
{
BLUE,
GREEN,
RED
}
public enum LineStyle public enum LineStyle { DOTTED, SOLID }
{
DOTTED,
SOLID
}
public enum Marker public enum Marker { TRIANGLE_UP, TRIANGLE_DOWN, SQUARE }
{
TRIANGLE_UP,
TRIANGLE_DOWN,
SQUARE
}
public enum SensorType public enum SensorType { IDEAL }
{
IDEAL
}

View File

@ -3,71 +3,65 @@ using System.Collections.Generic;
using UnityEngine; using UnityEngine;
[System.Serializable] [System.Serializable]
public class StaticConfig public class StaticConfig {
{ [System.Serializable]
[System.Serializable] public class AccelerationConfig {
public class AccelerationConfig [Tooltip("Maximum reference acceleration")]
{ public float maxReferenceAcceleration = 300f;
[Tooltip("Maximum reference acceleration")] [Tooltip("Reference speed")]
public float maxReferenceAcceleration = 300f; public float referenceSpeed = 1000f;
[Tooltip("Reference speed")] }
public float referenceSpeed = 1000f;
}
[System.Serializable] [System.Serializable]
public class BoostConfig public class BoostConfig {
{ [Tooltip("Boost time in seconds")]
[Tooltip("Boost time in seconds")] public float boostTime = 0.3f;
public float boostTime = 0.3f; [Tooltip("Boost acceleration")]
[Tooltip("Boost acceleration")] public float boostAcceleration = 350f;
public float boostAcceleration = 350f; }
}
[System.Serializable] [System.Serializable]
public class LiftDragConfig public class LiftDragConfig {
{ [Tooltip("Lift coefficient")]
[Tooltip("Lift coefficient")] public float liftCoefficient = 0.2f;
public float liftCoefficient = 0.2f; [Tooltip("Drag coefficient")]
[Tooltip("Drag coefficient")] public float dragCoefficient = 0.7f;
public float dragCoefficient = 0.7f; [Tooltip("Lift to drag ratio")]
[Tooltip("Lift to drag ratio")] public float liftDragRatio = 5f;
public float liftDragRatio = 5f; }
}
[System.Serializable] [System.Serializable]
public class BodyConfig public class BodyConfig {
{ [Tooltip("Mass in kg")]
[Tooltip("Mass in kg")] public float mass = 0.37f;
public float mass = 0.37f; [Tooltip("Cross-sectional area in m²")]
[Tooltip("Cross-sectional area in m²")] public float crossSectionalArea = 3e-4f;
public float crossSectionalArea = 3e-4f; [Tooltip("Fin area in m²")]
[Tooltip("Fin area in m²")] public float finArea = 6e-4f;
public float finArea = 6e-4f; [Tooltip("Body area in m²")]
[Tooltip("Body area in m²")] public float bodyArea = 1e-2f;
public float bodyArea = 1e-2f; }
}
[System.Serializable] [System.Serializable]
public class HitConfig public class HitConfig {
{ [Tooltip("Hit radius")]
[Tooltip("Hit radius")] public float hitRadius = 1f;
public float hitRadius = 1f; [Tooltip("Kill probability")]
[Tooltip("Kill probability")] public float killProbability = 0.9f;
public float killProbability = 0.9f; }
}
[Header("Acceleration Configuration")] [Header("Acceleration Configuration")]
public AccelerationConfig accelerationConfig; public AccelerationConfig accelerationConfig;
[Header("Boost Configuration")] [Header("Boost Configuration")]
public BoostConfig boostConfig; public BoostConfig boostConfig;
[Header("Lift and Drag Configuration")] [Header("Lift and Drag Configuration")]
public LiftDragConfig liftDragConfig; public LiftDragConfig liftDragConfig;
[Header("Body Configuration")] [Header("Body Configuration")]
public BodyConfig bodyConfig; public BodyConfig bodyConfig;
[Header("Hit Configuration")] [Header("Hit Configuration")]
public HitConfig hitConfig; public HitConfig hitConfig;
} }

View File

@ -1,20 +1,17 @@
using System; using System;
public static class Constants public static class Constants {
{ // Constants (these should be defined with appropriate values)
// Constants (these should be defined with appropriate values) public const double kAirDensity = 1.204; // Sea level air density in kg/m^3
public const double kAirDensity = 1.204; // Sea level air density in kg/m^3 public const double kAirDensityScaleHeight = 10.4; // Scale height in km
public const double kAirDensityScaleHeight = 10.4; // Scale height in km public const double kGravity = 9.80665; // Standard gravity in m/s^2
public const double kGravity = 9.80665; // Standard gravity in m/s^2 public const double kEarthMeanRadius = 6378137; // Earth's mean radius in meters
public const double kEarthMeanRadius = 6378137; // Earth's mean radius in meters
public static double CalculateAirDensityAtAltitude(double altitude) public static double CalculateAirDensityAtAltitude(double altitude) {
{ return kAirDensity * Math.Exp(-altitude / (kAirDensityScaleHeight * 1000));
return kAirDensity * Math.Exp(-altitude / (kAirDensityScaleHeight * 1000)); }
}
public static double CalculateGravityAtAltitude(double altitude) public static double CalculateGravityAtAltitude(double altitude) {
{ return kGravity * Math.Pow(kEarthMeanRadius / (kEarthMeanRadius + altitude), 2);
return kGravity * Math.Pow(kEarthMeanRadius / (kEarthMeanRadius + altitude), 2); }
}
} }

View File

@ -3,155 +3,140 @@ using UnityEditor;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
public class GenerateCone : EditorWindow public class GenerateCone : EditorWindow {
{ private int sides = 16;
private int sides = 16; private float baseRadius = 1f;
private float baseRadius = 1f; private float height = 2f;
private float height = 2f;
[MenuItem("GameObject/3D Object/Cone", false, 10)] [MenuItem("GameObject/3D Object/Cone", false, 10)]
static void CreateCone() static void CreateCone() {
{ GameObject cone;
GameObject cone; GameObject selectedObject = Selection.activeGameObject;
GameObject selectedObject = Selection.activeGameObject;
if (selectedObject != null) if (selectedObject != null) {
{ // Create as child of selected object
// Create as child of selected object cone = new GameObject("Cone");
cone = new GameObject("Cone"); cone.transform.SetParent(selectedObject.transform, false);
cone.transform.SetParent(selectedObject.transform, false); } else {
} // Create as new root object
else cone = new GameObject("Cone");
{
// Create as new root object
cone = new GameObject("Cone");
}
cone.AddComponent<MeshFilter>();
cone.AddComponent<MeshRenderer>();
Undo.RegisterCreatedObjectUndo(cone, "Create Cone");
var window = ScriptableObject.CreateInstance<GenerateCone>();
window.GenerateConeObject(cone);
Selection.activeGameObject = cone;
} }
void GenerateConeObject(GameObject cone) cone.AddComponent<MeshFilter>();
{ cone.AddComponent<MeshRenderer>();
Mesh mesh = CreateConeMesh("ConeMesh", sides, Vector3.zero, Quaternion.identity, baseRadius, height); Undo.RegisterCreatedObjectUndo(cone, "Create Cone");
// Save the mesh as an asset
string path = "Assets/Meshes";
if (!AssetDatabase.IsValidFolder(path))
{
AssetDatabase.CreateFolder("Assets", "Meshes");
}
string assetPath = AssetDatabase.GenerateUniqueAssetPath(path + "/ConeMesh.asset");
AssetDatabase.CreateAsset(mesh, assetPath);
AssetDatabase.SaveAssets();
// Assign the mesh to the MeshFilter var window = ScriptableObject.CreateInstance<GenerateCone>();
cone.GetComponent<MeshFilter>().sharedMesh = mesh; window.GenerateConeObject(cone);
cone.GetComponent<MeshRenderer>().material = new Material(Shader.Find("Standard"));
Selection.activeGameObject = cone;
}
void GenerateConeObject(GameObject cone) {
Mesh mesh =
CreateConeMesh("ConeMesh", sides, Vector3.zero, Quaternion.identity, baseRadius, height);
// Save the mesh as an asset
string path = "Assets/Meshes";
if (!AssetDatabase.IsValidFolder(path)) {
AssetDatabase.CreateFolder("Assets", "Meshes");
}
string assetPath = AssetDatabase.GenerateUniqueAssetPath(path + "/ConeMesh.asset");
AssetDatabase.CreateAsset(mesh, assetPath);
AssetDatabase.SaveAssets();
// Assign the mesh to the MeshFilter
cone.GetComponent<MeshFilter>().sharedMesh = mesh;
cone.GetComponent<MeshRenderer>().material = new Material(Shader.Find("Standard"));
}
Vector2[] GetBasePoints(int vertices, float radius) {
const float TAU = 2f * Mathf.PI;
var pts = new Vector2[vertices];
var step = TAU / vertices; // angular step between two vertices
for (int i = 0; i < vertices; i++) {
pts[i] = radius * Trig(i * step); // convert polar coordinate to cartesian space
}
return pts;
}
static Vector2 Trig(float rad) => new Vector2(Mathf.Cos(rad), Mathf.Sin(rad));
Vector3[] BuildConeVertices(Vector2[] baseVerts, float coneHeight) {
if (baseVerts == null || baseVerts.Length < 3)
throw new InvalidOperationException("Requires at least 3 base vertices.");
var verts = new Vector3[baseVerts.Length + 1];
verts[0] = new Vector3(0f, coneHeight, 0f);
for (int i = 0; i < baseVerts.Length; i++) {
verts[i + 1] = new Vector3(baseVerts[i].x, 0f, baseVerts[i].y);
}
return verts;
}
void ConstructCone(Vector3[] coneVerts, List<Vector3> finalVerts, List<int> triangles) {
if (coneVerts == null || coneVerts.Length < 4)
throw new InvalidOperationException("Requires at least 4 vertices.");
if (finalVerts == null || triangles == null)
throw new ArgumentNullException();
finalVerts.Clear();
triangles.Clear();
var rimVertices = coneVerts.Length - 1;
// Side faces
for (int i = 1; i <= rimVertices; i++) {
int a = i, b = i < rimVertices ? i + 1 : 1;
AddTriangle(coneVerts[0], coneVerts[b], coneVerts[a]);
} }
Vector2[] GetBasePoints(int vertices, float radius) // Base face
{ for (int i = 1; i < rimVertices - 1; i++) {
const float TAU = 2f * Mathf.PI; AddTriangle(coneVerts[1], coneVerts[i + 1], coneVerts[i + 2]);
var pts = new Vector2[vertices];
var step = TAU / vertices; // angular step between two vertices
for (int i = 0; i < vertices; i++)
{
pts[i] = radius * Trig(i * step); // convert polar coordinate to cartesian space
}
return pts;
} }
static Vector2 Trig(float rad) => new Vector2(Mathf.Cos(rad), Mathf.Sin(rad)); void AddTriangle(Vector3 t1, Vector3 t2, Vector3 t3) {
finalVerts.Add(t1);
finalVerts.Add(t2);
finalVerts.Add(t3);
triangles.Add(finalVerts.Count - 3);
triangles.Add(finalVerts.Count - 2);
triangles.Add(finalVerts.Count - 1);
}
}
Mesh CreateConeMesh(string name, int sides, Vector3 apex, Quaternion rotation, float baseRadius,
float height) {
var baseVerts = GetBasePoints(sides, baseRadius);
var coneVerts = BuildConeVertices(baseVerts, height);
Vector3[] BuildConeVertices(Vector2[] baseVerts, float coneHeight) var verts = new List<Vector3>();
{ var tris = new List<int>();
if (baseVerts == null || baseVerts.Length < 3) throw new InvalidOperationException("Requires at least 3 base vertices."); ConstructCone(coneVerts, verts, tris);
var verts = new Vector3[baseVerts.Length + 1];
verts[0] = new Vector3(0f, coneHeight, 0f); for (int i = 0; i < verts.Count; i++) {
for (int i = 0; i < baseVerts.Length; i++) verts[i] = rotation * (verts[i] - coneVerts[0]);
{
verts[i + 1] = new Vector3(baseVerts[i].x, 0f, baseVerts[i].y);
}
return verts;
} }
void ConstructCone(Vector3[] coneVerts, List<Vector3> finalVerts, List<int> triangles) // Recenter the cone
{ Vector3 center = CalculateCenter(verts);
if (coneVerts == null || coneVerts.Length < 4) throw new InvalidOperationException("Requires at least 4 vertices."); for (int i = 0; i < verts.Count; i++) {
if (finalVerts == null || triangles == null) throw new ArgumentNullException(); verts[i] = verts[i] - center + apex;
finalVerts.Clear();
triangles.Clear();
var rimVertices = coneVerts.Length - 1;
// Side faces
for (int i = 1; i <= rimVertices; i++)
{
int a = i, b = i < rimVertices ? i + 1 : 1;
AddTriangle(coneVerts[0], coneVerts[b], coneVerts[a]);
}
// Base face
for (int i = 1; i < rimVertices - 1; i++)
{
AddTriangle(coneVerts[1], coneVerts[i + 1], coneVerts[i + 2]);
}
void AddTriangle(Vector3 t1, Vector3 t2, Vector3 t3)
{
finalVerts.Add(t1);
finalVerts.Add(t2);
finalVerts.Add(t3);
triangles.Add(finalVerts.Count - 3);
triangles.Add(finalVerts.Count - 2);
triangles.Add(finalVerts.Count - 1);
}
}
Mesh CreateConeMesh(string name, int sides, Vector3 apex, Quaternion rotation, float baseRadius, float height)
{
var baseVerts = GetBasePoints(sides, baseRadius);
var coneVerts = BuildConeVertices(baseVerts, height);
var verts = new List<Vector3>();
var tris = new List<int>();
ConstructCone(coneVerts, verts, tris);
for (int i = 0; i < verts.Count; i++)
{
verts[i] = rotation * (verts[i] - coneVerts[0]);
}
// Recenter the cone
Vector3 center = CalculateCenter(verts);
for (int i = 0; i < verts.Count; i++)
{
verts[i] = verts[i] - center + apex;
}
Mesh mesh = new Mesh();
mesh.name = name;
mesh.SetVertices(verts);
mesh.SetTriangles(tris.ToArray(), 0);
mesh.RecalculateNormals();
return mesh;
} }
Vector3 CalculateCenter(List<Vector3> vertices) Mesh mesh = new Mesh();
{ mesh.name = name;
Vector3 sum = Vector3.zero; mesh.SetVertices(verts);
foreach (Vector3 vert in vertices) mesh.SetTriangles(tris.ToArray(), 0);
{ mesh.RecalculateNormals();
sum += vert;
} return mesh;
return sum / vertices.Count; }
Vector3 CalculateCenter(List<Vector3> vertices) {
Vector3 sum = Vector3.zero;
foreach (Vector3 vert in vertices) {
sum += vert;
} }
return sum / vertices.Count;
}
} }

View File

@ -4,36 +4,27 @@ using System.Collections;
using System; using System;
// Integrated Air Defense System // Integrated Air Defense System
public class IADS : MonoBehaviour public class IADS : MonoBehaviour {
{ public enum TargetStatus { UNASSIGNED, ASSIGNED, HIT, DEGRADED, DESTROYED }
public enum TargetStatus { // Look up target status by unique target ID
UNASSIGNED, public Dictionary<string, TargetStatus> _targetStatusDictionary;
ASSIGNED,
HIT,
DEGRADED,
DESTROYED
}
// Look up target status by unique target ID private List<Target> _targets;
public Dictionary<string, TargetStatus> _targetStatusDictionary;
private List<Target> _targets;
private List<Missile> _missiles; private List<Missile> _missiles;
private List<Vessel> _vessels; private List<Vessel> _vessels;
public delegate void RegisterNewTargetDelegate(Target target); public delegate void RegisterNewTargetDelegate(Target target);
public event RegisterNewTargetDelegate OnRegisterNewTarget; public event RegisterNewTargetDelegate OnRegisterNewTarget;
void Start() void Start() {
{ _targets = new List<Target>();
_targets = new List<Target>(); }
}
public void RegisterNewTarget(Target target) { public void RegisterNewTarget(Target target) {
_targets.Add(target); _targets.Add(target);
OnRegisterNewTarget?.Invoke(target); OnRegisterNewTarget?.Invoke(target);
} }
} }

View File

@ -1,33 +1,27 @@
using UnityEngine; using UnityEngine;
using System.Collections.Generic; using System.Collections.Generic;
public class Vessel : MonoBehaviour public class Vessel : MonoBehaviour {
{ [SerializeField]
[SerializeField] private List<Missile> missileInventory = new List<Missile>();
private List<Missile> missileInventory = new List<Missile>();
public void AddMissile(Missile missile) public void AddMissile(Missile missile) {
{ if (missile != null) {
if (missile != null) missileInventory.Add(missile);
{
missileInventory.Add(missile);
}
} }
}
public void RemoveMissile(Missile missile) public void RemoveMissile(Missile missile) {
{ missileInventory.Remove(missile);
missileInventory.Remove(missile); }
}
public List<Missile> GetMissileInventory() public List<Missile> GetMissileInventory() {
{ return new List<Missile>(missileInventory);
return new List<Missile>(missileInventory); }
}
public int GetMissileCount() public int GetMissileCount() {
{ return missileInventory.Count;
return missileInventory.Count; }
}
// Additional methods can be added here as needed // Additional methods can be added here as needed
} }

View File

@ -3,63 +3,57 @@ using System.Collections.Generic;
using JetBrains.Annotations; using JetBrains.Annotations;
using UnityEngine; using UnityEngine;
public class Hydra70 : Missile public class Hydra70 : Missile {
{ private Vector3 _acceleration;
private bool _submunitionsLaunched = false;
private Vector3 _acceleration; protected override void Update() {
private bool _submunitionsLaunched = false; base.Update();
protected override void Update() { // Check if it's time to launch submunitions
if (!_submunitionsLaunched &&
(GetFlightPhase() == FlightPhase.MIDCOURSE || GetFlightPhase() == FlightPhase.BOOST) &&
SimManager.Instance.GetElapsedSimulationTime() >=
_agentConfig.submunitions_config.launch_config.launch_time) {
SpawnSubmunitions();
_submunitionsLaunched = true;
}
}
base.Update(); protected override void UpdateMidCourse(double deltaTime) {
Vector3 accelerationInput = Vector3.zero;
// Calculate and set the total acceleration
Vector3 acceleration = CalculateAcceleration(accelerationInput);
GetComponent<Rigidbody>().AddForce(acceleration, ForceMode.Acceleration);
_acceleration = acceleration;
}
// Check if it's time to launch submunitions protected override void DrawDebugVectors() {
if (!_submunitionsLaunched && (GetFlightPhase() == FlightPhase.MIDCOURSE || GetFlightPhase() == FlightPhase.BOOST) && base.DrawDebugVectors();
SimManager.Instance.GetElapsedSimulationTime() >= _agentConfig.submunitions_config.launch_config.launch_time) if (_acceleration != null) {
{ Debug.DrawRay(transform.position, _acceleration * 1f, Color.green);
SpawnSubmunitions(); }
_submunitionsLaunched = true; }
public void SpawnSubmunitions() {
Debug.Log("Spawning submunitions");
// print the callstack
Debug.Log(new System.Diagnostics.StackTrace().ToString());
List<Missile> submunitions = new List<Missile>();
switch (_agentConfig.submunitions_config.agent_config.missile_type) {
case MissileType.MICROMISSILE:
for (int i = 0; i < _agentConfig.submunitions_config.num_submunitions; i++) {
AgentConfig convertedConfig =
AgentConfig.FromSubmunitionAgentConfig(_agentConfig.submunitions_config.agent_config);
convertedConfig.initial_state.position = transform.position;
convertedConfig.initial_state.velocity = GetComponent<Rigidbody>().velocity;
Missile submunition = SimManager.Instance.CreateMissile(convertedConfig);
submunitions.Add(submunition);
Debug.Log("Created submunition");
} }
break;
} }
SimManager.Instance.AssignMissilesToTargets(submunitions);
protected override void UpdateMidCourse(double deltaTime) }
{
Vector3 accelerationInput = Vector3.zero;
// Calculate and set the total acceleration
Vector3 acceleration = CalculateAcceleration(accelerationInput);
GetComponent<Rigidbody>().AddForce(acceleration, ForceMode.Acceleration);
_acceleration = acceleration;
}
protected override void DrawDebugVectors()
{
base.DrawDebugVectors();
if (_acceleration != null)
{
Debug.DrawRay(transform.position, _acceleration * 1f, Color.green);
}
}
public void SpawnSubmunitions() {
Debug.Log("Spawning submunitions");
// print the callstack
Debug.Log(new System.Diagnostics.StackTrace().ToString());
List<Missile> submunitions = new List<Missile>();
switch(_agentConfig.submunitions_config.agent_config.missile_type) {
case MissileType.MICROMISSILE:
for (int i = 0; i < _agentConfig.submunitions_config.num_submunitions; i++) {
AgentConfig convertedConfig = AgentConfig.FromSubmunitionAgentConfig(_agentConfig.submunitions_config.agent_config);
convertedConfig.initial_state.position = transform.position;
convertedConfig.initial_state.velocity = GetComponent<Rigidbody>().velocity;
Missile submunition = SimManager.Instance.CreateMissile(convertedConfig);
submunitions.Add(submunition);
Debug.Log("Created submunition");
}
break;
}
SimManager.Instance.AssignMissilesToTargets(submunitions);
}
} }

View File

@ -2,84 +2,77 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine; using UnityEngine;
public class Micromissile : Missile public class Micromissile : Missile {
{ [SerializeField]
[SerializeField] private float _navigationGain = 5f; // Typically 3-5 private float _navigationGain = 5f; // Typically 3-5
private SensorOutput _sensorOutput; private SensorOutput _sensorOutput;
private Vector3 _accelerationCommand; private Vector3 _accelerationCommand;
private double _elapsedTime = 0; private double _elapsedTime = 0;
protected override void UpdateMidCourse(double deltaTime) protected override void UpdateMidCourse(double deltaTime) {
{ _elapsedTime += deltaTime;
_elapsedTime += deltaTime; Vector3 accelerationInput = Vector3.zero;
Vector3 accelerationInput = Vector3.zero; if (HasAssignedTarget()) {
if (HasAssignedTarget()) // Update the target model (assuming we have a target model)
{ // TODO: Implement target model update logic
// Update the target model (assuming we have a target model)
// TODO: Implement target model update logic
// Correct the state of the target model at the sensor frequency // Correct the state of the target model at the sensor frequency
float sensorUpdatePeriod = 1f / _agentConfig.dynamic_config.sensor_config.frequency; float sensorUpdatePeriod = 1f / _agentConfig.dynamic_config.sensor_config.frequency;
if (_elapsedTime >= sensorUpdatePeriod) if (_elapsedTime >= sensorUpdatePeriod) {
{ // TODO: Implement guidance filter to estimate state from sensor output
// TODO: Implement guidance filter to estimate state from sensor output // For now, we'll use the target's actual state
// For now, we'll use the target's actual state _sensorOutput = GetComponent<Sensor>().Sense(_target);
_sensorOutput = GetComponent<Sensor>().Sense(_target); _elapsedTime = 0;
_elapsedTime = 0; }
}
// Check whether the target should be considered a miss // Check whether the target should be considered a miss
SensorOutput sensorOutput = GetComponent<Sensor>().Sense(_target); SensorOutput sensorOutput = GetComponent<Sensor>().Sense(_target);
if(sensorOutput.velocity.range > 1000f) { if (sensorOutput.velocity.range > 1000f) {
this.MarkAsMiss(); this.MarkAsMiss();
} }
// Calculate the acceleration input // Calculate the acceleration input
accelerationInput = CalculateAccelerationCommand(_sensorOutput); accelerationInput = CalculateAccelerationCommand(_sensorOutput);
}
// Calculate and set the total acceleration
Vector3 acceleration = CalculateAcceleration(accelerationInput, compensateForGravity: true);
GetComponent<Rigidbody>().AddForce(acceleration, ForceMode.Acceleration);
}
private Vector3 CalculateAccelerationCommand(SensorOutput sensorOutput)
{
// Implement Proportional Navigation guidance law
Vector3 accelerationCommand = Vector3.zero;
// Extract relevant information from sensor output
float los_rate_az = sensorOutput.velocity.azimuth;
float los_rate_el = sensorOutput.velocity.elevation;
float closing_velocity = -sensorOutput.velocity.range; // Negative because closing velocity is opposite to range rate
// Navigation gain (adjust as needed)
float N = _navigationGain;
// Calculate acceleration commands in azimuth and elevation planes
float acc_az = N * closing_velocity * los_rate_az;
float acc_el = N * closing_velocity * los_rate_el;
// Convert acceleration commands to missile body frame
accelerationCommand = transform.right * acc_az + transform.up * acc_el;
// Clamp the acceleration command to the maximum acceleration
float maxAcceleration = CalculateMaxAcceleration();
accelerationCommand = Vector3.ClampMagnitude(accelerationCommand, maxAcceleration);
// Update the stored acceleration command for debugging
_accelerationCommand = accelerationCommand;
return accelerationCommand;
} }
protected override void DrawDebugVectors() // Calculate and set the total acceleration
{ Vector3 acceleration = CalculateAcceleration(accelerationInput, compensateForGravity: true);
base.DrawDebugVectors(); GetComponent<Rigidbody>().AddForce(acceleration, ForceMode.Acceleration);
if (_accelerationCommand != null) }
{ private Vector3 CalculateAccelerationCommand(SensorOutput sensorOutput) {
Debug.DrawRay(transform.position, _accelerationCommand * 1f, Color.green); // Implement Proportional Navigation guidance law
} Vector3 accelerationCommand = Vector3.zero;
}
// Extract relevant information from sensor output
float los_rate_az = sensorOutput.velocity.azimuth;
float los_rate_el = sensorOutput.velocity.elevation;
float closing_velocity =
-sensorOutput.velocity
.range; // Negative because closing velocity is opposite to range rate
// Navigation gain (adjust as needed)
float N = _navigationGain;
// Calculate acceleration commands in azimuth and elevation planes
float acc_az = N * closing_velocity * los_rate_az;
float acc_el = N * closing_velocity * los_rate_el;
// Convert acceleration commands to missile body frame
accelerationCommand = transform.right * acc_az + transform.up * acc_el;
// Clamp the acceleration command to the maximum acceleration
float maxAcceleration = CalculateMaxAcceleration();
accelerationCommand = Vector3.ClampMagnitude(accelerationCommand, maxAcceleration);
// Update the stored acceleration command for debugging
_accelerationCommand = accelerationCommand;
return accelerationCommand;
}
protected override void DrawDebugVectors() {
base.DrawDebugVectors();
if (_accelerationCommand != null) {
Debug.DrawRay(transform.position, _accelerationCommand * 1f, Color.green);
}
}
} }

View File

@ -2,171 +2,156 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine; using UnityEngine;
public class Missile : Agent public class Missile : Agent {
{ [SerializeField]
protected bool _showDebugVectors = true;
[SerializeField] protected bool _showDebugVectors = true; [SerializeField]
private Vector3 _boostAcceleration;
// Return whether a target can be assigned to the missile.
public override bool IsAssignable() {
bool assignable = !HasLaunched() && !HasAssignedTarget();
return assignable;
}
[SerializeField] // Assign the given target to the missile.
private Vector3 _boostAcceleration; public override void AssignTarget(Agent target) {
// Return whether a target can be assigned to the missile. base.AssignTarget(target);
public override bool IsAssignable() { }
bool assignable = !HasLaunched() && !HasAssignedTarget();
return assignable; // Unassign the target from the missile.
public override void UnassignTarget() {
base.UnassignTarget();
}
protected override void UpdateReady(double deltaTime) {
Vector3 accelerationInput = Vector3.zero;
Vector3 acceleration = CalculateAcceleration(accelerationInput);
// GetComponent<Rigidbody>().AddForce(acceleration, ForceMode.Acceleration);
}
protected override void Update() {
base.Update();
if (_showDebugVectors) {
DrawDebugVectors();
}
}
protected override void UpdateBoost(double deltaTime) {
// The missile only accelerates along its roll axis (forward in Unity)
Vector3 rollAxis = transform.forward;
// Calculate boost acceleration
float boostAcceleration =
(float)(StaticConfig.boostConfig.boostAcceleration * Constants.kGravity);
Vector3 accelerationInput = boostAcceleration * rollAxis;
// Calculate the total acceleration
Vector3 acceleration = CalculateAcceleration(accelerationInput);
// Apply the acceleration force
GetComponent<Rigidbody>().AddForce(acceleration, ForceMode.Acceleration);
_boostAcceleration = acceleration;
}
protected override void UpdateMidCourse(double deltaTime) {}
protected Vector3 CalculateAcceleration(Vector3 accelerationInput,
bool compensateForGravity = false) {
Vector3 gravity = Physics.gravity;
if (compensateForGravity) {
Vector3 gravityProjection = CalculateGravityProjectionOnPitchAndYaw();
accelerationInput -= gravityProjection;
} }
// Assign the given target to the missile. float airDrag = CalculateDrag();
public override void AssignTarget(Agent target) { float liftInducedDrag = CalculateLiftInducedDrag(accelerationInput);
base.AssignTarget(target); float dragAcceleration = -(airDrag + liftInducedDrag);
// Project the drag acceleration onto the forward direction
Vector3 dragAccelerationAlongRoll = dragAcceleration * transform.forward;
return accelerationInput + gravity + dragAccelerationAlongRoll;
}
private void OnTriggerEnter(Collider other) {
if (other.gameObject.name == "Floor") {
this.MarkAsMiss();
} }
// Check if the collision is with another Agent
Agent otherAgent = other.gameObject.GetComponentInParent<Agent>();
if (otherAgent != null && otherAgent.GetComponent<Target>() != null) {
// Check kill probability before marking as hit
float killProbability = StaticConfig.hitConfig.killProbability;
GameObject markerObject = Instantiate(Resources.Load<GameObject>("Prefabs/HitMarkerPrefab"),
transform.position, Quaternion.identity);
if (Random.value <= killProbability) {
// Set green for hit
markerObject.GetComponent<Renderer>().material.color = new Color(0, 1, 0, 0.15f);
// Mark both this agent and the other agent as hit
this.MarkAsHit();
otherAgent.MarkAsHit();
} else {
// Unassign the target from the missile. // Set red for miss
public override void UnassignTarget() { markerObject.GetComponent<Renderer>().material.color = new Color(1, 0, 0, 0.15f);
base.UnassignTarget(); this.MarkAsMiss();
// otherAgent.MarkAsMiss();
}
} }
}
protected override void UpdateReady(double deltaTime) { protected float CalculateMaxAcceleration() {
Vector3 accelerationInput = Vector3.zero; float maxReferenceAcceleration =
Vector3 acceleration = CalculateAcceleration(accelerationInput); (float)(StaticConfig.accelerationConfig.maxReferenceAcceleration * Constants.kGravity);
//GetComponent<Rigidbody>().AddForce(acceleration, ForceMode.Acceleration); float referenceSpeed = StaticConfig.accelerationConfig.referenceSpeed;
} return Mathf.Pow(GetComponent<Rigidbody>().velocity.magnitude / referenceSpeed, 2) *
maxReferenceAcceleration;
protected override void Update() { }
base.Update(); protected Vector3 CalculateGravityProjectionOnPitchAndYaw() {
if(_showDebugVectors) { Vector3 gravity = Physics.gravity;
DrawDebugVectors(); Vector3 pitchAxis = transform.right;
} Vector3 yawAxis = transform.up;
}
// Project the gravity onto the pitch and yaw axes
protected override void UpdateBoost(double deltaTime) float gravityProjectionPitchCoefficient = Vector3.Dot(gravity, pitchAxis);
{ float gravityProjectionYawCoefficient = Vector3.Dot(gravity, yawAxis);
// The missile only accelerates along its roll axis (forward in Unity)
Vector3 rollAxis = transform.forward; // Return the sum of the projections
return gravityProjectionPitchCoefficient * pitchAxis +
// Calculate boost acceleration gravityProjectionYawCoefficient * yawAxis;
float boostAcceleration = (float)(StaticConfig.boostConfig.boostAcceleration * Constants.kGravity); }
Vector3 accelerationInput = boostAcceleration * rollAxis;
private float CalculateDrag() {
// Calculate the total acceleration float dragCoefficient = StaticConfig.liftDragConfig.dragCoefficient;
Vector3 acceleration = CalculateAcceleration(accelerationInput); float crossSectionalArea = StaticConfig.bodyConfig.crossSectionalArea;
float mass = StaticConfig.bodyConfig.mass;
// Apply the acceleration force float dynamicPressure = (float)GetDynamicPressure();
GetComponent<Rigidbody>().AddForce(acceleration, ForceMode.Acceleration); float dragForce = dragCoefficient * dynamicPressure * crossSectionalArea;
_boostAcceleration = acceleration; return dragForce / mass;
} }
protected override void UpdateMidCourse(double deltaTime) {
private float CalculateLiftInducedDrag(Vector3 accelerationInput) {
} float liftAcceleration = Vector3.Dot(accelerationInput, transform.forward);
float liftDragRatio = StaticConfig.liftDragConfig.liftDragRatio;
protected Vector3 CalculateAcceleration(Vector3 accelerationInput, bool compensateForGravity = false) return Mathf.Abs(liftAcceleration / liftDragRatio);
{ }
Vector3 gravity = Physics.gravity;
if (compensateForGravity) protected virtual void DrawDebugVectors() {
{ if (_target != null) {
Vector3 gravityProjection = CalculateGravityProjectionOnPitchAndYaw(); // Line of sight
accelerationInput -= gravityProjection; Debug.DrawLine(transform.position, _target.transform.position, new Color(1, 1, 1, 0.15f));
}
// Velocity vector
float airDrag = CalculateDrag(); Debug.DrawRay(transform.position, GetVelocity() * 0.01f, new Color(0, 0, 1, 0.15f));
float liftInducedDrag = CalculateLiftInducedDrag(accelerationInput);
float dragAcceleration = -(airDrag + liftInducedDrag); // Current forward direction
Debug.DrawRay(transform.position, transform.forward * 5f, Color.yellow);
// Project the drag acceleration onto the forward direction
Vector3 dragAccelerationAlongRoll = dragAcceleration * transform.forward; // Pitch axis (right)
Debug.DrawRay(transform.position, transform.right * 5f, Color.red);
return accelerationInput + gravity + dragAccelerationAlongRoll;
} // Yaw axis (up)
Debug.DrawRay(transform.position, transform.up * 5f, Color.magenta);
private void OnTriggerEnter(Collider other)
{
if(other.gameObject.name == "Floor") {
this.MarkAsMiss();
}
// Check if the collision is with another Agent
Agent otherAgent = other.gameObject.GetComponentInParent<Agent>();
if (otherAgent != null && otherAgent.GetComponent<Target>() != null)
{
// Check kill probability before marking as hit
float killProbability = StaticConfig.hitConfig.killProbability;
GameObject markerObject = Instantiate(Resources.Load<GameObject>("Prefabs/HitMarkerPrefab"), transform.position, Quaternion.identity);
if (Random.value <= killProbability)
{
// Set green for hit
markerObject.GetComponent<Renderer>().material.color = new Color(0, 1, 0, 0.15f);
// Mark both this agent and the other agent as hit
this.MarkAsHit();
otherAgent.MarkAsHit();
}
else {
// Set red for miss
markerObject.GetComponent<Renderer>().material.color = new Color(1, 0, 0, 0.15f);
this.MarkAsMiss();
//otherAgent.MarkAsMiss();
}
}
}
protected float CalculateMaxAcceleration()
{
float maxReferenceAcceleration = (float)(StaticConfig.accelerationConfig.maxReferenceAcceleration * Constants.kGravity);
float referenceSpeed = StaticConfig.accelerationConfig.referenceSpeed;
return Mathf.Pow(GetComponent<Rigidbody>().velocity.magnitude / referenceSpeed, 2) * maxReferenceAcceleration;
}
protected Vector3 CalculateGravityProjectionOnPitchAndYaw()
{
Vector3 gravity = Physics.gravity;
Vector3 pitchAxis = transform.right;
Vector3 yawAxis = transform.up;
// Project the gravity onto the pitch and yaw axes
float gravityProjectionPitchCoefficient = Vector3.Dot(gravity, pitchAxis);
float gravityProjectionYawCoefficient = Vector3.Dot(gravity, yawAxis);
// Return the sum of the projections
return gravityProjectionPitchCoefficient * pitchAxis +
gravityProjectionYawCoefficient * yawAxis;
}
private float CalculateDrag()
{
float dragCoefficient = StaticConfig.liftDragConfig.dragCoefficient;
float crossSectionalArea = StaticConfig.bodyConfig.crossSectionalArea;
float mass = StaticConfig.bodyConfig.mass;
float dynamicPressure = (float)GetDynamicPressure();
float dragForce = dragCoefficient * dynamicPressure * crossSectionalArea;
return dragForce / mass;
}
private float CalculateLiftInducedDrag(Vector3 accelerationInput)
{
float liftAcceleration = Vector3.Dot(accelerationInput, transform.forward);
float liftDragRatio = StaticConfig.liftDragConfig.liftDragRatio;
return Mathf.Abs(liftAcceleration / liftDragRatio);
}
protected virtual void DrawDebugVectors()
{
if (_target != null)
{
// Line of sight
Debug.DrawLine(transform.position, _target.transform.position, new Color(1, 1, 1, 0.15f));
// Velocity vector
Debug.DrawRay(transform.position, GetVelocity()*0.01f, new Color(0, 0, 1, 0.15f));
// Current forward direction
Debug.DrawRay(transform.position, transform.forward * 5f, Color.yellow);
// Pitch axis (right)
Debug.DrawRay(transform.position, transform.right * 5f, Color.red);
// Yaw axis (up)
Debug.DrawRay(transform.position, transform.up * 5f, Color.magenta);
}
} }
}
} }

View File

@ -1,74 +1,71 @@
using UnityEngine; using UnityEngine;
public class IdealSensor : Sensor public class IdealSensor : Sensor {
{ protected override void Start() {
protected override void Start() base.Start();
{ }
base.Start();
public override SensorOutput Sense(Agent target) {
SensorOutput targetSensorOutput = new SensorOutput();
// Sense the target's position
PositionOutput targetPositionSensorOutput = SensePosition(target);
targetSensorOutput.position = targetPositionSensorOutput;
// Sense the target's velocity
VelocityOutput targetVelocitySensorOutput = SenseVelocity(target);
targetSensorOutput.velocity = targetVelocitySensorOutput;
return targetSensorOutput;
}
protected override PositionOutput SensePosition(Agent target) {
PositionOutput positionSensorOutput = new PositionOutput();
// Calculate the relative position of the target
Vector3 relativePosition = target.transform.position - transform.position;
// Calculate the distance (range) to the target
positionSensorOutput.range = relativePosition.magnitude;
// Calculate azimuth (horizontal angle relative to forward)
positionSensorOutput.azimuth =
Vector3.SignedAngle(transform.forward, relativePosition, transform.up);
// Calculate elevation (vertical angle relative to forward)
Vector3 flatRelativePosition = Vector3.ProjectOnPlane(relativePosition, transform.up);
positionSensorOutput.elevation =
Vector3.SignedAngle(flatRelativePosition, relativePosition, transform.right);
return positionSensorOutput;
}
protected override VelocityOutput SenseVelocity(Agent target) {
VelocityOutput velocitySensorOutput = new VelocityOutput();
// Calculate relative position and velocity
Vector3 relativePosition = target.transform.position - transform.position;
Vector3 relativeVelocity = target.GetVelocity() - GetComponent<Rigidbody>().velocity;
// Calculate range rate (radial velocity)
velocitySensorOutput.range = Vector3.Dot(relativeVelocity, relativePosition.normalized);
// Project relative velocity onto a plane perpendicular to relative position
Vector3 tangentialVelocity =
Vector3.ProjectOnPlane(relativeVelocity, relativePosition.normalized);
// Calculate azimuth rate
Vector3 horizontalVelocity = Vector3.ProjectOnPlane(tangentialVelocity, transform.up);
velocitySensorOutput.azimuth =
Vector3.Dot(horizontalVelocity, transform.right) / relativePosition.magnitude;
// Calculate elevation rate
Vector3 verticalVelocity = Vector3.Project(tangentialVelocity, transform.up);
velocitySensorOutput.elevation = verticalVelocity.magnitude / relativePosition.magnitude;
if (Vector3.Dot(verticalVelocity, transform.up) < 0) {
velocitySensorOutput.elevation *= -1;
} }
public override SensorOutput Sense(Agent target) return velocitySensorOutput;
{ }
SensorOutput targetSensorOutput = new SensorOutput();
// Sense the target's position
PositionOutput targetPositionSensorOutput = SensePosition(target);
targetSensorOutput.position = targetPositionSensorOutput;
// Sense the target's velocity
VelocityOutput targetVelocitySensorOutput = SenseVelocity(target);
targetSensorOutput.velocity = targetVelocitySensorOutput;
return targetSensorOutput;
}
protected override PositionOutput SensePosition(Agent target)
{
PositionOutput positionSensorOutput = new PositionOutput();
// Calculate the relative position of the target
Vector3 relativePosition = target.transform.position - transform.position;
// Calculate the distance (range) to the target
positionSensorOutput.range = relativePosition.magnitude;
// Calculate azimuth (horizontal angle relative to forward)
positionSensorOutput.azimuth = Vector3.SignedAngle(transform.forward, relativePosition, transform.up);
// Calculate elevation (vertical angle relative to forward)
Vector3 flatRelativePosition = Vector3.ProjectOnPlane(relativePosition, transform.up);
positionSensorOutput.elevation = Vector3.SignedAngle(flatRelativePosition, relativePosition, transform.right);
return positionSensorOutput;
}
protected override VelocityOutput SenseVelocity(Agent target)
{
VelocityOutput velocitySensorOutput = new VelocityOutput();
// Calculate relative position and velocity
Vector3 relativePosition = target.transform.position - transform.position;
Vector3 relativeVelocity = target.GetVelocity() - GetComponent<Rigidbody>().velocity;
// Calculate range rate (radial velocity)
velocitySensorOutput.range = Vector3.Dot(relativeVelocity, relativePosition.normalized);
// Project relative velocity onto a plane perpendicular to relative position
Vector3 tangentialVelocity = Vector3.ProjectOnPlane(relativeVelocity, relativePosition.normalized);
// Calculate azimuth rate
Vector3 horizontalVelocity = Vector3.ProjectOnPlane(tangentialVelocity, transform.up);
velocitySensorOutput.azimuth = Vector3.Dot(horizontalVelocity, transform.right) / relativePosition.magnitude;
// Calculate elevation rate
Vector3 verticalVelocity = Vector3.Project(tangentialVelocity, transform.up);
velocitySensorOutput.elevation = verticalVelocity.magnitude / relativePosition.magnitude;
if (Vector3.Dot(verticalVelocity, transform.up) < 0)
{
velocitySensorOutput.elevation *= -1;
}
return velocitySensorOutput;
}
} }

View File

@ -1,75 +1,70 @@
using UnityEngine; using UnityEngine;
public abstract class Sensor : MonoBehaviour public abstract class Sensor : MonoBehaviour {
{ protected Agent _agent;
protected Agent _agent;
protected virtual void Start() protected virtual void Start() {
{ _agent = GetComponent<Agent>();
_agent = GetComponent<Agent>(); }
}
/// <summary> /// <summary>
/// Main sensing method to gather information about a target agent. /// Main sensing method to gather information about a target agent.
/// </summary> /// </summary>
/// <param name="target">The agent to sense.</param> /// <param name="target">The agent to sense.</param>
/// <returns>SensorOutput containing position and velocity data.</returns> /// <returns>SensorOutput containing position and velocity data.</returns>
/// <remarks> /// <remarks>
/// Implementers should: /// Implementers should:
/// 1. Call SensePosition to get position data. /// 1. Call SensePosition to get position data.
/// 2. Call SenseVelocity to get velocity data. /// 2. Call SenseVelocity to get velocity data.
/// 3. Combine results into a SensorOutput struct. /// 3. Combine results into a SensorOutput struct.
/// </remarks> /// </remarks>
public abstract SensorOutput Sense(Agent target); public abstract SensorOutput Sense(Agent target);
/// <summary> /// <summary>
/// Calculates the relative position of the target agent. /// Calculates the relative position of the target agent.
/// </summary> /// </summary>
/// <param name="target">The agent to sense.</param> /// <param name="target">The agent to sense.</param>
/// <returns>PositionOutput containing range, azimuth, and elevation.</returns> /// <returns>PositionOutput containing range, azimuth, and elevation.</returns>
/// <remarks> /// <remarks>
/// Implementers should calculate: /// Implementers should calculate:
/// - range: Distance to the target (in unity units). /// - range: Distance to the target (in unity units).
/// - azimuth: Horizontal angle to the target (in degrees). /// - azimuth: Horizontal angle to the target (in degrees).
/// Positive is clockwise from the forward direction. /// Positive is clockwise from the forward direction.
/// - elevation: Vertical angle to the target (in degrees). /// - elevation: Vertical angle to the target (in degrees).
/// Positive is above the horizontal plane. /// Positive is above the horizontal plane.
/// </remarks> /// </remarks>
protected abstract PositionOutput SensePosition(Agent target); protected abstract PositionOutput SensePosition(Agent target);
/// <summary> /// <summary>
/// Calculates the relative velocity of the target agent. /// Calculates the relative velocity of the target agent.
/// </summary> /// </summary>
/// <param name="target">The agent to sense.</param> /// <param name="target">The agent to sense.</param>
/// <returns>VelocityOutput containing range rate, azimuth rate, and elevation rate.</returns> /// <returns>VelocityOutput containing range rate, azimuth rate, and elevation rate.</returns>
/// <remarks> /// <remarks>
/// Implementers should calculate: /// Implementers should calculate:
/// - range: Radial velocity (closing speed) in units/second. /// - range: Radial velocity (closing speed) in units/second.
/// Positive means the target is moving away. /// Positive means the target is moving away.
/// - azimuth: Rate of change of azimuth in degrees/second. /// - azimuth: Rate of change of azimuth in degrees/second.
/// Positive means the target is moving clockwise. /// Positive means the target is moving clockwise.
/// - elevation: Rate of change of elevation in degrees/second. /// - elevation: Rate of change of elevation in degrees/second.
/// Positive means the target is moving upwards. /// Positive means the target is moving upwards.
/// </remarks> /// </remarks>
protected abstract VelocityOutput SenseVelocity(Agent target); protected abstract VelocityOutput SenseVelocity(Agent target);
} }
public struct SensorOutput public struct SensorOutput {
{ public PositionOutput position;
public PositionOutput position; public VelocityOutput velocity;
public VelocityOutput velocity;
} }
public struct PositionOutput public struct PositionOutput {
{ public float range;
public float range; public float azimuth;
public float azimuth; public float elevation;
public float elevation;
} }
public struct VelocityOutput public struct VelocityOutput {
{ public float range;
public float range; public float azimuth;
public float azimuth; public float elevation;
public float elevation;
} }

View File

@ -3,266 +3,226 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using UnityEngine; using UnityEngine;
public class SimManager : MonoBehaviour public class SimManager : MonoBehaviour {
{ // Singleton instance
public static SimManager Instance { get; private set; }
// Singleton instance [SerializeField]
public static SimManager Instance { get; private set; } public SimulationConfig simulationConfig;
private List<Missile> _missiles = new List<Missile>();
private List<Target> _unassignedTargets = new List<Target>();
private List<Target> _targets = new List<Target>();
private float _elapsedSimulationTime = 0f;
private float endTime = 100f; // Set an appropriate end time
private bool simulationRunning = false;
[SerializeField] private IAssignment _assignmentScheme;
public SimulationConfig simulationConfig;
public double GetElapsedSimulationTime() {
private List<Missile> _missiles = new List<Missile>(); return _elapsedSimulationTime;
private List<Target> _unassignedTargets = new List<Target>(); }
private List<Target> _targets = new List<Target>();
private float _elapsedSimulationTime = 0f;
private float endTime = 100f; // Set an appropriate end time
private bool simulationRunning = false;
private IAssignment _assignmentScheme; void Awake() {
// Ensure only one instance of SimManager exists
if (Instance == null) {
Instance = this;
DontDestroyOnLoad(gameObject);
} else {
Destroy(gameObject);
}
}
public double GetElapsedSimulationTime() void Start() {
{ // Slow down time by simulationConfig.timeScale
return _elapsedSimulationTime; if (Instance == this) {
Time.timeScale = simulationConfig.timeScale;
Time.fixedDeltaTime = Time.timeScale * 0.02f;
Time.maximumDeltaTime = Time.timeScale * 0.15f;
InitializeSimulation();
simulationRunning = true;
}
}
private void InitializeSimulation() {
List<Missile> missiles = new List<Missile>();
// Create missiles based on config
foreach (var swarmConfig in simulationConfig.missile_swarm_configs) {
for (int i = 0; i < swarmConfig.num_agents; i++) {
var missile = CreateMissile(swarmConfig.agent_config);
}
} }
void Awake() List<Target> targets = new List<Target>();
{ // Create targets based on config
// Ensure only one instance of SimManager exists foreach (var swarmConfig in simulationConfig.target_swarm_configs) {
if (Instance == null) for (int i = 0; i < swarmConfig.num_agents; i++) {
{ var target = CreateTarget(swarmConfig.agent_config);
Instance = this; }
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
}
} }
void Start() { _assignmentScheme = new ThreatAssignment();
// Slow down time by simulationConfig.timeScale // Perform initial assignment
if(Instance == this) { }
Time.timeScale = simulationConfig.timeScale;
Time.fixedDeltaTime = Time.timeScale * 0.02f; public void AssignMissilesToTargets() {
Time.maximumDeltaTime = Time.timeScale * 0.15f; AssignMissilesToTargets(_missiles);
InitializeSimulation(); }
simulationRunning = true;
} public void RegisterTargetMiss(Target target) {
_unassignedTargets.Add(target);
}
public void AssignMissilesToTargets(List<Missile> missilesToAssign) {
// Convert Missile and Target lists to Agent lists
List<Agent> missileAgents = new List<Agent>(missilesToAssign.ConvertAll(m => m as Agent));
// Convert Target list to Agent list, excluding already assigned targets
List<Agent> targetAgents = _unassignedTargets.ToList<Agent>();
// Perform the assignment
IEnumerable<IAssignment.AssignmentItem> assignments =
_assignmentScheme.Assign(missileAgents, targetAgents);
// Apply the assignments to the missiles
foreach (var assignment in assignments) {
if (assignment.MissileIndex < missilesToAssign.Count) {
Missile missile = missilesToAssign[assignment.MissileIndex];
Target target = _unassignedTargets[assignment.TargetIndex];
missile.AssignTarget(target);
Debug.Log($"Missile {missile.name} assigned to target {target.name}");
}
} }
// TODO this whole function should be optimized
private void InitializeSimulation() _unassignedTargets.RemoveAll(
{ target => missilesToAssign.Any(missile => missile.GetAssignedTarget() == target));
List<Missile> missiles = new List<Missile>(); }
// Create missiles based on config
foreach (var swarmConfig in simulationConfig.missile_swarm_configs)
{
for (int i = 0; i < swarmConfig.num_agents; i++) {
var missile = CreateMissile(swarmConfig.agent_config);
}
}
List<Target> targets = new List<Target>(); public Missile CreateMissile(AgentConfig config) {
// Create targets based on config string prefabName = config.missile_type switch { MissileType.HYDRA_70 => "Hydra70",
foreach (var swarmConfig in simulationConfig.target_swarm_configs) MissileType.MICROMISSILE => "Micromissile",
{ _ => "Hydra70" };
for (int i = 0; i < swarmConfig.num_agents; i++) {
var target = CreateTarget(swarmConfig.agent_config);
} GameObject missileObject = CreateAgent(config, prefabName);
} if (missileObject == null)
return null;
_assignmentScheme = new ThreatAssignment(); // Missile-specific logic
// Perform initial assignment switch (config.dynamic_config.sensor_config.type) {
case SensorType.IDEAL:
missileObject.AddComponent<IdealSensor>();
break;
default:
Debug.LogError($"Sensor type '{config.dynamic_config.sensor_config.type}' not found.");
break;
} }
public void AssignMissilesToTargets() // Missile missile = missileObject.GetComponent<Missile>();
{ // if (missile == null)
AssignMissilesToTargets(_missiles); // {
// Debug.LogError($"Missile component not found on prefab '{prefabName}'.");
// Destroy(missileObject);
// return null;
// }
// missile.SetAgentConfig(config);
_missiles.Add(missileObject.GetComponent<Missile>());
// Assign a unique and simple target ID
int missileId = _missiles.Count;
missileObject.name = $"{config.missile_type}_Missile_{missileId}";
return missileObject.GetComponent<Missile>();
}
private Target CreateTarget(AgentConfig config) {
string prefabName = config.target_type switch {
TargetType.DRONE => "DroneTarget", TargetType.MISSILE => "MissileTarget",
_ => throw new System.ArgumentException($"Unsupported target type: {config.target_type}")
};
GameObject targetObject = CreateAgent(config, prefabName);
if (targetObject == null)
return null;
// Target target = targetObject.GetComponent<Target>();
// if (target == null)
// {
// Debug.LogError($"Target component not found on prefab '{config.prefabName}'.");
// Destroy(targetObject);
// return null;
// }
// target.SetAgentConfig(config);
_targets.Add(targetObject.GetComponent<Target>());
_unassignedTargets.Add(targetObject.GetComponent<Target>());
// Assign a unique and simple target ID
int targetId = _targets.Count;
targetObject.name = $"{config.target_type}_Target_{targetId}";
return targetObject.GetComponent<Target>();
}
public GameObject CreateAgent(AgentConfig config, string prefabName) {
GameObject prefab = Resources.Load<GameObject>($"Prefabs/{prefabName}");
if (prefab == null) {
Debug.LogError($"Prefab '{prefabName}' not found in Resources/Prefabs folder.");
return null;
} }
public void RegisterTargetMiss(Target target) { Vector3 noiseOffset = Utilities.GenerateRandomNoise(config.standard_deviation.position);
_unassignedTargets.Add(target); Vector3 noisyPosition = config.initial_state.position + noiseOffset;
GameObject agentObject =
Instantiate(prefab, noisyPosition, Quaternion.Euler(config.initial_state.rotation));
Rigidbody agentRigidbody = agentObject.GetComponent<Rigidbody>();
Vector3 velocityNoise = Utilities.GenerateRandomNoise(config.standard_deviation.velocity);
Vector3 noisyVelocity = config.initial_state.velocity + velocityNoise;
agentRigidbody.velocity = noisyVelocity;
agentObject.GetComponent<Agent>().SetAgentConfig(config);
return agentObject;
}
private void RestartSimulation() {
// Reset simulation time
_elapsedSimulationTime = 0f;
simulationRunning = true;
// Clear existing missiles and targets
foreach (var missile in _missiles) {
if (missile != null) {
Destroy(missile.gameObject);
}
}
_missiles.Clear();
foreach (var target in _targets) {
if (target != null) {
Destroy(target.gameObject);
}
}
_targets.Clear();
InitializeSimulation();
}
void Update() {
// Check if all missiles have terminated
bool allMissilesTerminated = true;
foreach (var missile in _missiles) {
if (missile != null && !missile.IsHit() && !missile.IsMiss()) {
allMissilesTerminated = false;
break;
}
}
// If all missiles have terminated, restart the simulation
if (allMissilesTerminated) {
RestartSimulation();
} }
public void AssignMissilesToTargets(List<Missile> missilesToAssign) if (simulationRunning && _elapsedSimulationTime < endTime) {
{ _elapsedSimulationTime += Time.deltaTime;
} else if (_elapsedSimulationTime >= endTime) {
// Convert Missile and Target lists to Agent lists simulationRunning = false;
List<Agent> missileAgents = new List<Agent>(missilesToAssign.ConvertAll(m => m as Agent)); Debug.Log("Simulation completed.");
// Convert Target list to Agent list, excluding already assigned targets
List<Agent> targetAgents = _unassignedTargets.ToList<Agent>();
// Perform the assignment
IEnumerable<IAssignment.AssignmentItem> assignments = _assignmentScheme.Assign(missileAgents, targetAgents);
// Apply the assignments to the missiles
foreach (var assignment in assignments)
{
if (assignment.MissileIndex < missilesToAssign.Count)
{
Missile missile = missilesToAssign[assignment.MissileIndex];
Target target = _unassignedTargets[assignment.TargetIndex];
missile.AssignTarget(target);
Debug.Log($"Missile {missile.name} assigned to target {target.name}");
}
}
// TODO this whole function should be optimized
_unassignedTargets.RemoveAll(target => missilesToAssign.Any(missile => missile.GetAssignedTarget() == target));
} }
}
public Missile CreateMissile(AgentConfig config)
{
string prefabName = config.missile_type switch
{
MissileType.HYDRA_70 => "Hydra70",
MissileType.MICROMISSILE => "Micromissile",
_ => "Hydra70"
};
GameObject missileObject = CreateAgent(config, prefabName);
if (missileObject == null) return null;
// Missile-specific logic
switch(config.dynamic_config.sensor_config.type) {
case SensorType.IDEAL:
missileObject.AddComponent<IdealSensor>();
break;
default:
Debug.LogError($"Sensor type '{config.dynamic_config.sensor_config.type}' not found.");
break;
}
// Missile missile = missileObject.GetComponent<Missile>();
// if (missile == null)
// {
// Debug.LogError($"Missile component not found on prefab '{prefabName}'.");
// Destroy(missileObject);
// return null;
// }
// missile.SetAgentConfig(config);
_missiles.Add(missileObject.GetComponent<Missile>());
// Assign a unique and simple target ID
int missileId = _missiles.Count;
missileObject.name = $"{config.missile_type}_Missile_{missileId}";
return missileObject.GetComponent<Missile>();
}
private Target CreateTarget(AgentConfig config)
{
string prefabName = config.target_type switch
{
TargetType.DRONE => "DroneTarget",
TargetType.MISSILE => "MissileTarget",
_ => throw new System.ArgumentException($"Unsupported target type: {config.target_type}")
};
GameObject targetObject = CreateAgent(config, prefabName);
if (targetObject == null) return null;
// Target target = targetObject.GetComponent<Target>();
// if (target == null)
// {
// Debug.LogError($"Target component not found on prefab '{config.prefabName}'.");
// Destroy(targetObject);
// return null;
// }
// target.SetAgentConfig(config);
_targets.Add(targetObject.GetComponent<Target>());
_unassignedTargets.Add(targetObject.GetComponent<Target>());
// Assign a unique and simple target ID
int targetId = _targets.Count;
targetObject.name = $"{config.target_type}_Target_{targetId}";
return targetObject.GetComponent<Target>();
}
public GameObject CreateAgent(AgentConfig config, string prefabName)
{
GameObject prefab = Resources.Load<GameObject>($"Prefabs/{prefabName}");
if (prefab == null)
{
Debug.LogError($"Prefab '{prefabName}' not found in Resources/Prefabs folder.");
return null;
}
Vector3 noiseOffset = Utilities.GenerateRandomNoise(config.standard_deviation.position);
Vector3 noisyPosition = config.initial_state.position + noiseOffset;
GameObject agentObject = Instantiate(prefab, noisyPosition, Quaternion.Euler(config.initial_state.rotation));
Rigidbody agentRigidbody = agentObject.GetComponent<Rigidbody>();
Vector3 velocityNoise = Utilities.GenerateRandomNoise(config.standard_deviation.velocity);
Vector3 noisyVelocity = config.initial_state.velocity + velocityNoise;
agentRigidbody.velocity = noisyVelocity;
agentObject.GetComponent<Agent>().SetAgentConfig(config);
return agentObject;
}
private void RestartSimulation()
{
// Reset simulation time
_elapsedSimulationTime = 0f;
simulationRunning = true;
// Clear existing missiles and targets
foreach (var missile in _missiles)
{
if (missile != null)
{
Destroy(missile.gameObject);
}
}
_missiles.Clear();
foreach (var target in _targets)
{
if (target != null)
{
Destroy(target.gameObject);
}
}
_targets.Clear();
InitializeSimulation();
}
void Update()
{
// Check if all missiles have terminated
bool allMissilesTerminated = true;
foreach (var missile in _missiles)
{
if (missile != null && !missile.IsHit() && !missile.IsMiss())
{
allMissilesTerminated = false;
break;
}
}
// If all missiles have terminated, restart the simulation
if (allMissilesTerminated)
{
RestartSimulation();
}
if (simulationRunning && _elapsedSimulationTime < endTime)
{
_elapsedSimulationTime += Time.deltaTime;
}
else if (_elapsedSimulationTime >= endTime)
{
simulationRunning = false;
Debug.Log("Simulation completed.");
}
}
} }

View File

@ -2,29 +2,20 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine; using UnityEngine;
public class DroneTarget : Target public class DroneTarget : Target {
{ // Start is called before the first frame update
// Start is called before the first frame update protected override void Start() {
protected override void Start() base.Start();
{ }
base.Start();
}
// Update is called once per frame // Update is called once per frame
protected override void Update() protected override void Update() {
{ base.Update();
base.Update(); }
}
protected override void UpdateReady(double deltaTime) { protected override void UpdateReady(double deltaTime) {}
} protected override void UpdateBoost(double deltaTime) {}
protected override void UpdateBoost(double deltaTime) { protected override void UpdateMidCourse(double deltaTime) {}
}
protected override void UpdateMidCourse(double deltaTime) {
}
} }

View File

@ -2,17 +2,10 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine; using UnityEngine;
public class MissileTarget : MonoBehaviour public class MissileTarget : MonoBehaviour {
{ // Start is called before the first frame update
// Start is called before the first frame update void Start() {}
void Start()
{
}
// Update is called once per frame // Update is called once per frame
void Update() void Update() {}
{
}
} }

View File

@ -2,17 +2,16 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine; using UnityEngine;
public abstract class Target : Agent public abstract class Target : Agent {
{ public override bool IsAssignable() {
public override bool IsAssignable() { return false;
return false; }
}
protected override void Start() { protected override void Start() {
base.Start(); base.Start();
} }
protected override void Update() { protected override void Update() {
base.Update(); base.Update();
} }
} }

View File

@ -1,13 +1,9 @@
using UnityEngine; using UnityEngine;
public static class Utilities public static class Utilities {
{ public static Vector3 GenerateRandomNoise(Vector3 standardDeviation) {
public static Vector3 GenerateRandomNoise(Vector3 standardDeviation) return new Vector3(Random.Range(-standardDeviation.x, standardDeviation.x),
{ Random.Range(-standardDeviation.y, standardDeviation.y),
return new Vector3( Random.Range(-standardDeviation.z, standardDeviation.z));
Random.Range(-standardDeviation.x, standardDeviation.x), }
Random.Range(-standardDeviation.y, standardDeviation.y),
Random.Range(-standardDeviation.z, standardDeviation.z)
);
}
} }