diff --git a/Assets/Resources/Prefabs/Hydra70.prefab b/Assets/Resources/Prefabs/Hydra70.prefab new file mode 100644 index 0000000..05b5d50 --- /dev/null +++ b/Assets/Resources/Prefabs/Hydra70.prefab @@ -0,0 +1,372 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &2209441373060073351 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 371620299618260830} + - component: {fileID: 6661103048554822104} + - component: {fileID: 8798706627460794869} + m_Layer: 0 + m_Name: ConeModel + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &371620299618260830 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2209441373060073351} + serializedVersion: 2 + m_LocalRotation: {x: 0.7071068, y: 0, z: 0, w: 0.7071068} + m_LocalPosition: {x: 0, y: 0, z: -1.79} + m_LocalScale: {x: 20, y: 20, z: 20} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 644108147276659055} + m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0} +--- !u!33 &6661103048554822104 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2209441373060073351} + m_Mesh: {fileID: 4300000, guid: 36b72d5a34c22d94c88a068dfcbdf27a, type: 2} +--- !u!23 &8798706627460794869 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2209441373060073351} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: f09133e0e18241045a6efe65c0415cae, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!1 &3258882778433859900 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5422129902450564486} + - component: {fileID: 5461165653410223278} + m_Layer: 0 + m_Name: TrailRenderer + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &5422129902450564486 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3258882778433859900} + serializedVersion: 2 + m_LocalRotation: {x: 0.7071068, y: 0, z: 0, w: 0.7071068} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 10, y: 10, z: 10} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 644108147276659055} + m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0} +--- !u!96 &5461165653410223278 +TrailRenderer: + serializedVersion: 3 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3258882778433859900} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 0 + m_LightProbeUsage: 0 + m_ReflectionProbeUsage: 0 + m_RayTracingMode: 0 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 548fe8e516bc01e48a296f5b86c7aa18, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_Time: 5 + m_PreviewTimeScale: 1 + m_Parameters: + serializedVersion: 3 + widthMultiplier: 1 + widthCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + colorGradient: + serializedVersion: 2 + key0: {r: 1, g: 1, b: 1, a: 1} + key1: {r: 1, g: 1, b: 1, a: 1} + key2: {r: 0, g: 0, b: 0, a: 0} + key3: {r: 0, g: 0, b: 0, a: 0} + key4: {r: 0, g: 0, b: 0, a: 0} + key5: {r: 0, g: 0, b: 0, a: 0} + key6: {r: 0, g: 0, b: 0, a: 0} + key7: {r: 0, g: 0, b: 0, a: 0} + ctime0: 0 + ctime1: 65535 + ctime2: 0 + ctime3: 0 + ctime4: 0 + ctime5: 0 + ctime6: 0 + ctime7: 0 + atime0: 0 + atime1: 65535 + atime2: 0 + atime3: 0 + atime4: 0 + atime5: 0 + atime6: 0 + atime7: 0 + m_Mode: 0 + m_ColorSpace: -1 + m_NumColorKeys: 2 + m_NumAlphaKeys: 2 + numCornerVertices: 0 + numCapVertices: 0 + alignment: 0 + textureMode: 0 + textureScale: {x: 1, y: 1} + shadowBias: 0.5 + generateLightingData: 0 + m_MinVertexDistance: 0.1 + m_MaskInteraction: 0 + m_Autodestruct: 0 + m_Emitting: 1 + m_ApplyActiveColorSpace: 0 +--- !u!1 &4605237398022313567 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3424325125878309675} + - component: {fileID: 6110744000869714338} + m_Layer: 0 + m_Name: Collider + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &3424325125878309675 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4605237398022313567} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 644108147276659055} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!135 &6110744000869714338 +SphereCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4605237398022313567} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 1 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Radius: 10 + m_Center: {x: 0, y: 0, z: 0} +--- !u!1 &7084771228906499395 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 644108147276659055} + - component: {fileID: 8120612314902529408} + - component: {fileID: 3328180170527195603} + m_Layer: 0 + m_Name: Hydra70 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &644108147276659055 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7084771228906499395} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 26.6, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 5422129902450564486} + - {fileID: 371620299618260830} + - {fileID: 3424325125878309675} + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &8120612314902529408 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7084771228906499395} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 403a04456de34694a8946c6a5084a9cc, type: 3} + m_Name: + m_EditorClassIdentifier: + _flightPhase: 0 + _target: {fileID: 0} + StaticConfig: + accelerationConfig: + maxReferenceAcceleration: 300 + referenceSpeed: 1000 + boostConfig: + boostTime: 0.3 + boostAcceleration: 100 + liftDragConfig: + liftCoefficient: 0.2 + dragCoefficient: 1 + liftDragRatio: 5 + bodyConfig: + mass: 15.8 + crossSectionalArea: 0.004 + finArea: 0.007 + bodyArea: 0.12 + hitConfig: + hitRadius: 1 + killProbability: 0.9 + _showDebugVectors: 1 + _boostAcceleration: {x: 0, y: 0, z: 0} +--- !u!54 &3328180170527195603 +Rigidbody: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7084771228906499395} + serializedVersion: 4 + m_Mass: 1 + m_Drag: 0 + m_AngularDrag: 0 + m_CenterOfMass: {x: 0, y: 0, z: 0} + m_InertiaTensor: {x: 1, y: 1, z: 1} + m_InertiaRotation: {x: 0, y: 0, z: 0, w: 1} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ImplicitCom: 1 + m_ImplicitTensor: 1 + m_UseGravity: 0 + m_IsKinematic: 0 + m_Interpolate: 0 + m_Constraints: 0 + m_CollisionDetection: 0 diff --git a/Assets/Resources/Prefabs/Hydra70.prefab.meta b/Assets/Resources/Prefabs/Hydra70.prefab.meta new file mode 100644 index 0000000..4484501 --- /dev/null +++ b/Assets/Resources/Prefabs/Hydra70.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: c4043eb46f764b044a7f744441e1e330 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scenes/MainScene.unity b/Assets/Scenes/MainScene.unity index e214433..a0210e0 100644 --- a/Assets/Scenes/MainScene.unity +++ b/Assets/Scenes/MainScene.unity @@ -681,7 +681,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 84f2990fd69b0284ca96912cbe968b62, type: 3} m_Name: m_EditorClassIdentifier: - simulationConfig: {fileID: 11400000, guid: b7e9e8f13e8db0f47a33344c45790f20, type: 2} + simulationConfig: {fileID: 11400000, guid: 6d7c61b7d025c9b49b3410fd1195a525, type: 2} --- !u!4 &253946927 Transform: m_ObjectHideFlags: 0 diff --git a/Assets/Scripts/Agent.cs b/Assets/Scripts/Agent.cs index 15a1dfe..41638a0 100644 --- a/Assets/Scripts/Agent.cs +++ b/Assets/Scripts/Agent.cs @@ -16,20 +16,34 @@ public abstract class Agent : MonoBehaviour TERMINATED } - protected double _elapsedTime = 0; [SerializeField] - protected FlightPhase _flightPhase = FlightPhase.INITIALIZED; + private FlightPhase _flightPhase = FlightPhase.INITIALIZED; [SerializeField] protected Agent _target; protected bool _isHit = false; protected bool _isMiss = false; - protected DynamicConfig _dynamicConfig; + 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); } @@ -39,7 +53,7 @@ public abstract class Agent : MonoBehaviour } public virtual void SetAgentConfig(AgentConfig config) { - _dynamicConfig = config.dynamic_config; + _agentConfig = config; } public virtual bool IsAssignable() { @@ -118,21 +132,28 @@ public abstract class Agent : MonoBehaviour // Start is called before the first frame update protected virtual void Start() { - _elapsedTime = 0; _flightPhase = FlightPhase.READY; } // Update is called once per frame protected virtual void Update() { - var launch_time = _dynamicConfig.launch_config.launch_time; + _timeSinceLaunch += Time.deltaTime; + _timeInPhase += Time.deltaTime; + + var launch_time = _agentConfig.dynamic_config.launch_config.launch_time; var boost_time = launch_time + StaticConfig.boostConfig.boostTime; - _elapsedTime += Time.deltaTime; - if(_elapsedTime > launch_time) { - _flightPhase = FlightPhase.BOOST; + double elapsedSimulationTime = SimManager.Instance.GetElapsedSimulationTime(); + + if(_flightPhase == FlightPhase.TERMINATED) { + return; } - if(_elapsedTime > boost_time) { - _flightPhase = FlightPhase.MIDCOURSE; + + if(elapsedSimulationTime >= launch_time && _flightPhase == FlightPhase.READY) { + SetFlightPhase(FlightPhase.BOOST); + } + if(_timeSinceLaunch > boost_time && _flightPhase == FlightPhase.BOOST) { + SetFlightPhase(FlightPhase.MIDCOURSE); } AlignWithVelocity(); switch (_flightPhase) { diff --git a/Assets/Scripts/Assignment/Assignment.cs b/Assets/Scripts/Assignment/Assignment.cs index 55ee8ad..3d98bb4 100644 --- a/Assets/Scripts/Assignment/Assignment.cs +++ b/Assets/Scripts/Assignment/Assignment.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using UnityEngine; // The assignment class is an interface for assigning a target to each missile. -public abstract class Assignment +public interface IAssignment { // Assignment item type. // The first element corresponds to the missile index, and the second element @@ -21,13 +21,9 @@ public abstract class Assignment } // A list containing the missile-target assignments. - protected LinkedList missileToTargetAssignments = new LinkedList(); - - // Return the missile-target assignments. - public IEnumerable Assignments => missileToTargetAssignments; // Assign a target to each missile that has not been assigned a target yet. - public abstract void Assign(List missiles, List targets); + public abstract IEnumerable Assign(List missiles, List targets); // Get the list of assignable missile indices. protected static List GetAssignableMissileIndices(List missiles) diff --git a/Assets/Scripts/Assignment/RoundRobinAssignment.cs b/Assets/Scripts/Assignment/RoundRobinAssignment.cs index efd69d9..51bc957 100644 --- a/Assets/Scripts/Assignment/RoundRobinAssignment.cs +++ b/Assets/Scripts/Assignment/RoundRobinAssignment.cs @@ -5,24 +5,25 @@ using UnityEngine; // The round-robin assignment class assigns missiles to the targets in a // round-robin order. -public class RoundRobinAssignment : Assignment +public class RoundRobinAssignment : IAssignment { // Previous target index that was assigned. private int prevTargetIndex = -1; // Assign a target to each missile that has not been assigned a target yet. - public override void Assign(List missiles, List targets) + public IEnumerable Assign(List missiles, List targets) { - List assignableMissileIndices = GetAssignableMissileIndices(missiles); + List assignments = new List(); + List assignableMissileIndices = IAssignment.GetAssignableMissileIndices(missiles); if (assignableMissileIndices.Count == 0) { - return; + return assignments; } - List activeTargetIndices = GetActiveTargetIndices(targets); + List activeTargetIndices = IAssignment.GetActiveTargetIndices(targets); if (activeTargetIndices.Count == 0) { - return; + return assignments; } foreach (int missileIndex in assignableMissileIndices) @@ -36,8 +37,10 @@ public class RoundRobinAssignment : Assignment } int nextTargetIndex = activeTargetIndices[nextActiveTargetIndex]; - missileToTargetAssignments.AddFirst(new AssignmentItem(missileIndex, nextTargetIndex)); + assignments.Add(new IAssignment.AssignmentItem(missileIndex, nextTargetIndex)); prevTargetIndex = nextTargetIndex; } + + return assignments; } } \ No newline at end of file diff --git a/Assets/Scripts/Assignment/ThreatAssignment.cs b/Assets/Scripts/Assignment/ThreatAssignment.cs index 8469cc8..52a9c6a 100644 --- a/Assets/Scripts/Assignment/ThreatAssignment.cs +++ b/Assets/Scripts/Assignment/ThreatAssignment.cs @@ -1,44 +1,46 @@ using System; using System.Collections.Generic; using System.Linq; +using Unity.VisualScripting; using UnityEngine; // The threat assignment class assigns missiles to the targets based // on the threat level of the targets. -public class ThreatAssignment : Assignment +public class ThreatAssignment : IAssignment { // Assign a target to each missile that has not been assigned a target yet. - public override void Assign(List missiles, List targets) + public IEnumerable Assign(List missiles, List targets) { - List assignableMissileIndices = GetAssignableMissileIndices(missiles); + + List assignments = new List(); + + List assignableMissileIndices = IAssignment.GetAssignableMissileIndices(missiles); if (assignableMissileIndices.Count == 0) { - return; + return assignments; } - List activeTargetIndices = GetActiveTargetIndices(targets); + List activeTargetIndices = IAssignment.GetActiveTargetIndices(targets); if (activeTargetIndices.Count == 0) { - return; + return assignments; } - Vector3 missilesMeanPosition = CalculateMeanPosition(missiles); - List threatInfos = CalculateThreatLevels(targets, activeTargetIndices, missilesMeanPosition); + Vector3 positionToDefend = Vector3.zero; + List threatInfos = CalculateThreatLevels(targets, activeTargetIndices, positionToDefend); foreach (int missileIndex in assignableMissileIndices) { + if (missiles[missileIndex].HasAssignedTarget()) continue; if (threatInfos.Count == 0) break; ThreatInfo highestThreat = threatInfos[0]; - missileToTargetAssignments.AddFirst(new AssignmentItem(missileIndex, highestThreat.TargetIndex)); + assignments.Add(new IAssignment.AssignmentItem(missileIndex, highestThreat.TargetIndex)); threatInfos.RemoveAt(0); } + return assignments; } - private Vector3 CalculateMeanPosition(List agents) - { - return agents.Aggregate(Vector3.zero, (sum, agent) => sum + agent.transform.position) / agents.Count; - } private List CalculateThreatLevels(List targets, List activeTargetIndices, Vector3 missilesMeanPosition) { diff --git a/Assets/Scripts/Config/SimulationConfig.cs b/Assets/Scripts/Config/SimulationConfig.cs index 9fc2f93..26b4aa6 100644 --- a/Assets/Scripts/Config/SimulationConfig.cs +++ b/Assets/Scripts/Config/SimulationConfig.cs @@ -40,7 +40,22 @@ public class AgentConfig public DynamicConfig dynamic_config; public PlottingConfig plotting_config; public SubmunitionsConfig submunitions_config; - public string prefabName; + + public static AgentConfig FromSubmunitionAgentConfig(SubmunitionAgentConfig submunitionConfig) + { + return new AgentConfig + { + missile_type = submunitionConfig.missile_type, + initial_state = submunitionConfig.initial_state, + standard_deviation = submunitionConfig.standard_deviation, + 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 + submunitions_config = null // Or a default value if needed + }; + } } [System.Serializable] @@ -88,7 +103,6 @@ public class SubmunitionAgentConfig public StandardDeviation standard_deviation; public DynamicConfig dynamic_config; public PlottingConfig plotting_config; - public string prefabName; } [System.Serializable] @@ -115,7 +129,8 @@ public enum MissileType public enum TargetType { - DRONE + DRONE, + MISSILE } public enum ConfigColor diff --git a/Assets/Scripts/Interceptors/Hydra70.cs b/Assets/Scripts/Interceptors/Hydra70.cs new file mode 100644 index 0000000..c8abcd8 --- /dev/null +++ b/Assets/Scripts/Interceptors/Hydra70.cs @@ -0,0 +1,65 @@ +using System.Collections; +using System.Collections.Generic; +using JetBrains.Annotations; +using UnityEngine; + +public class Hydra70 : Missile +{ + + private Vector3 _acceleration; + private bool _submunitionsLaunched = false; + + protected override void Update() { + + base.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; + } + } + + protected override void UpdateMidCourse(double deltaTime) + { + Vector3 accelerationInput = Vector3.zero; + // Calculate and set the total acceleration + Vector3 acceleration = CalculateAcceleration(accelerationInput, compensateForGravity: true); + GetComponent().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 submunitions = new List(); + 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().velocity; + Missile submunition = SimManager.Instance.CreateMissile(convertedConfig); + submunitions.Add(submunition); + Debug.Log("Created submunition"); + } + break; + } + SimManager.Instance.AssignMissilesToTargets(submunitions); + + } + +} diff --git a/Assets/Scripts/Interceptors/Hydra70.cs.meta b/Assets/Scripts/Interceptors/Hydra70.cs.meta new file mode 100644 index 0000000..555ebe5 --- /dev/null +++ b/Assets/Scripts/Interceptors/Hydra70.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 403a04456de34694a8946c6a5084a9cc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Interceptors/Micromissile.cs b/Assets/Scripts/Interceptors/Micromissile.cs index 7758fa4..9062ab6 100644 --- a/Assets/Scripts/Interceptors/Micromissile.cs +++ b/Assets/Scripts/Interceptors/Micromissile.cs @@ -4,14 +4,15 @@ using UnityEngine; public class Micromissile : Missile { - [SerializeField] private float navigationGain = 5f; // Typically 3-5 - [SerializeField] private bool _showDebugVectors = true; + [SerializeField] private float _navigationGain = 5f; // Typically 3-5 private Vector3 _previousLOS; private Vector3 _accelerationCommand; private float _lastUpdateTime; + private double _elapsedTime = 0; protected override void UpdateMidCourse(double deltaTime) { + _elapsedTime += deltaTime; Vector3 accelerationInput = Vector3.zero; if (HasAssignedTarget()) { @@ -19,13 +20,14 @@ public class Micromissile : Missile // TODO: Implement target model update logic // Correct the state of the target model at the sensor frequency - float sensorUpdatePeriod = 1f / _dynamicConfig.sensor_config.frequency; - if (_elapsedTime - _sensorUpdateTime >= sensorUpdatePeriod) + float sensorUpdatePeriod = 1f / _agentConfig.dynamic_config.sensor_config.frequency; + if (_elapsedTime - _lastUpdateTime >= sensorUpdatePeriod) { // TODO: Implement guidance filter to estimate state from sensor output // For now, we'll use the target's actual state // targetModel.SetState(_target.GetState()); - _sensorUpdateTime = (float)_elapsedTime; + _lastUpdateTime = (float)_elapsedTime; + _elapsedTime = 0; } // Sense the target @@ -43,11 +45,6 @@ public class Micromissile : Missile Vector3 acceleration = CalculateAcceleration(accelerationInput, compensateForGravity: true); GetComponent().AddForce(acceleration, ForceMode.Acceleration); - if (_showDebugVectors) - { - DrawDebugVectors(); - } - } private Vector3 CalculateAccelerationCommand(SensorOutput sensorOutput) @@ -61,7 +58,7 @@ public class Micromissile : Missile float closing_velocity = -sensorOutput.velocity.range; // Negative because closing velocity is opposite to range rate // Navigation gain (adjust as needed) - float N = navigationGain; + float N = _navigationGain; // Calculate acceleration commands in azimuth and elevation planes float acc_az = N * closing_velocity * los_rate_az; @@ -78,27 +75,14 @@ public class Micromissile : Missile _accelerationCommand = accelerationCommand; return accelerationCommand; } - private void DrawDebugVectors() + + protected override void DrawDebugVectors() { - if (_target != null) + base.DrawDebugVectors(); + if (_accelerationCommand != 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)); - - // Acceleration input - Debug.DrawRay(transform.position, _accelerationCommand*1f, Color.green); - - // 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); + Debug.DrawRay(transform.position, _accelerationCommand * 1f, Color.green); } } + } diff --git a/Assets/Scripts/Missile.cs b/Assets/Scripts/Missile.cs index e1fc342..8e1b68b 100644 --- a/Assets/Scripts/Missile.cs +++ b/Assets/Scripts/Missile.cs @@ -5,7 +5,7 @@ using UnityEngine; public class Missile : Agent { - protected double _sensorUpdateTime = 0.0; + [SerializeField] protected bool _showDebugVectors = true; [SerializeField] private Vector3 _boostAcceleration; @@ -32,6 +32,13 @@ public class Missile : Agent GetComponent().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) @@ -142,4 +149,25 @@ public class Missile : Agent return Mathf.Abs(liftAcceleration / liftDragRatio) * Vector3.one; } + + 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); + } + } } diff --git a/Assets/Scripts/SimManager.cs b/Assets/Scripts/SimManager.cs index 998d3f9..08777ae 100644 --- a/Assets/Scripts/SimManager.cs +++ b/Assets/Scripts/SimManager.cs @@ -1,92 +1,126 @@ using System.Collections; using System.Collections.Generic; +using System.Linq; using UnityEngine; public class SimManager : MonoBehaviour { + + // Singleton instance + public static SimManager Instance { get; private set; } + + [SerializeField] public SimulationConfig simulationConfig; - private List missiles = new List(); - private List targets = new List(); - private float currentTime = 0f; + private List _missiles = new List(); + private HashSet _unassignedTargets = new HashSet(); + private HashSet _targets = new HashSet(); + private float _elapsedSimulationTime = 0f; private float endTime = 100f; // Set an appropriate end time private bool simulationRunning = false; - private Assignment _assignment; + private IAssignment _assignmentScheme; + public double GetElapsedSimulationTime() + { + return _elapsedSimulationTime; + } + + void Awake() + { + // Ensure only one instance of SimManager exists + if (Instance == null) + { + Instance = this; + DontDestroyOnLoad(gameObject); + } + else + { + Destroy(gameObject); + } + } void Start() { // Slow down time by simulationConfig.timeScale - Time.timeScale = simulationConfig.timeScale; - Time.fixedDeltaTime = Time.timeScale * 0.02f; - Time.maximumDeltaTime = Time.timeScale * 0.15f; - InitializeSimulation(); - simulationRunning = true; + 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 missiles = new List(); // 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); - missiles.Add(missile); } } + List targets = new List(); // Create targets based on config foreach (var swarmConfig in simulationConfig.target_swarm_configs) { for (int i = 0; i < swarmConfig.num_agents; i++) { var target = CreateTarget(swarmConfig.agent_config); - targets.Add(target); + } } - _assignment = new ThreatAssignment(); + _assignmentScheme = new ThreatAssignment(); // Perform initial assignment - AssignMissilesToTargets(); } - private void AssignMissilesToTargets() + public void AssignMissilesToTargets() { - // Convert Missile and Target lists to Agent lists - List missileAgents = new List(missiles.ConvertAll(m => m as Agent)); - List targetAgents = new List(targets.ConvertAll(t => t as Agent)); + AssignMissilesToTargets(_missiles); + } + public void AssignMissilesToTargets(List missilesToAssign) + { + + // Convert Missile and Target lists to Agent lists + List missileAgents = new List(missilesToAssign.ConvertAll(m => m as Agent)); + // Convert Target list to Agent list, excluding already assigned targets + List targetAgents = _unassignedTargets.ToList(); + // Perform the assignment - _assignment.Assign(missileAgents, targetAgents); + IEnumerable assignments = _assignmentScheme.Assign(missileAgents, targetAgents); // Apply the assignments to the missiles - foreach (var assignment in _assignment.Assignments) + foreach (var assignment in assignments) { - Missile missile = missiles[assignment.MissileIndex]; - Target target = targets[assignment.TargetIndex]; - missile.AssignTarget(target); - Debug.Log($"Missile {missile.name} assigned to target {target.name}"); + if (assignment.MissileIndex < missilesToAssign.Count) + { + Missile missile = missilesToAssign[assignment.MissileIndex]; + Target target = _targets.ElementAt(assignment.TargetIndex); + missile.AssignTarget(target); + Debug.Log($"Missile {missile.name} assigned to target {target.name}"); + _unassignedTargets.Remove(target); + } } } - private Missile CreateMissile(AgentConfig config) + public Missile CreateMissile(AgentConfig config) { - // Load the missile prefab from Resources - GameObject missilePrefab = Resources.Load($"Prefabs/{config.prefabName}"); - if (missilePrefab == null) + string prefabName = config.missile_type switch { - Debug.LogError($"Missile prefab '{config.prefabName}' not found in Resources/Prefabs folder."); - return null; - } + MissileType.HYDRA_70 => "Hydra70", + MissileType.MICROMISSILE => "Micromissile", + _ => "Hydra70" + }; - // Apply noise to the initial position - Vector3 noiseOffset = Utilities.GenerateRandomNoise(config.standard_deviation.position); - Vector3 noisyPosition = config.initial_state.position + noiseOffset; - - // Instantiate the missile with the noisy position - GameObject missileObject = Instantiate(missilePrefab, noisyPosition, Quaternion.Euler(config.initial_state.rotation)); + 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(); @@ -96,91 +130,100 @@ public class SimManager : MonoBehaviour break; } - // Set initial velocity - Rigidbody missileRigidbody = missileObject.GetComponent(); - // Apply noise to the initial velocity - Vector3 velocityNoise = Utilities.GenerateRandomNoise(config.standard_deviation.velocity); - Vector3 noisyVelocity = config.initial_state.velocity + velocityNoise; - missileRigidbody.velocity = noisyVelocity; + // Missile missile = missileObject.GetComponent(); + // if (missile == null) + // { + // Debug.LogError($"Missile component not found on prefab '{prefabName}'."); + // Destroy(missileObject); + // return null; + // } - - Missile missile = missileObject.GetComponent(); - missile.SetAgentConfig(config); - if (missile == null) - { - Debug.LogError($"Missile component not found on prefab '{config.prefabName}'."); - Destroy(missileObject); - return null; - } - - // Initialize missile properties - //missile.Initialize(config); - - return missile; + // missile.SetAgentConfig(config); + _missiles.Add(missileObject.GetComponent()); + // Assign a unique and simple target ID + int missileId = _missiles.Count; + missileObject.name = $"{config.missile_type}_Missile_{missileId}"; + return missileObject.GetComponent(); } + private Target CreateTarget(AgentConfig config) { - // Load the target prefab from Resources - GameObject targetPrefab = Resources.Load($"Prefabs/{config.prefabName}"); - if (targetPrefab == null) + string prefabName = config.target_type switch { - Debug.LogError($"Target prefab '{config.prefabName}' not found in Resources/Prefabs folder."); + 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(); + // if (target == null) + // { + // Debug.LogError($"Target component not found on prefab '{config.prefabName}'."); + // Destroy(targetObject); + // return null; + // } + + // target.SetAgentConfig(config); + _targets.Add(targetObject.GetComponent()); + _unassignedTargets.Add(targetObject.GetComponent()); + // Assign a unique and simple target ID + int targetId = _targets.Count; + targetObject.name = $"{config.target_type}_Target_{targetId}"; + return targetObject.GetComponent(); + } + + public GameObject CreateAgent(AgentConfig config, string prefabName) + { + GameObject prefab = Resources.Load($"Prefabs/{prefabName}"); + if (prefab == null) + { + Debug.LogError($"Prefab '{prefabName}' not found in Resources/Prefabs folder."); return null; } - // Apply noise to the initial position Vector3 noiseOffset = Utilities.GenerateRandomNoise(config.standard_deviation.position); Vector3 noisyPosition = config.initial_state.position + noiseOffset; - // Instantiate the target with the noisy position - GameObject targetObject = Instantiate(targetPrefab, noisyPosition, Quaternion.Euler(config.initial_state.rotation)); + GameObject agentObject = Instantiate(prefab, noisyPosition, Quaternion.Euler(config.initial_state.rotation)); - // Set initial velocity with noise - Rigidbody targetRigidbody = targetObject.GetComponent(); + Rigidbody agentRigidbody = agentObject.GetComponent(); Vector3 velocityNoise = Utilities.GenerateRandomNoise(config.standard_deviation.velocity); Vector3 noisyVelocity = config.initial_state.velocity + velocityNoise; - targetRigidbody.velocity = noisyVelocity; + agentRigidbody.velocity = noisyVelocity; - Target target = targetObject.GetComponent(); - target.SetAgentConfig(config); + agentObject.GetComponent().SetAgentConfig(config); - if (target == null) - { - Debug.LogError($"Target component not found on prefab '{config.prefabName}'."); - Destroy(targetObject); - return null; - } + return agentObject; + } - // Initialize target properties - //target.Initialize(config); - return target; - } private void RestartSimulation() { // Reset simulation time - currentTime = 0f; + _elapsedSimulationTime = 0f; simulationRunning = true; // Clear existing missiles and targets - foreach (var missile in missiles) + foreach (var missile in _missiles) { if (missile != null) { Destroy(missile.gameObject); } } - missiles.Clear(); + _missiles.Clear(); - foreach (var target in targets) + foreach (var target in _targets) { if (target != null) { Destroy(target.gameObject); } } - targets.Clear(); + _targets.Clear(); InitializeSimulation(); } @@ -189,7 +232,7 @@ public class SimManager : MonoBehaviour { // Check if all missiles have terminated bool allMissilesTerminated = true; - foreach (var missile in missiles) + foreach (var missile in _missiles) { if (missile != null && !missile.IsHit() && !missile.IsMiss()) { @@ -203,11 +246,11 @@ public class SimManager : MonoBehaviour RestartSimulation(); } - if (simulationRunning && currentTime < endTime) + if (simulationRunning && _elapsedSimulationTime < endTime) { - currentTime += Time.deltaTime; + _elapsedSimulationTime += Time.deltaTime; } - else if (currentTime >= endTime) + else if (_elapsedSimulationTime >= endTime) { simulationRunning = false; Debug.Log("Simulation completed."); diff --git a/Assets/Scripts/Targets/Target.cs b/Assets/Scripts/Targets/Target.cs index 4b9f694..0a4e225 100644 --- a/Assets/Scripts/Targets/Target.cs +++ b/Assets/Scripts/Targets/Target.cs @@ -4,8 +4,6 @@ using UnityEngine; public abstract class Target : Agent { - - public override bool IsAssignable() { return false; } diff --git a/Assets/SimulationConfigHydra70.asset b/Assets/SimulationConfigHydra70.asset new file mode 100644 index 0000000..6f4cc6e --- /dev/null +++ b/Assets/SimulationConfigHydra70.asset @@ -0,0 +1,105 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 79f1fe138866d6a40b209a4edcf2ee06, type: 3} + m_Name: SimulationConfigHydra70 + m_EditorClassIdentifier: + timeScale: 0.2 + missile_swarm_configs: + - num_agents: 10 + agent_config: + missile_type: 0 + target_type: 0 + initial_state: + position: {x: 0, y: 10, z: 0} + rotation: {x: -45, y: 0, z: 0} + velocity: {x: 0, y: 0, z: 10} + standard_deviation: + position: {x: 10, y: 10, z: 10} + velocity: {x: 0, y: 0, z: 0} + dynamic_config: + launch_config: + launch_time: 0 + sensor_config: + type: 0 + frequency: 0 + plotting_config: + color: {r: 0, g: 0, b: 0, a: 0} + linestyle: 0 + marker: 0 + submunitions_config: + num_submunitions: 10 + launch_config: + launch_time: 2 + agent_config: + missile_type: 1 + initial_state: + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0} + velocity: {x: 0, y: 0, z: 0} + standard_deviation: + position: {x: 5, y: 5, z: 5} + velocity: {x: 0, y: 0, z: 0} + dynamic_config: + launch_config: + launch_time: 0 + sensor_config: + type: 0 + frequency: 0 + plotting_config: + color: {r: 0, g: 0, b: 0, a: 0} + linestyle: 0 + marker: 0 + target_swarm_configs: + - num_agents: 200 + agent_config: + missile_type: 0 + target_type: 0 + initial_state: + position: {x: 0, y: 400, z: 3000} + rotation: {x: 90, y: 0, z: 0} + velocity: {x: 0, y: 0, z: -150} + standard_deviation: + position: {x: 500, y: 200, z: 100} + velocity: {x: 0, y: 0, z: 50} + dynamic_config: + launch_config: + launch_time: 0 + sensor_config: + type: 0 + frequency: 0 + plotting_config: + color: {r: 0, g: 0, b: 0, a: 0} + linestyle: 0 + marker: 0 + submunitions_config: + num_submunitions: 0 + launch_config: + launch_time: 0 + agent_config: + missile_type: 0 + initial_state: + position: {x: 0, y: 0, z: 0} + rotation: {x: 0, y: 0, z: 0} + velocity: {x: 0, y: 0, z: 0} + standard_deviation: + position: {x: 0, y: 0, z: 0} + velocity: {x: 0, y: 0, z: 0} + dynamic_config: + launch_config: + launch_time: 0 + sensor_config: + type: 0 + frequency: 0 + plotting_config: + color: {r: 0, g: 0, b: 0, a: 0} + linestyle: 0 + marker: 0 diff --git a/Assets/SimulationConfigHydra70.asset.meta b/Assets/SimulationConfigHydra70.asset.meta new file mode 100644 index 0000000..c88d77d --- /dev/null +++ b/Assets/SimulationConfigHydra70.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6d7c61b7d025c9b49b3410fd1195a525 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: