Initial commit of unity project

This commit is contained in:
Daniel Lovell
2024-09-12 00:17:21 -07:00
parent 7b8fa4096a
commit 76b6037ce8
74 changed files with 5289 additions and 0 deletions

154
Assets/Scripts/Agent.cs Normal file
View File

@@ -0,0 +1,154 @@
using System.Collections;
using System.Collections.Generic;
using TMPro;
using Unity.PlasticSCM.Editor.UI;
using Unity.VisualScripting;
using UnityEngine;
public abstract class Agent : MonoBehaviour
{
public enum FlightPhase {
INITIALIZED,
READY,
BOOST,
MIDCOURSE,
TERMINAL,
TERMINATED
}
protected double _elapsedTime = 0;
[SerializeField]
protected FlightPhase _flightPhase = FlightPhase.INITIALIZED;
[SerializeField]
protected Agent _target;
protected bool _isHit = false;
protected DynamicConfig _dynamicConfig;
[SerializeField]
public StaticConfig StaticConfig;
public bool HasLaunched() {
return (_flightPhase != FlightPhase.INITIALIZED) && (_flightPhase != FlightPhase.READY);
}
public bool HasTerminated() {
return _flightPhase == FlightPhase.TERMINATED;
}
public virtual void SetAgentConfig(AgentConfig config) {
_dynamicConfig = config.dynamic_config;
}
public virtual bool IsAssignable() {
return true;
}
public virtual void AssignTarget(Agent target)
{
_target = 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;
}
// Mark the agent as having hit the target or been hit.
public void MarkAsHit() {
_isHit = true;
_flightPhase = FlightPhase.TERMINATED;
}
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()
{
_elapsedTime = 0;
_flightPhase = FlightPhase.READY;
}
// Update is called once per frame
protected virtual void Update()
{
var launch_time = _dynamicConfig.launch_config.launch_time;
var boost_time = launch_time + StaticConfig.boostConfig.boostTime;
_elapsedTime += Time.deltaTime;
if(_elapsedTime > launch_time) {
_flightPhase = FlightPhase.BOOST;
}
if(_elapsedTime > boost_time) {
_flightPhase = 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, 100f * Time.deltaTime);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a86fc0084ee738845a451b91dd4960d9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 95c1ee01149331443bb91460f4d1e3a2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 52d6188ab9d8da8498db11ceddd027bf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f532f6ee629e87643b7b75a50e83083f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,144 @@
using UnityEngine;
public class IdealSensor : Sensor
{
protected override void 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 the relative position of the target with respect to the agent
// Vector3 position = _agent.transform.position;
// Vector3 targetPosition = target.transform.position;
// Vector3 targetRelativePosition = targetPosition - position;
// // Calculate the relative velocity of the target with respect to the agent
// Vector3 velocity = _agent.GetVelocity();
// Vector3 targetVelocity = target.GetVelocity();
// Vector3 targetRelativeVelocity = targetVelocity - velocity;
// // Project the relative velocity vector onto the relative position vector
// Vector3 velocityProjectionOnRelativePosition = Vector3.Project(targetRelativeVelocity, targetRelativePosition);
// // Determine the sign of the range rate
// float rangeRateSign = Vector3.Dot(velocityProjectionOnRelativePosition, targetRelativePosition) >= 0 ? 1 : -1;
// // Calculate the range rate
// velocitySensorOutput.range = rangeRateSign * velocityProjectionOnRelativePosition.magnitude;
// // Project the relative velocity vector onto the sphere passing through the target
// Vector3 velocityProjectionOnAzimuthElevationSphere = targetRelativeVelocity - velocityProjectionOnRelativePosition;
// // The target azimuth vector is orthogonal to the relative position vector and
// // points to the starboard of the target along the azimuth-elevation sphere
// Vector3 targetAzimuth = Vector3.Cross(targetRelativePosition, _agent.transform.forward).normalized;
// // The target elevation vector is orthogonal to the relative position vector
// // and points upwards from the target along the azimuth-elevation sphere
// Vector3 targetElevation = Vector3.Cross(targetAzimuth, targetRelativePosition).normalized;
// // If the relative position vector is parallel to the yaw or pitch axis, the
// // target azimuth vector or the target elevation vector will be undefined
// if (targetAzimuth.magnitude == 0)
// {
// // In this case, we can use the right vector as the azimuth
// targetAzimuth = _agent.transform.right;
// // And recalculate the elevation vector
// targetElevation = Vector3.Cross(targetAzimuth, targetRelativePosition).normalized;
// }
// else if (targetElevation.magnitude == 0)
// {
// targetElevation = Vector3.Cross(targetAzimuth, targetRelativePosition);
// }
// // Project the relative velocity vector on the azimuth-elevation sphere onto the target azimuth vector
// Vector3 velocityProjectionOnTargetAzimuth = Vector3.Project(velocityProjectionOnAzimuthElevationSphere, targetAzimuth);
// // Determine the sign of the azimuth velocity
// float azimuthVelocitySign = Vector3.Dot(velocityProjectionOnTargetAzimuth, targetAzimuth) >= 0 ? 1 : -1;
// // Calculate the time derivative of the azimuth to the target
// velocitySensorOutput.azimuth = azimuthVelocitySign * velocityProjectionOnTargetAzimuth.magnitude / targetRelativePosition.magnitude;
// // Project the velocity vector on the azimuth-elevation sphere onto the target elevation vector
// Vector3 velocityProjectionOnTargetElevation = velocityProjectionOnAzimuthElevationSphere - velocityProjectionOnTargetAzimuth;
// // Determine the sign of the elevation velocity
// float elevationVelocitySign = Vector3.Dot(velocityProjectionOnTargetElevation, targetElevation) >= 0 ? 1 : -1;
// // Calculate the time derivative of the elevation to the target
// velocitySensorOutput.elevation = elevationVelocitySign * velocityProjectionOnTargetElevation.magnitude / targetRelativePosition.magnitude;
// return velocitySensorOutput;
// }
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

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f9b285afc8c96ee4f9ca22836fd105d0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,112 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Micromissile : Missile
{
[SerializeField] private float navigationGain = 10f; // Typically 3-5
[SerializeField] private bool _showDebugVectors = true;
private Vector3 _previousLOS;
private Vector3 _accelerationCommand;
private float _lastUpdateTime;
protected override void UpdateMidCourse(double deltaTime)
{
Vector3 accelerationInput = Vector3.zero;
if (HasAssignedTarget())
{
// 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
float sensorUpdatePeriod = 1f / _dynamicConfig.sensor_config.frequency;
if (_elapsedTime - _sensorUpdateTime >= 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;
}
// Sense the target
SensorOutput sensorOutput = GetComponent<Sensor>().Sense(_target);
// Check whether the target has been hit
if (_target.IsHit())
{
float killProbability = _target.GetComponent<Agent>().StaticConfig.hitConfig.killProbability;
if (Random.value < killProbability)
{
MarkAsHit();
_target.MarkAsHit();
return;
}
}
// Calculate the acceleration input
accelerationInput = CalculateAccelerationCommand(sensorOutput);
}
// Calculate and set the total acceleration
Vector3 acceleration = CalculateAcceleration(accelerationInput, compensateForGravity: true);
GetComponent<Rigidbody>().AddForce(acceleration, ForceMode.Acceleration);
if (_showDebugVectors)
{
DrawDebugVectors();
}
}
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;
}
private void DrawDebugVectors()
{
if (_target != null)
{
// Line of sight
Debug.DrawLine(transform.position, _target.transform.position, Color.white);
// Velocity vector
Debug.DrawRay(transform.position, GetVelocity(), Color.blue);
// Acceleration input
Debug.DrawRay(transform.position, _accelerationCommand*1000f, 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);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 864f4855030b29d44a944391ce02342a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

130
Assets/Scripts/Missile.cs Normal file
View File

@@ -0,0 +1,130 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Missile : Agent
{
protected double _sensorUpdateTime = 0.0;
[SerializeField]
private Vector3 _boostAcceleration;
// Return whether a target can be assigned to the missile.
public override bool IsAssignable() {
bool assignable = !HasLaunched() && !HasAssignedTarget();
return assignable;
}
// Assign the given target to the missile.
public override void AssignTarget(Agent target) {
base.AssignTarget(target);
}
// 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 UpdateBoost(double deltaTime)
{
// The missile only accelerates along its roll axis (forward in Unity)
Vector3 rollAxis = transform.forward;
// Calculate boost acceleration
float boostAcceleration = StaticConfig.boostConfig.boostAcceleration * Physics.gravity.magnitude;
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 = true)
{
Vector3 gravity = Physics.gravity;
if (compensateForGravity)
{
Vector3 gravityProjection = CalculateGravityProjectionOnPitchAndYaw();
accelerationInput -= gravityProjection;
}
Vector3 airDrag = CalculateDrag();
Vector3 liftInducedDrag = CalculateLiftInducedDrag(accelerationInput);
Vector3 dragAcceleration = -(airDrag + liftInducedDrag);
// Project the drag acceleration onto the forward direction
Vector3 dragAccelerationAlongRoll = Vector3.Dot(dragAcceleration, transform.forward) * transform.forward;
return accelerationInput + gravity + dragAccelerationAlongRoll;
}
private void OnTriggerEnter(Collider other)
{
// 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;
if (Random.value <= killProbability)
{
// Mark both this agent and the other agent as hit
this.MarkAsHit();
otherAgent.MarkAsHit();
}
}
}
protected float CalculateMaxAcceleration()
{
float maxReferenceAcceleration = StaticConfig.accelerationConfig.maxReferenceAcceleration * Physics.gravity.magnitude;
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 Vector3 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 * Vector3.one;
}
private Vector3 CalculateLiftInducedDrag(Vector3 accelerationInput)
{
Vector3 principalAxes = transform.forward;
float liftAcceleration = Vector3.Dot(accelerationInput, principalAxes);
float liftDragRatio = StaticConfig.liftDragConfig.liftDragRatio;
return Mathf.Abs(liftAcceleration / liftDragRatio) * Vector3.one;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 932fd95d676a82349ab18bddf929d4bb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 16e40584b2154ef4a95c84e85a321999
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
// The round-robin assignment class assigns missiles to the targets in a
// round-robin order.
public class RoundRobinAssignment : Assignment
{
// 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<Agent> missiles, List<Agent> targets)
{
List<int> assignableMissileIndices = GetAssignableMissileIndices(missiles);
if (assignableMissileIndices.Count == 0)
{
return;
}
List<int> activeTargetIndices = GetActiveTargetIndices(targets);
if (activeTargetIndices.Count == 0)
{
return;
}
foreach (int missileIndex in assignableMissileIndices)
{
int nextActiveTargetIndex = activeTargetIndices
.FindIndex(index => index > prevTargetIndex);
if (nextActiveTargetIndex == -1)
{
nextActiveTargetIndex = 0;
}
int nextTargetIndex = activeTargetIndices[nextActiveTargetIndex];
missileToTargetAssignments.AddFirst(new AssignmentItem(missileIndex, nextTargetIndex));
prevTargetIndex = nextTargetIndex;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f668f6d7413495c4093550e492ce36bd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

37
Assets/Scripts/Sensor.cs Normal file
View File

@@ -0,0 +1,37 @@
using UnityEngine;
public abstract class Sensor : MonoBehaviour
{
protected Agent _agent;
protected virtual void Start()
{
_agent = GetComponent<Agent>();
}
public abstract SensorOutput Sense(Agent target);
protected abstract PositionOutput SensePosition(Agent target);
protected abstract VelocityOutput SenseVelocity(Agent target);
}
public struct SensorOutput
{
public PositionOutput position;
public VelocityOutput velocity;
}
public struct PositionOutput
{
public float range;
public float azimuth;
public float elevation;
}
public struct VelocityOutput
{
public float range;
public float azimuth;
public float elevation;
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 285bc14e5585a804b9bc44b10b8c3cb1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,219 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SimManager : MonoBehaviour
{
[SerializeField]
public SimulationConfig simulationConfig;
private List<Missile> missiles = new List<Missile>();
private List<Target> targets = new List<Target>();
private float currentTime = 0f;
private float endTime = 100f; // Set an appropriate end time
private bool simulationRunning = false;
private Assignment _assignment;
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;
}
private void InitializeSimulation()
{
// 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);
}
}
// 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 RoundRobinAssignment();
// Perform initial assignment
AssignMissilesToTargets();
}
private void AssignMissilesToTargets()
{
// Convert Missile and Target lists to Agent lists
List<Agent> missileAgents = new List<Agent>(missiles.ConvertAll(m => m as Agent));
List<Agent> targetAgents = new List<Agent>(targets.ConvertAll(t => t as Agent));
// Perform the assignment
_assignment.Assign(missileAgents, targetAgents);
// Apply the assignments to the missiles
foreach (var assignment in _assignment.Assignments)
{
Missile missile = missiles[assignment.MissileIndex];
Target target = targets[assignment.TargetIndex];
missile.AssignTarget(target);
Debug.Log($"Missile {missile.name} assigned to target {target.name}");
}
}
private Missile CreateMissile(AgentConfig config)
{
// Load the missile prefab from Resources
GameObject missilePrefab = Resources.Load<GameObject>($"Prefabs/{config.prefabName}");
if (missilePrefab == null)
{
Debug.LogError($"Missile prefab '{config.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 missile with the noisy position
GameObject missileObject = Instantiate(missilePrefab, noisyPosition, Quaternion.Euler(config.initial_state.rotation));
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;
}
// Set initial velocity
Rigidbody missileRigidbody = missileObject.GetComponent<Rigidbody>();
// 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<Missile>();
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;
}
private Target CreateTarget(AgentConfig config)
{
// Load the target prefab from Resources
GameObject targetPrefab = Resources.Load<GameObject>($"Prefabs/{config.prefabName}");
if (targetPrefab == null)
{
Debug.LogError($"Target prefab '{config.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));
// Set initial velocity with noise
Rigidbody targetRigidbody = targetObject.GetComponent<Rigidbody>();
Vector3 velocityNoise = Utilities.GenerateRandomNoise(config.standard_deviation.velocity);
Vector3 noisyVelocity = config.initial_state.velocity + velocityNoise;
targetRigidbody.velocity = noisyVelocity;
Target target = targetObject.GetComponent<Target>();
target.SetAgentConfig(config);
if (target == null)
{
Debug.LogError($"Target component not found on prefab '{config.prefabName}'.");
Destroy(targetObject);
return null;
}
// Initialize target properties
//target.Initialize(config);
return target;
}
private void RestartSimulation()
{
// Reset simulation time
currentTime = 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())
{
allMissilesTerminated = false;
break;
}
}
// If all missiles have terminated, restart the simulation
if (allMissilesTerminated)
{
RestartSimulation();
}
if (simulationRunning && currentTime < endTime)
{
currentTime += Time.deltaTime;
}
else if (currentTime >= endTime)
{
simulationRunning = false;
Debug.Log("Simulation completed.");
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 84f2990fd69b0284ca96912cbe968b62
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 79f1fe138866d6a40b209a4edcf2ee06
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 35d9cdcf93cb04b40a7538fc87071e3b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

20
Assets/Scripts/Target.cs Normal file
View File

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

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ad412917d9c68a144b3124e83ca083c1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: caa881af3be57584a91f17ed3683fed7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: