Format all files
parent
df4c8dfbfe
commit
9be43821ef
|
@ -2,3 +2,6 @@ BasedOnStyle: Google
|
|||
---
|
||||
Language: CSharp
|
||||
ColumnLimit: 100
|
||||
---
|
||||
Language: Json
|
||||
DisableFormat: true
|
||||
|
|
|
@ -5,194 +5,174 @@ using Unity.PlasticSCM.Editor.UI;
|
|||
using Unity.VisualScripting;
|
||||
using UnityEngine;
|
||||
|
||||
public abstract class Agent : MonoBehaviour
|
||||
{
|
||||
public enum FlightPhase {
|
||||
INITIALIZED,
|
||||
READY,
|
||||
BOOST,
|
||||
MIDCOURSE,
|
||||
TERMINAL,
|
||||
TERMINATED
|
||||
public abstract class Agent : MonoBehaviour {
|
||||
public enum FlightPhase { INITIALIZED, READY, BOOST, MIDCOURSE, TERMINAL, TERMINATED }
|
||||
|
||||
[SerializeField]
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
[SerializeField]
|
||||
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 (elapsedSimulationTime >= launch_time && _flightPhase == FlightPhase.READY) {
|
||||
SetFlightPhase(FlightPhase.BOOST);
|
||||
}
|
||||
|
||||
public FlightPhase GetFlightPhase() {
|
||||
return _flightPhase;
|
||||
if (_timeSinceLaunch > boost_time && _flightPhase == FlightPhase.BOOST) {
|
||||
SetFlightPhase(FlightPhase.MIDCOURSE);
|
||||
}
|
||||
|
||||
|
||||
public bool HasLaunched() {
|
||||
return (_flightPhase != FlightPhase.INITIALIZED) && (_flightPhase != FlightPhase.READY);
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
protected virtual void AlignWithVelocity() {
|
||||
Vector3 velocity = GetVelocity();
|
||||
if (velocity.magnitude > 0.1f) // Only align if we have significant velocity
|
||||
{
|
||||
_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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,53 +3,44 @@ using System.Collections.Generic;
|
|||
using UnityEngine;
|
||||
|
||||
// The assignment class is an interface for assigning a target to each missile.
|
||||
public interface IAssignment
|
||||
{
|
||||
// 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 interface IAssignment {
|
||||
// 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;
|
||||
}
|
||||
public AssignmentItem(int missileIndex, int targetIndex) {
|
||||
MissileIndex = missileIndex;
|
||||
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.
|
||||
public abstract IEnumerable<AssignmentItem> Assign(List<Agent> missiles, List<Agent> targets);
|
||||
// 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);
|
||||
|
||||
// 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 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;
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,42 +5,35 @@ using UnityEngine;
|
|||
|
||||
// The round-robin assignment class assigns missiles to the targets in a
|
||||
// round-robin order.
|
||||
public class RoundRobinAssignment : IAssignment
|
||||
{
|
||||
// Previous target index that was assigned.
|
||||
private int prevTargetIndex = -1;
|
||||
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 IEnumerable<IAssignment.AssignmentItem> Assign(List<Agent> missiles, List<Agent> targets)
|
||||
{
|
||||
List<IAssignment.AssignmentItem> assignments = new List<IAssignment.AssignmentItem>();
|
||||
List<int> assignableMissileIndices = IAssignment.GetAssignableMissileIndices(missiles);
|
||||
if (assignableMissileIndices.Count == 0)
|
||||
{
|
||||
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;
|
||||
// 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) {
|
||||
List<IAssignment.AssignmentItem> assignments = new List<IAssignment.AssignmentItem>();
|
||||
List<int> assignableMissileIndices = IAssignment.GetAssignableMissileIndices(missiles);
|
||||
if (assignableMissileIndices.Count == 0) {
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -6,89 +6,80 @@ using UnityEngine;
|
|||
|
||||
// The threat assignment class assigns missiles to the targets based
|
||||
// on the threat level of the targets.
|
||||
public class ThreatAssignment : IAssignment
|
||||
{
|
||||
// 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 class ThreatAssignment : IAssignment {
|
||||
// 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) {
|
||||
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)
|
||||
{
|
||||
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> assignableMissileIndices = IAssignment.GetAssignableMissileIndices(missiles);
|
||||
if (assignableMissileIndices.Count == 0) {
|
||||
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();
|
||||
List<int> activeTargetIndices = IAssignment.GetActiveTargetIndices(targets);
|
||||
if (activeTargetIndices.Count == 0) {
|
||||
return assignments;
|
||||
}
|
||||
|
||||
private class ThreatInfo
|
||||
{
|
||||
public int TargetIndex { get; }
|
||||
public float ThreatLevel { get; }
|
||||
Vector3 positionToDefend = Vector3.zero;
|
||||
List<ThreatInfo> threatInfos =
|
||||
CalculateThreatLevels(targets, activeTargetIndices, positionToDefend);
|
||||
|
||||
public ThreatInfo(int targetIndex, float threatLevel)
|
||||
{
|
||||
TargetIndex = targetIndex;
|
||||
ThreatLevel = threatLevel;
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,157 +3,117 @@ 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;
|
||||
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("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>();
|
||||
|
||||
|
||||
[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;
|
||||
public class DynamicConfig {
|
||||
public LaunchConfig launch_config;
|
||||
public SensorConfig sensor_config;
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class SwarmConfig
|
||||
{
|
||||
public int num_agents;
|
||||
public AgentConfig agent_config;
|
||||
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 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 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
|
||||
};
|
||||
}
|
||||
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]
|
||||
public class InitialState
|
||||
{
|
||||
public Vector3 position;
|
||||
public Vector3 rotation;
|
||||
public Vector3 velocity;
|
||||
public class InitialState {
|
||||
public Vector3 position;
|
||||
public Vector3 rotation;
|
||||
public Vector3 velocity;
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class StandardDeviation
|
||||
{
|
||||
public Vector3 position;
|
||||
public Vector3 velocity;
|
||||
public class StandardDeviation {
|
||||
public Vector3 position;
|
||||
public Vector3 velocity;
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class LaunchConfig
|
||||
{
|
||||
public float launch_time;
|
||||
public class LaunchConfig {
|
||||
public float launch_time;
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class PlottingConfig
|
||||
{
|
||||
public Color color;
|
||||
public LineStyle linestyle;
|
||||
public Marker marker;
|
||||
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;
|
||||
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 class SubmunitionAgentConfig {
|
||||
public MissileType missile_type;
|
||||
public InitialState initial_state;
|
||||
public StandardDeviation standard_deviation;
|
||||
public DynamicConfig dynamic_config;
|
||||
public PlottingConfig plotting_config;
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class SensorConfig
|
||||
{
|
||||
public SensorType type;
|
||||
public float frequency;
|
||||
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 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 MissileType { HYDRA_70, MICROMISSILE }
|
||||
|
||||
public enum TargetType
|
||||
{
|
||||
DRONE,
|
||||
MISSILE
|
||||
}
|
||||
public enum TargetType { DRONE, MISSILE }
|
||||
|
||||
public enum ConfigColor
|
||||
{
|
||||
BLUE,
|
||||
GREEN,
|
||||
RED
|
||||
}
|
||||
public enum ConfigColor { BLUE, GREEN, RED }
|
||||
|
||||
public enum LineStyle
|
||||
{
|
||||
DOTTED,
|
||||
SOLID
|
||||
}
|
||||
public enum LineStyle { DOTTED, SOLID }
|
||||
|
||||
public enum Marker
|
||||
{
|
||||
TRIANGLE_UP,
|
||||
TRIANGLE_DOWN,
|
||||
SQUARE
|
||||
}
|
||||
public enum Marker { TRIANGLE_UP, TRIANGLE_DOWN, SQUARE }
|
||||
|
||||
public enum SensorType
|
||||
{
|
||||
IDEAL
|
||||
}
|
||||
public enum SensorType { IDEAL }
|
|
@ -3,71 +3,65 @@ 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;
|
||||
}
|
||||
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 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 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 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;
|
||||
}
|
||||
[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("Acceleration Configuration")]
|
||||
public AccelerationConfig accelerationConfig;
|
||||
|
||||
[Header("Boost Configuration")]
|
||||
public BoostConfig boostConfig;
|
||||
[Header("Boost Configuration")]
|
||||
public BoostConfig boostConfig;
|
||||
|
||||
[Header("Lift and Drag Configuration")]
|
||||
public LiftDragConfig liftDragConfig;
|
||||
[Header("Lift and Drag Configuration")]
|
||||
public LiftDragConfig liftDragConfig;
|
||||
|
||||
[Header("Body Configuration")]
|
||||
public BodyConfig bodyConfig;
|
||||
[Header("Body Configuration")]
|
||||
public BodyConfig bodyConfig;
|
||||
|
||||
[Header("Hit Configuration")]
|
||||
public HitConfig hitConfig;
|
||||
[Header("Hit Configuration")]
|
||||
public HitConfig hitConfig;
|
||||
}
|
|
@ -1,20 +1,17 @@
|
|||
using System;
|
||||
|
||||
public static class Constants
|
||||
{
|
||||
// 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 kAirDensityScaleHeight = 10.4; // Scale height in km
|
||||
public const double kGravity = 9.80665; // Standard gravity in m/s^2
|
||||
public const double kEarthMeanRadius = 6378137; // Earth's mean radius in meters
|
||||
public static class Constants {
|
||||
// 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 kAirDensityScaleHeight = 10.4; // Scale height in km
|
||||
public const double kGravity = 9.80665; // Standard gravity in m/s^2
|
||||
public 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 CalculateAirDensityAtAltitude(double altitude) {
|
||||
return kAirDensity * Math.Exp(-altitude / (kAirDensityScaleHeight * 1000));
|
||||
}
|
||||
|
||||
public static double CalculateGravityAtAltitude(double altitude)
|
||||
{
|
||||
return kGravity * Math.Pow(kEarthMeanRadius / (kEarthMeanRadius + altitude), 2);
|
||||
}
|
||||
public static double CalculateGravityAtAltitude(double altitude) {
|
||||
return kGravity * Math.Pow(kEarthMeanRadius / (kEarthMeanRadius + altitude), 2);
|
||||
}
|
||||
}
|
|
@ -3,155 +3,140 @@ using UnityEditor;
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class GenerateCone : EditorWindow
|
||||
{
|
||||
private int sides = 16;
|
||||
private float baseRadius = 1f;
|
||||
private float height = 2f;
|
||||
public class GenerateCone : EditorWindow {
|
||||
private int sides = 16;
|
||||
private float baseRadius = 1f;
|
||||
private float height = 2f;
|
||||
|
||||
[MenuItem("GameObject/3D Object/Cone", false, 10)]
|
||||
static void CreateCone()
|
||||
{
|
||||
GameObject cone;
|
||||
GameObject selectedObject = Selection.activeGameObject;
|
||||
[MenuItem("GameObject/3D Object/Cone", false, 10)]
|
||||
static void CreateCone() {
|
||||
GameObject cone;
|
||||
GameObject selectedObject = Selection.activeGameObject;
|
||||
|
||||
if (selectedObject != null)
|
||||
{
|
||||
// Create as child of selected object
|
||||
cone = new GameObject("Cone");
|
||||
cone.transform.SetParent(selectedObject.transform, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 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;
|
||||
if (selectedObject != null) {
|
||||
// Create as child of selected object
|
||||
cone = new GameObject("Cone");
|
||||
cone.transform.SetParent(selectedObject.transform, false);
|
||||
} else {
|
||||
// Create as new root object
|
||||
cone = new GameObject("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();
|
||||
cone.AddComponent<MeshFilter>();
|
||||
cone.AddComponent<MeshRenderer>();
|
||||
Undo.RegisterCreatedObjectUndo(cone, "Create Cone");
|
||||
|
||||
// Assign the mesh to the MeshFilter
|
||||
cone.GetComponent<MeshFilter>().sharedMesh = mesh;
|
||||
cone.GetComponent<MeshRenderer>().material = new Material(Shader.Find("Standard"));
|
||||
var window = ScriptableObject.CreateInstance<GenerateCone>();
|
||||
window.GenerateConeObject(cone);
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
// Base face
|
||||
for (int i = 1; i < rimVertices - 1; i++) {
|
||||
AddTriangle(coneVerts[1], coneVerts[i + 1], coneVerts[i + 2]);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
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]);
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
// 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;
|
||||
// Recenter the cone
|
||||
Vector3 center = CalculateCenter(verts);
|
||||
for (int i = 0; i < verts.Count; i++) {
|
||||
verts[i] = verts[i] - center + apex;
|
||||
}
|
||||
|
||||
Vector3 CalculateCenter(List<Vector3> vertices)
|
||||
{
|
||||
Vector3 sum = Vector3.zero;
|
||||
foreach (Vector3 vert in vertices)
|
||||
{
|
||||
sum += vert;
|
||||
}
|
||||
return sum / vertices.Count;
|
||||
Mesh mesh = new Mesh();
|
||||
mesh.name = name;
|
||||
mesh.SetVertices(verts);
|
||||
mesh.SetTriangles(tris.ToArray(), 0);
|
||||
mesh.RecalculateNormals();
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
Vector3 CalculateCenter(List<Vector3> vertices) {
|
||||
Vector3 sum = Vector3.zero;
|
||||
foreach (Vector3 vert in vertices) {
|
||||
sum += vert;
|
||||
}
|
||||
return sum / vertices.Count;
|
||||
}
|
||||
}
|
|
@ -4,36 +4,27 @@ using System.Collections;
|
|||
using System;
|
||||
|
||||
// Integrated Air Defense System
|
||||
public class IADS : MonoBehaviour
|
||||
{
|
||||
public class IADS : MonoBehaviour {
|
||||
public enum TargetStatus { UNASSIGNED, ASSIGNED, HIT, DEGRADED, DESTROYED }
|
||||
|
||||
public enum TargetStatus {
|
||||
UNASSIGNED,
|
||||
ASSIGNED,
|
||||
HIT,
|
||||
DEGRADED,
|
||||
DESTROYED
|
||||
}
|
||||
// Look up target status by unique target ID
|
||||
public Dictionary<string, TargetStatus> _targetStatusDictionary;
|
||||
|
||||
// Look up target status by unique target ID
|
||||
public Dictionary<string, TargetStatus> _targetStatusDictionary;
|
||||
|
||||
private List<Target> _targets;
|
||||
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 event RegisterNewTargetDelegate OnRegisterNewTarget;
|
||||
public delegate void RegisterNewTargetDelegate(Target target);
|
||||
public event RegisterNewTargetDelegate OnRegisterNewTarget;
|
||||
|
||||
void Start()
|
||||
{
|
||||
_targets = new List<Target>();
|
||||
}
|
||||
void Start() {
|
||||
_targets = new List<Target>();
|
||||
}
|
||||
|
||||
public void RegisterNewTarget(Target target) {
|
||||
_targets.Add(target);
|
||||
OnRegisterNewTarget?.Invoke(target);
|
||||
}
|
||||
public void RegisterNewTarget(Target target) {
|
||||
_targets.Add(target);
|
||||
OnRegisterNewTarget?.Invoke(target);
|
||||
}
|
||||
}
|
|
@ -1,33 +1,27 @@
|
|||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class Vessel : MonoBehaviour
|
||||
{
|
||||
[SerializeField]
|
||||
private List<Missile> missileInventory = new List<Missile>();
|
||||
public class Vessel : MonoBehaviour {
|
||||
[SerializeField]
|
||||
private List<Missile> missileInventory = new List<Missile>();
|
||||
|
||||
public void AddMissile(Missile missile)
|
||||
{
|
||||
if (missile != null)
|
||||
{
|
||||
missileInventory.Add(missile);
|
||||
}
|
||||
public void AddMissile(Missile missile) {
|
||||
if (missile != null) {
|
||||
missileInventory.Add(missile);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveMissile(Missile missile)
|
||||
{
|
||||
missileInventory.Remove(missile);
|
||||
}
|
||||
public void RemoveMissile(Missile missile) {
|
||||
missileInventory.Remove(missile);
|
||||
}
|
||||
|
||||
public List<Missile> GetMissileInventory()
|
||||
{
|
||||
return new List<Missile>(missileInventory);
|
||||
}
|
||||
public List<Missile> GetMissileInventory() {
|
||||
return new List<Missile>(missileInventory);
|
||||
}
|
||||
|
||||
public int GetMissileCount()
|
||||
{
|
||||
return missileInventory.Count;
|
||||
}
|
||||
public int GetMissileCount() {
|
||||
return missileInventory.Count;
|
||||
}
|
||||
|
||||
// Additional methods can be added here as needed
|
||||
// Additional methods can be added here as needed
|
||||
}
|
||||
|
|
|
@ -3,63 +3,57 @@ using System.Collections.Generic;
|
|||
using JetBrains.Annotations;
|
||||
using UnityEngine;
|
||||
|
||||
public class Hydra70 : Missile
|
||||
{
|
||||
public class Hydra70 : Missile {
|
||||
private Vector3 _acceleration;
|
||||
private bool _submunitionsLaunched = false;
|
||||
|
||||
private Vector3 _acceleration;
|
||||
private bool _submunitionsLaunched = false;
|
||||
protected override void Update() {
|
||||
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
|
||||
if (!_submunitionsLaunched && (GetFlightPhase() == FlightPhase.MIDCOURSE || GetFlightPhase() == FlightPhase.BOOST) &&
|
||||
SimManager.Instance.GetElapsedSimulationTime() >= _agentConfig.submunitions_config.launch_config.launch_time)
|
||||
{
|
||||
SpawnSubmunitions();
|
||||
_submunitionsLaunched = true;
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
SimManager.Instance.AssignMissilesToTargets(submunitions);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,84 +2,77 @@ using System.Collections;
|
|||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class Micromissile : Missile
|
||||
{
|
||||
[SerializeField] private float _navigationGain = 5f; // Typically 3-5
|
||||
public class Micromissile : Missile {
|
||||
[SerializeField]
|
||||
private float _navigationGain = 5f; // Typically 3-5
|
||||
|
||||
private SensorOutput _sensorOutput;
|
||||
private Vector3 _accelerationCommand;
|
||||
private double _elapsedTime = 0;
|
||||
protected override void UpdateMidCourse(double deltaTime)
|
||||
{
|
||||
_elapsedTime += deltaTime;
|
||||
Vector3 accelerationInput = Vector3.zero;
|
||||
if (HasAssignedTarget())
|
||||
{
|
||||
// Update the target model (assuming we have a target model)
|
||||
// TODO: Implement target model update logic
|
||||
private SensorOutput _sensorOutput;
|
||||
private Vector3 _accelerationCommand;
|
||||
private double _elapsedTime = 0;
|
||||
protected override void UpdateMidCourse(double deltaTime) {
|
||||
_elapsedTime += 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 / _agentConfig.dynamic_config.sensor_config.frequency;
|
||||
if (_elapsedTime >= sensorUpdatePeriod)
|
||||
{
|
||||
// TODO: Implement guidance filter to estimate state from sensor output
|
||||
// For now, we'll use the target's actual state
|
||||
_sensorOutput = GetComponent<Sensor>().Sense(_target);
|
||||
_elapsedTime = 0;
|
||||
}
|
||||
// Correct the state of the target model at the sensor frequency
|
||||
float sensorUpdatePeriod = 1f / _agentConfig.dynamic_config.sensor_config.frequency;
|
||||
if (_elapsedTime >= sensorUpdatePeriod) {
|
||||
// TODO: Implement guidance filter to estimate state from sensor output
|
||||
// For now, we'll use the target's actual state
|
||||
_sensorOutput = GetComponent<Sensor>().Sense(_target);
|
||||
_elapsedTime = 0;
|
||||
}
|
||||
|
||||
// Check whether the target should be considered a miss
|
||||
SensorOutput sensorOutput = GetComponent<Sensor>().Sense(_target);
|
||||
if(sensorOutput.velocity.range > 1000f) {
|
||||
this.MarkAsMiss();
|
||||
}
|
||||
// Check whether the target should be considered a miss
|
||||
SensorOutput sensorOutput = GetComponent<Sensor>().Sense(_target);
|
||||
if (sensorOutput.velocity.range > 1000f) {
|
||||
this.MarkAsMiss();
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
|
||||
}
|
||||
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;
|
||||
// Calculate the acceleration input
|
||||
accelerationInput = CalculateAccelerationCommand(_sensorOutput);
|
||||
}
|
||||
|
||||
protected override void DrawDebugVectors()
|
||||
{
|
||||
base.DrawDebugVectors();
|
||||
if (_accelerationCommand != null)
|
||||
{
|
||||
Debug.DrawRay(transform.position, _accelerationCommand * 1f, Color.green);
|
||||
}
|
||||
}
|
||||
// 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() {
|
||||
base.DrawDebugVectors();
|
||||
if (_accelerationCommand != null) {
|
||||
Debug.DrawRay(transform.position, _accelerationCommand * 1f, Color.green);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,171 +2,156 @@ using System.Collections;
|
|||
using System.Collections.Generic;
|
||||
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]
|
||||
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 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.
|
||||
public override void AssignTarget(Agent target) {
|
||||
base.AssignTarget(target);
|
||||
float airDrag = CalculateDrag();
|
||||
float liftInducedDrag = CalculateLiftInducedDrag(accelerationInput);
|
||||
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();
|
||||
|
||||
|
||||
// Unassign the target from the missile.
|
||||
public override void UnassignTarget() {
|
||||
base.UnassignTarget();
|
||||
} else {
|
||||
// Set red for miss
|
||||
markerObject.GetComponent<Renderer>().material.color = new Color(1, 0, 0, 0.15f);
|
||||
this.MarkAsMiss();
|
||||
// otherAgent.MarkAsMiss();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
float airDrag = CalculateDrag();
|
||||
float liftInducedDrag = CalculateLiftInducedDrag(accelerationInput);
|
||||
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 {
|
||||
// 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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,74 +1,71 @@
|
|||
using UnityEngine;
|
||||
|
||||
public class IdealSensor : Sensor
|
||||
{
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
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 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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
return velocitySensorOutput;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,75 +1,70 @@
|
|||
using UnityEngine;
|
||||
|
||||
public abstract class Sensor : MonoBehaviour
|
||||
{
|
||||
protected Agent _agent;
|
||||
public abstract class Sensor : MonoBehaviour {
|
||||
protected Agent _agent;
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
_agent = GetComponent<Agent>();
|
||||
}
|
||||
protected virtual void Start() {
|
||||
_agent = GetComponent<Agent>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Main sensing method to gather information about a target agent.
|
||||
/// </summary>
|
||||
/// <param name="target">The agent to sense.</param>
|
||||
/// <returns>SensorOutput containing position and velocity data.</returns>
|
||||
/// <remarks>
|
||||
/// Implementers should:
|
||||
/// 1. Call SensePosition to get position data.
|
||||
/// 2. Call SenseVelocity to get velocity data.
|
||||
/// 3. Combine results into a SensorOutput struct.
|
||||
/// </remarks>
|
||||
public abstract SensorOutput Sense(Agent target);
|
||||
/// <summary>
|
||||
/// Main sensing method to gather information about a target agent.
|
||||
/// </summary>
|
||||
/// <param name="target">The agent to sense.</param>
|
||||
/// <returns>SensorOutput containing position and velocity data.</returns>
|
||||
/// <remarks>
|
||||
/// Implementers should:
|
||||
/// 1. Call SensePosition to get position data.
|
||||
/// 2. Call SenseVelocity to get velocity data.
|
||||
/// 3. Combine results into a SensorOutput struct.
|
||||
/// </remarks>
|
||||
public abstract SensorOutput Sense(Agent target);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the relative position of the target agent.
|
||||
/// </summary>
|
||||
/// <param name="target">The agent to sense.</param>
|
||||
/// <returns>PositionOutput containing range, azimuth, and elevation.</returns>
|
||||
/// <remarks>
|
||||
/// Implementers should calculate:
|
||||
/// - range: Distance to the target (in unity units).
|
||||
/// - azimuth: Horizontal angle to the target (in degrees).
|
||||
/// Positive is clockwise from the forward direction.
|
||||
/// - elevation: Vertical angle to the target (in degrees).
|
||||
/// Positive is above the horizontal plane.
|
||||
/// </remarks>
|
||||
protected abstract PositionOutput SensePosition(Agent target);
|
||||
/// <summary>
|
||||
/// Calculates the relative position of the target agent.
|
||||
/// </summary>
|
||||
/// <param name="target">The agent to sense.</param>
|
||||
/// <returns>PositionOutput containing range, azimuth, and elevation.</returns>
|
||||
/// <remarks>
|
||||
/// Implementers should calculate:
|
||||
/// - range: Distance to the target (in unity units).
|
||||
/// - azimuth: Horizontal angle to the target (in degrees).
|
||||
/// Positive is clockwise from the forward direction.
|
||||
/// - elevation: Vertical angle to the target (in degrees).
|
||||
/// Positive is above the horizontal plane.
|
||||
/// </remarks>
|
||||
protected abstract PositionOutput SensePosition(Agent target);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the relative velocity of the target agent.
|
||||
/// </summary>
|
||||
/// <param name="target">The agent to sense.</param>
|
||||
/// <returns>VelocityOutput containing range rate, azimuth rate, and elevation rate.</returns>
|
||||
/// <remarks>
|
||||
/// Implementers should calculate:
|
||||
/// - range: Radial velocity (closing speed) in units/second.
|
||||
/// Positive means the target is moving away.
|
||||
/// - azimuth: Rate of change of azimuth in degrees/second.
|
||||
/// Positive means the target is moving clockwise.
|
||||
/// - elevation: Rate of change of elevation in degrees/second.
|
||||
/// Positive means the target is moving upwards.
|
||||
/// </remarks>
|
||||
protected abstract VelocityOutput SenseVelocity(Agent target);
|
||||
/// <summary>
|
||||
/// Calculates the relative velocity of the target agent.
|
||||
/// </summary>
|
||||
/// <param name="target">The agent to sense.</param>
|
||||
/// <returns>VelocityOutput containing range rate, azimuth rate, and elevation rate.</returns>
|
||||
/// <remarks>
|
||||
/// Implementers should calculate:
|
||||
/// - range: Radial velocity (closing speed) in units/second.
|
||||
/// Positive means the target is moving away.
|
||||
/// - azimuth: Rate of change of azimuth in degrees/second.
|
||||
/// Positive means the target is moving clockwise.
|
||||
/// - elevation: Rate of change of elevation in degrees/second.
|
||||
/// Positive means the target is moving upwards.
|
||||
/// </remarks>
|
||||
protected abstract VelocityOutput SenseVelocity(Agent target);
|
||||
}
|
||||
|
||||
public struct SensorOutput
|
||||
{
|
||||
public PositionOutput position;
|
||||
public VelocityOutput velocity;
|
||||
public struct SensorOutput {
|
||||
public PositionOutput position;
|
||||
public VelocityOutput velocity;
|
||||
}
|
||||
|
||||
public struct PositionOutput
|
||||
{
|
||||
public float range;
|
||||
public float azimuth;
|
||||
public float elevation;
|
||||
public struct PositionOutput {
|
||||
public float range;
|
||||
public float azimuth;
|
||||
public float elevation;
|
||||
}
|
||||
|
||||
public struct VelocityOutput
|
||||
{
|
||||
public float range;
|
||||
public float azimuth;
|
||||
public float elevation;
|
||||
public struct VelocityOutput {
|
||||
public float range;
|
||||
public float azimuth;
|
||||
public float elevation;
|
||||
}
|
|
@ -3,266 +3,226 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
public class SimManager : MonoBehaviour
|
||||
{
|
||||
public class SimManager : MonoBehaviour {
|
||||
// Singleton instance
|
||||
public static SimManager Instance { get; private set; }
|
||||
|
||||
// Singleton instance
|
||||
public static SimManager Instance { get; private set; }
|
||||
[SerializeField]
|
||||
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]
|
||||
public SimulationConfig simulationConfig;
|
||||
private IAssignment _assignmentScheme;
|
||||
|
||||
|
||||
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;
|
||||
public double GetElapsedSimulationTime() {
|
||||
return _elapsedSimulationTime;
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
return _elapsedSimulationTime;
|
||||
void Start() {
|
||||
// Slow down time by simulationConfig.timeScale
|
||||
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()
|
||||
{
|
||||
// Ensure only one instance of SimManager exists
|
||||
if (Instance == null)
|
||||
{
|
||||
Instance = this;
|
||||
DontDestroyOnLoad(gameObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
Destroy(gameObject);
|
||||
}
|
||||
List<Target> targets = new List<Target>();
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
void Start() {
|
||||
// Slow down time by simulationConfig.timeScale
|
||||
if(Instance == this) {
|
||||
Time.timeScale = simulationConfig.timeScale;
|
||||
Time.fixedDeltaTime = Time.timeScale * 0.02f;
|
||||
Time.maximumDeltaTime = Time.timeScale * 0.15f;
|
||||
InitializeSimulation();
|
||||
simulationRunning = true;
|
||||
}
|
||||
_assignmentScheme = new ThreatAssignment();
|
||||
// Perform initial assignment
|
||||
}
|
||||
|
||||
public void AssignMissilesToTargets() {
|
||||
AssignMissilesToTargets(_missiles);
|
||||
}
|
||||
|
||||
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}");
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
// TODO this whole function should be optimized
|
||||
_unassignedTargets.RemoveAll(
|
||||
target => missilesToAssign.Any(missile => missile.GetAssignedTarget() == target));
|
||||
}
|
||||
|
||||
List<Target> targets = new List<Target>();
|
||||
// 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);
|
||||
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;
|
||||
|
||||
_assignmentScheme = new ThreatAssignment();
|
||||
// Perform initial assignment
|
||||
// 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;
|
||||
}
|
||||
|
||||
public void AssignMissilesToTargets()
|
||||
{
|
||||
AssignMissilesToTargets(_missiles);
|
||||
// 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;
|
||||
}
|
||||
|
||||
public void RegisterTargetMiss(Target target) {
|
||||
_unassignedTargets.Add(target);
|
||||
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();
|
||||
}
|
||||
|
||||
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
|
||||
_unassignedTargets.RemoveAll(target => missilesToAssign.Any(missile => missile.GetAssignedTarget() == target));
|
||||
if (simulationRunning && _elapsedSimulationTime < endTime) {
|
||||
_elapsedSimulationTime += Time.deltaTime;
|
||||
} else if (_elapsedSimulationTime >= endTime) {
|
||||
simulationRunning = false;
|
||||
Debug.Log("Simulation completed.");
|
||||
}
|
||||
|
||||
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.");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,29 +2,20 @@ 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();
|
||||
}
|
||||
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();
|
||||
}
|
||||
// Update is called once per frame
|
||||
protected override void 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) {}
|
||||
}
|
||||
|
|
|
@ -2,17 +2,10 @@ using System.Collections;
|
|||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class MissileTarget : MonoBehaviour
|
||||
{
|
||||
// Start is called before the first frame update
|
||||
void Start()
|
||||
{
|
||||
|
||||
}
|
||||
public class MissileTarget : MonoBehaviour {
|
||||
// Start is called before the first frame update
|
||||
void Start() {}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
|
||||
}
|
||||
// Update is called once per frame
|
||||
void Update() {}
|
||||
}
|
||||
|
|
|
@ -2,17 +2,16 @@ using System.Collections;
|
|||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public abstract class Target : Agent
|
||||
{
|
||||
public override bool IsAssignable() {
|
||||
return false;
|
||||
}
|
||||
public abstract class Target : Agent {
|
||||
public override bool IsAssignable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override void Start() {
|
||||
base.Start();
|
||||
}
|
||||
protected override void Start() {
|
||||
base.Start();
|
||||
}
|
||||
|
||||
protected override void Update() {
|
||||
base.Update();
|
||||
}
|
||||
protected override void Update() {
|
||||
base.Update();
|
||||
}
|
||||
}
|
|
@ -1,13 +1,9 @@
|
|||
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)
|
||||
);
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue