deploy: 54c7e5fbc7
This commit is contained in:
208
coverage/playmode/Assets/Scripts/Agent.cs
Normal file
208
coverage/playmode/Assets/Scripts/Agent.cs
Normal file
@@ -0,0 +1,208 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public abstract class Agent : MonoBehaviour {
|
||||
public enum FlightPhase { INITIALIZED, READY, BOOST, MIDCOURSE, TERMINAL, TERMINATED }
|
||||
|
||||
[SerializeField]
|
||||
private FlightPhase _flightPhase = FlightPhase.INITIALIZED;
|
||||
|
||||
[SerializeField]
|
||||
protected Vector3 _velocity;
|
||||
|
||||
[SerializeField]
|
||||
protected Vector3 _acceleration;
|
||||
|
||||
[SerializeField]
|
||||
protected Vector3 _dragAcceleration;
|
||||
|
||||
[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 string staticConfigFile = "generic_static_config.json";
|
||||
|
||||
protected StaticConfig _staticConfig;
|
||||
|
||||
// Define delegates
|
||||
public delegate void InterceptHitEventHandler(Interceptor interceptor, Threat target);
|
||||
public delegate void InterceptMissEventHandler(Interceptor interceptor, Threat target);
|
||||
|
||||
// Define events
|
||||
public event InterceptHitEventHandler OnInterceptHit;
|
||||
public event InterceptMissEventHandler OnInterceptMiss;
|
||||
|
||||
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 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 HandleInterceptHit(Agent otherAgent) {
|
||||
_isHit = true;
|
||||
if (this is Interceptor interceptor && otherAgent is Threat threat) {
|
||||
OnInterceptHit?.Invoke(interceptor, threat);
|
||||
} else if (this is Threat threatAgent && otherAgent is Interceptor interceptorTarget) {
|
||||
OnInterceptHit?.Invoke(interceptorTarget, threatAgent);
|
||||
}
|
||||
TerminateAgent();
|
||||
}
|
||||
|
||||
public void HandleInterceptMiss() {
|
||||
if (_target != null) {
|
||||
if (this is Interceptor interceptor && _target is Threat threat) {
|
||||
OnInterceptMiss?.Invoke(interceptor, threat);
|
||||
} else if (this is Threat threatAgent && _target is Interceptor interceptorTarget) {
|
||||
OnInterceptMiss?.Invoke(interceptorTarget, threatAgent);
|
||||
}
|
||||
_target = null;
|
||||
}
|
||||
TerminateAgent();
|
||||
}
|
||||
|
||||
public double GetSpeed() {
|
||||
return GetComponent<Rigidbody>().linearVelocity.magnitude;
|
||||
}
|
||||
|
||||
public Vector3 GetVelocity() {
|
||||
return GetComponent<Rigidbody>().linearVelocity;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
protected virtual void Awake() {
|
||||
_staticConfig = ConfigLoader.LoadStaticConfig(staticConfigFile);
|
||||
GetComponent<Rigidbody>().mass = _staticConfig.bodyConfig.mass;
|
||||
}
|
||||
|
||||
// Start is called before the first frame update
|
||||
protected virtual void Start() {
|
||||
_flightPhase = FlightPhase.READY;
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
protected virtual void FixedUpdate() {
|
||||
_timeSinceLaunch += Time.fixedDeltaTime;
|
||||
_timeInPhase += Time.fixedDeltaTime;
|
||||
|
||||
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.fixedDeltaTime);
|
||||
break;
|
||||
case FlightPhase.BOOST:
|
||||
UpdateBoost(Time.fixedDeltaTime);
|
||||
break;
|
||||
case FlightPhase.MIDCOURSE:
|
||||
case FlightPhase.TERMINAL:
|
||||
UpdateMidCourse(Time.fixedDeltaTime);
|
||||
break;
|
||||
case FlightPhase.TERMINATED:
|
||||
break;
|
||||
}
|
||||
|
||||
_velocity = GetComponent<Rigidbody>().linearVelocity;
|
||||
_acceleration =
|
||||
GetComponent<Rigidbody>().GetAccumulatedForce() / GetComponent<Rigidbody>().mass;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
coverage/playmode/Assets/Scripts/Agent.cs.meta
Normal file
11
coverage/playmode/Assets/Scripts/Agent.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a86fc0084ee738845a451b91dd4960d9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
coverage/playmode/Assets/Scripts/Assignment.meta
Normal file
8
coverage/playmode/Assets/Scripts/Assignment.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 06e870818cf329e49a0d0c5a0d024f21
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
40
coverage/playmode/Assets/Scripts/Assignment/Assignment.cs
Normal file
40
coverage/playmode/Assets/Scripts/Assignment/Assignment.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using System.Linq;
|
||||
using System.Diagnostics.Contracts;
|
||||
|
||||
// The assignment class is an interface for assigning a threat to each interceptor.
|
||||
public interface IAssignment {
|
||||
// Assignment item type.
|
||||
// The first element corresponds to the interceptor index, and the second element
|
||||
// corresponds to the threat index.
|
||||
public struct AssignmentItem {
|
||||
public Interceptor Interceptor;
|
||||
public Threat Threat;
|
||||
|
||||
public AssignmentItem(Interceptor interceptor, Threat threat) {
|
||||
Interceptor = interceptor;
|
||||
Threat = threat;
|
||||
}
|
||||
}
|
||||
|
||||
// A list containing the interceptor-target assignments.
|
||||
|
||||
// Assign a target to each interceptor that has not been assigned a target yet.
|
||||
[Pure]
|
||||
public abstract IEnumerable<AssignmentItem> Assign(in IReadOnlyList<Interceptor> interceptors, in IReadOnlyList<ThreatData> threatTable);
|
||||
|
||||
// Get the list of assignable interceptor indices.
|
||||
[Pure]
|
||||
protected static List<Interceptor> GetAssignableInterceptors(in IReadOnlyList<Interceptor> interceptors) {
|
||||
return interceptors.Where(interceptor => interceptor.IsAssignable()).ToList();
|
||||
}
|
||||
|
||||
// Get the list of active threats.
|
||||
[Pure]
|
||||
protected static List<ThreatData> GetActiveThreats(in IReadOnlyList<ThreatData> threats) {
|
||||
return threats.Where(t => t.Status != ThreatStatus.DESTROYED).ToList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 95c1ee01149331443bb91460f4d1e3a2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,43 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using System.Diagnostics.Contracts;
|
||||
// The round-robin assignment class assigns interceptors to the targets in a
|
||||
// round-robin order using the new paradigm.
|
||||
public class RoundRobinAssignment : IAssignment {
|
||||
// Previous target index that was assigned.
|
||||
private int prevTargetIndex = -1;
|
||||
|
||||
// Assign a target to each interceptor that has not been assigned a target yet.
|
||||
[Pure]
|
||||
public IEnumerable<IAssignment.AssignmentItem> Assign(in IReadOnlyList<Interceptor> interceptors, in IReadOnlyList<ThreatData> targets) {
|
||||
List<IAssignment.AssignmentItem> assignments = new List<IAssignment.AssignmentItem>();
|
||||
|
||||
// Get the list of interceptors that are available for assignment.
|
||||
List<Interceptor> assignableInterceptors = IAssignment.GetAssignableInterceptors(interceptors);
|
||||
if (assignableInterceptors.Count == 0) {
|
||||
return assignments;
|
||||
}
|
||||
|
||||
// Get the list of active threats that need to be addressed.
|
||||
List<ThreatData> activeThreats = IAssignment.GetActiveThreats(targets);
|
||||
if (activeThreats.Count == 0) {
|
||||
return assignments;
|
||||
}
|
||||
|
||||
// Perform round-robin assignment.
|
||||
foreach (Interceptor interceptor in assignableInterceptors) {
|
||||
// Determine the next target index in a round-robin fashion.
|
||||
int nextTargetIndex = (prevTargetIndex + 1) % activeThreats.Count;
|
||||
ThreatData selectedThreat = activeThreats[nextTargetIndex];
|
||||
|
||||
// Assign the interceptor to the selected threat.
|
||||
assignments.Add(new IAssignment.AssignmentItem(interceptor, selectedThreat.Threat));
|
||||
|
||||
// Update the previous target index.
|
||||
prevTargetIndex = nextTargetIndex;
|
||||
}
|
||||
|
||||
return assignments;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f668f6d7413495c4093550e492ce36bd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,79 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Unity.VisualScripting;
|
||||
using UnityEngine;
|
||||
using System.Diagnostics.Contracts;
|
||||
// The threat assignment class assigns interceptors to the targets based
|
||||
// on the threat level of the targets.
|
||||
public class ThreatAssignment : IAssignment {
|
||||
// Assign a target to each interceptor that has not been assigned a target yet.
|
||||
[Pure]
|
||||
public IEnumerable<IAssignment.AssignmentItem> Assign(in IReadOnlyList<Interceptor> interceptors, in IReadOnlyList<ThreatData> targets) {
|
||||
List<IAssignment.AssignmentItem> assignments = new List<IAssignment.AssignmentItem>();
|
||||
|
||||
List<Interceptor> assignableInterceptors = IAssignment.GetAssignableInterceptors(interceptors);
|
||||
if (assignableInterceptors.Count == 0) {
|
||||
Debug.LogWarning("No assignable interceptors found");
|
||||
return assignments;
|
||||
}
|
||||
|
||||
List<ThreatData> activeThreats = IAssignment.GetActiveThreats(targets);
|
||||
if (activeThreats.Count == 0) {
|
||||
Debug.LogWarning("No active threats found");
|
||||
return assignments;
|
||||
}
|
||||
|
||||
Vector3 positionToDefend = Vector3.zero;
|
||||
List<ThreatInfo> threatInfos =
|
||||
CalculateThreatLevels(activeThreats, positionToDefend);
|
||||
|
||||
// Sort ThreatInfo first by ThreatData.Status (UNASSIGNED first, then ASSIGNED)
|
||||
// Within each group, order by ThreatLevel descending
|
||||
threatInfos = threatInfos.OrderByDescending(t => t.ThreatData.Status == ThreatStatus.UNASSIGNED)
|
||||
.ThenByDescending(t => t.ThreatLevel)
|
||||
.ToList();
|
||||
|
||||
var assignableInterceptorsEnumerator = assignableInterceptors.GetEnumerator();
|
||||
if (assignableInterceptorsEnumerator.MoveNext()) // Move to the first element
|
||||
{
|
||||
foreach (ThreatInfo threatInfo in threatInfos) {
|
||||
assignments.Add(new IAssignment.AssignmentItem(assignableInterceptorsEnumerator.Current, threatInfo.ThreatData.Threat));
|
||||
if (!assignableInterceptorsEnumerator.MoveNext()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return assignments;
|
||||
}
|
||||
|
||||
|
||||
private List<ThreatInfo> CalculateThreatLevels(List<ThreatData> threatTable,
|
||||
Vector3 defensePosition) {
|
||||
List<ThreatInfo> threatInfos = new List<ThreatInfo>();
|
||||
|
||||
foreach (ThreatData threatData in threatTable) {
|
||||
Threat threat = threatData.Threat;
|
||||
float distanceToMean = Vector3.Distance(threat.transform.position, defensePosition);
|
||||
float velocityMagnitude = threat.GetVelocity().magnitude;
|
||||
|
||||
// Calculate threat level based on proximity and velocity
|
||||
float threatLevel = (1 / distanceToMean) * velocityMagnitude;
|
||||
|
||||
threatInfos.Add(new ThreatInfo(threatData, threatLevel));
|
||||
}
|
||||
|
||||
// Sort threats in descending order
|
||||
return threatInfos.OrderByDescending(t => t.ThreatLevel).ToList();
|
||||
}
|
||||
|
||||
private class ThreatInfo {
|
||||
public ThreatData ThreatData { get; }
|
||||
public float ThreatLevel { get; }
|
||||
|
||||
public ThreatInfo(ThreatData threatData, float threatLevel) {
|
||||
ThreatData = threatData;
|
||||
ThreatLevel = threatLevel;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8e9829915a9eb41409ea03fb46910432
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
coverage/playmode/Assets/Scripts/Config.meta
Normal file
8
coverage/playmode/Assets/Scripts/Config.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 068fab6561d36f445b1d765a9d3af005
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
168
coverage/playmode/Assets/Scripts/Config/ConfigLoader.cs
Normal file
168
coverage/playmode/Assets/Scripts/Config/ConfigLoader.cs
Normal file
@@ -0,0 +1,168 @@
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
public static class ConfigLoader {
|
||||
|
||||
private static string LoadFromStreamingAssets(string relativePath)
|
||||
{
|
||||
string filePath = Path.Combine(Application.streamingAssetsPath, relativePath);
|
||||
|
||||
#if UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX || UNITY_STANDALONE_LINUX || UNITY_EDITOR_LINUX || UNITY_IOS
|
||||
if (!filePath.StartsWith("file://"))
|
||||
{
|
||||
filePath = "file://" + filePath;
|
||||
}
|
||||
#endif
|
||||
|
||||
UnityWebRequest www = UnityWebRequest.Get(filePath);
|
||||
www.SendWebRequest();
|
||||
|
||||
// Wait for the request to complete
|
||||
while (!www.isDone)
|
||||
{
|
||||
// You might want to yield return null here if this is called from a coroutine
|
||||
}
|
||||
|
||||
if (www.result != UnityWebRequest.Result.Success)
|
||||
{
|
||||
Debug.LogError($"Error loading file at {filePath}: {www.error}");
|
||||
return null;
|
||||
}
|
||||
|
||||
return www.downloadHandler.text;
|
||||
}
|
||||
|
||||
public static SimulationConfig LoadSimulationConfig(string configFileName)
|
||||
{
|
||||
string relativePath = Path.Combine("Configs", configFileName);
|
||||
string fileContent = LoadFromStreamingAssets(relativePath);
|
||||
|
||||
if (string.IsNullOrEmpty(fileContent))
|
||||
{
|
||||
Debug.LogError($"Failed to load SimulationConfig from {relativePath}");
|
||||
return null;
|
||||
}
|
||||
|
||||
return JsonConvert.DeserializeObject<SimulationConfig>(fileContent, new JsonSerializerSettings
|
||||
{
|
||||
Converters = { new Newtonsoft.Json.Converters.StringEnumConverter() }
|
||||
});
|
||||
}
|
||||
|
||||
public static StaticConfig LoadStaticConfig(string configFileName)
|
||||
{
|
||||
string relativePath = Path.Combine("Configs/Models", configFileName);
|
||||
string fileContent = LoadFromStreamingAssets(relativePath);
|
||||
|
||||
if (string.IsNullOrEmpty(fileContent))
|
||||
{
|
||||
Debug.LogError($"Failed to load StaticConfig from {relativePath}");
|
||||
return null;
|
||||
}
|
||||
|
||||
return JsonConvert.DeserializeObject<StaticConfig>(fileContent, new JsonSerializerSettings
|
||||
{
|
||||
Converters = { new Newtonsoft.Json.Converters.StringEnumConverter() }
|
||||
});
|
||||
}
|
||||
|
||||
public static void PrintSimulationConfig(SimulationConfig config)
|
||||
{
|
||||
if (config == null)
|
||||
{
|
||||
Debug.Log("SimulationConfig is null");
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.Log("SimulationConfig:");
|
||||
Debug.Log($"Time Scale: {config.timeScale}");
|
||||
|
||||
Debug.Log("Interceptor Swarm Configurations:");
|
||||
for (int i = 0; i < config.interceptor_swarm_configs.Count; i++)
|
||||
{
|
||||
PrintSwarmConfig(config.interceptor_swarm_configs[i], $"Interceptor Swarm {i + 1}");
|
||||
}
|
||||
|
||||
Debug.Log("Threat Swarm Configurations:");
|
||||
for (int i = 0; i < config.threat_swarm_configs.Count; i++)
|
||||
{
|
||||
PrintSwarmConfig(config.threat_swarm_configs[i], $"Threat Swarm {i + 1}");
|
||||
}
|
||||
}
|
||||
|
||||
private static void PrintSwarmConfig(SwarmConfig swarmConfig, string swarmName)
|
||||
{
|
||||
Debug.Log($"{swarmName}:");
|
||||
Debug.Log($" Number of Agents: {swarmConfig.num_agents}");
|
||||
PrintAgentConfig(swarmConfig.agent_config);
|
||||
}
|
||||
|
||||
private static void PrintAgentConfig(AgentConfig agentConfig)
|
||||
{
|
||||
Debug.Log(" Agent Configuration:");
|
||||
Debug.Log($" Interceptor Type: {agentConfig.interceptor_type}");
|
||||
Debug.Log($" Threat Type: {agentConfig.threat_type}");
|
||||
PrintInitialState(agentConfig.initial_state);
|
||||
PrintStandardDeviation(agentConfig.standard_deviation);
|
||||
PrintDynamicConfig(agentConfig.dynamic_config);
|
||||
PrintPlottingConfig(agentConfig.plotting_config);
|
||||
PrintSubmunitionsConfig(agentConfig.submunitions_config);
|
||||
}
|
||||
|
||||
private static void PrintInitialState(InitialState initialState)
|
||||
{
|
||||
Debug.Log(" Initial State:");
|
||||
Debug.Log($" Position: {initialState.position}");
|
||||
Debug.Log($" Rotation: {initialState.rotation}");
|
||||
Debug.Log($" Velocity: {initialState.velocity}");
|
||||
}
|
||||
|
||||
private static void PrintStandardDeviation(StandardDeviation standardDeviation)
|
||||
{
|
||||
Debug.Log(" Standard Deviation:");
|
||||
Debug.Log($" Position: {standardDeviation.position}");
|
||||
Debug.Log($" Velocity: {standardDeviation.velocity}");
|
||||
}
|
||||
|
||||
private static void PrintDynamicConfig(DynamicConfig dynamicConfig)
|
||||
{
|
||||
Debug.Log(" Dynamic Configuration:");
|
||||
Debug.Log($" Launch Time: {dynamicConfig.launch_config.launch_time}");
|
||||
Debug.Log($" Sensor Type: {dynamicConfig.sensor_config.type}");
|
||||
Debug.Log($" Sensor Frequency: {dynamicConfig.sensor_config.frequency}");
|
||||
}
|
||||
|
||||
private static void PrintPlottingConfig(PlottingConfig plottingConfig)
|
||||
{
|
||||
Debug.Log(" Plotting Configuration:");
|
||||
Debug.Log($" Color: {plottingConfig.color}");
|
||||
Debug.Log($" Line Style: {plottingConfig.linestyle}");
|
||||
Debug.Log($" Marker: {plottingConfig.marker}");
|
||||
}
|
||||
|
||||
private static void PrintSubmunitionsConfig(SubmunitionsConfig submunitionsConfig)
|
||||
{
|
||||
if (submunitionsConfig == null)
|
||||
{
|
||||
Debug.Log(" Submunitions Configuration: None");
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.Log(" Submunitions Configuration:");
|
||||
Debug.Log($" Number of Submunitions: {submunitionsConfig.num_submunitions}");
|
||||
Debug.Log($" Launch Time: {submunitionsConfig.launch_config.launch_time}");
|
||||
PrintSubmunitionAgentConfig(submunitionsConfig.agent_config);
|
||||
}
|
||||
|
||||
private static void PrintSubmunitionAgentConfig(SubmunitionAgentConfig agentConfig)
|
||||
{
|
||||
Debug.Log(" Submunition Agent Configuration:");
|
||||
Debug.Log($" Interceptor Type: {agentConfig.interceptor_type}");
|
||||
PrintInitialState(agentConfig.initial_state);
|
||||
PrintStandardDeviation(agentConfig.standard_deviation);
|
||||
PrintDynamicConfig(agentConfig.dynamic_config);
|
||||
PrintPlottingConfig(agentConfig.plotting_config);
|
||||
}
|
||||
}
|
||||
11
coverage/playmode/Assets/Scripts/Config/ConfigLoader.cs.meta
Normal file
11
coverage/playmode/Assets/Scripts/Config/ConfigLoader.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: da3b9eead33b94a4f8df7a4af7a79d49
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
123
coverage/playmode/Assets/Scripts/Config/SimulationConfig.cs
Normal file
123
coverage/playmode/Assets/Scripts/Config/SimulationConfig.cs
Normal file
@@ -0,0 +1,123 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
|
||||
[Serializable]
|
||||
public class SimulationConfig {
|
||||
[Header("Simulation Settings")]
|
||||
public float timeScale = 0.05f;
|
||||
|
||||
[Header("Interceptor Swarm Configurations")]
|
||||
public List<SwarmConfig> interceptor_swarm_configs = new List<SwarmConfig>();
|
||||
|
||||
[Header("Threat Swarm Configurations")]
|
||||
public List<SwarmConfig> threat_swarm_configs = new List<SwarmConfig>();
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class DynamicConfig {
|
||||
public LaunchConfig launch_config;
|
||||
public SensorConfig sensor_config;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class SwarmConfig {
|
||||
public int num_agents;
|
||||
public AgentConfig agent_config;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class AgentConfig {
|
||||
public InterceptorType interceptor_type;
|
||||
public ThreatType threat_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 {
|
||||
interceptor_type = submunitionConfig.interceptor_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
|
||||
threat_type = ThreatType.DRONE, // Or another default value
|
||||
submunitions_config = null // Or a default value if needed
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class InitialState {
|
||||
public Vector3 position;
|
||||
public Vector3 rotation;
|
||||
public Vector3 velocity;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class StandardDeviation {
|
||||
public Vector3 position;
|
||||
public Vector3 velocity;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class LaunchConfig {
|
||||
public float launch_time;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class PlottingConfig {
|
||||
public ConfigColor color;
|
||||
public LineStyle linestyle;
|
||||
public Marker marker;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class SubmunitionsConfig {
|
||||
public int num_submunitions;
|
||||
public LaunchConfig launch_config;
|
||||
public SubmunitionAgentConfig agent_config;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class SubmunitionAgentConfig {
|
||||
public InterceptorType interceptor_type;
|
||||
public InitialState initial_state;
|
||||
public StandardDeviation standard_deviation;
|
||||
public DynamicConfig dynamic_config;
|
||||
public PlottingConfig plotting_config;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class SensorConfig {
|
||||
public SensorType type;
|
||||
public float frequency;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class TargetConfig {
|
||||
public ThreatType threat_type;
|
||||
public InitialState initial_state;
|
||||
public PlottingConfig plotting_config;
|
||||
public string prefabName;
|
||||
}
|
||||
|
||||
// Enums
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum InterceptorType { HYDRA_70, MICROMISSILE }
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum ThreatType { DRONE, ANTISHIP_MISSILE }
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum ConfigColor { BLUE, GREEN, RED }
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum LineStyle { DOTTED, SOLID }
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum Marker { TRIANGLE_UP, TRIANGLE_DOWN, SQUARE }
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public enum SensorType { IDEAL }
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 79f1fe138866d6a40b209a4edcf2ee06
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
43
coverage/playmode/Assets/Scripts/Config/StaticConfig.cs
Normal file
43
coverage/playmode/Assets/Scripts/Config/StaticConfig.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
|
||||
[Serializable]
|
||||
public class StaticConfig {
|
||||
[Serializable]
|
||||
public class AccelerationConfig {
|
||||
public float maxReferenceAcceleration = 300f;
|
||||
public float referenceSpeed = 1000f;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class BoostConfig {
|
||||
public float boostTime = 0.3f;
|
||||
public float boostAcceleration = 350f;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class LiftDragConfig {
|
||||
public float liftCoefficient = 0.2f;
|
||||
public float dragCoefficient = 0.7f;
|
||||
public float liftDragRatio = 5f;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class BodyConfig {
|
||||
public float mass = 0.37f;
|
||||
public float crossSectionalArea = 3e-4f;
|
||||
public float finArea = 6e-4f;
|
||||
public float bodyArea = 1e-2f;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class HitConfig {
|
||||
public float hitRadius = 1f;
|
||||
public float killProbability = 0.9f;
|
||||
}
|
||||
|
||||
public AccelerationConfig accelerationConfig;
|
||||
public BoostConfig boostConfig;
|
||||
public LiftDragConfig liftDragConfig;
|
||||
public BodyConfig bodyConfig;
|
||||
public HitConfig hitConfig;
|
||||
}
|
||||
11
coverage/playmode/Assets/Scripts/Config/StaticConfig.cs.meta
Normal file
11
coverage/playmode/Assets/Scripts/Config/StaticConfig.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 35d9cdcf93cb04b40a7538fc87071e3b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
17
coverage/playmode/Assets/Scripts/Constants.cs
Normal file
17
coverage/playmode/Assets/Scripts/Constants.cs
Normal file
@@ -0,0 +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 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);
|
||||
}
|
||||
}
|
||||
11
coverage/playmode/Assets/Scripts/Constants.cs.meta
Normal file
11
coverage/playmode/Assets/Scripts/Constants.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 52d6188ab9d8da8498db11ceddd027bf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
coverage/playmode/Assets/Scripts/Editor.meta
Normal file
8
coverage/playmode/Assets/Scripts/Editor.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 91594f3f9a39e6746879216d6dd0d4ec
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
144
coverage/playmode/Assets/Scripts/Editor/GenerateCone.cs
Normal file
144
coverage/playmode/Assets/Scripts/Editor/GenerateCone.cs
Normal file
@@ -0,0 +1,144 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
// Base face
|
||||
for (int i = 1; i < rimVertices - 1; i++) {
|
||||
AddTriangle(coneVerts[1], coneVerts[i + 1], coneVerts[i + 2]);
|
||||
}
|
||||
|
||||
void AddTriangle(Vector3 t1, Vector3 t2, Vector3 t3) {
|
||||
finalVerts.Add(t1);
|
||||
finalVerts.Add(t2);
|
||||
finalVerts.Add(t3);
|
||||
triangles.Add(finalVerts.Count - 3);
|
||||
triangles.Add(finalVerts.Count - 2);
|
||||
triangles.Add(finalVerts.Count - 1);
|
||||
}
|
||||
}
|
||||
Mesh CreateConeMesh(string name, int sides, Vector3 apex, Quaternion rotation, float baseRadius,
|
||||
float height) {
|
||||
var baseVerts = GetBasePoints(sides, baseRadius);
|
||||
var coneVerts = BuildConeVertices(baseVerts, height);
|
||||
|
||||
var verts = new List<Vector3>();
|
||||
var tris = new List<int>();
|
||||
ConstructCone(coneVerts, verts, tris);
|
||||
|
||||
for (int i = 0; i < verts.Count; i++) {
|
||||
verts[i] = rotation * (verts[i] - coneVerts[0]);
|
||||
}
|
||||
|
||||
// Recenter the cone
|
||||
Vector3 center = CalculateCenter(verts);
|
||||
for (int i = 0; i < verts.Count; i++) {
|
||||
verts[i] = verts[i] - center + apex;
|
||||
}
|
||||
|
||||
Mesh mesh = new Mesh();
|
||||
mesh.name = name;
|
||||
mesh.SetVertices(verts);
|
||||
mesh.SetTriangles(tris.ToArray(), 0);
|
||||
mesh.RecalculateNormals();
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
Vector3 CalculateCenter(List<Vector3> vertices) {
|
||||
Vector3 sum = Vector3.zero;
|
||||
foreach (Vector3 vert in vertices) {
|
||||
sum += vert;
|
||||
}
|
||||
return sum / vertices.Count;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
11
coverage/playmode/Assets/Scripts/Editor/GenerateCone.cs.meta
Normal file
11
coverage/playmode/Assets/Scripts/Editor/GenerateCone.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 27925dcc8d7b5dd4ab28ed7de5b806a4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
coverage/playmode/Assets/Scripts/IADS.meta
Normal file
8
coverage/playmode/Assets/Scripts/IADS.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9c6197c2ed2c6bc49b0667188a72a29d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
146
coverage/playmode/Assets/Scripts/IADS/IADS.cs
Normal file
146
coverage/playmode/Assets/Scripts/IADS/IADS.cs
Normal file
@@ -0,0 +1,146 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
// Integrated Air Defense System
|
||||
public class IADS : MonoBehaviour {
|
||||
|
||||
public enum ThreatAssignmentStyle {
|
||||
ONE_TIME,
|
||||
CONTINUOUS
|
||||
}
|
||||
|
||||
public static IADS Instance { get; private set; }
|
||||
private IAssignment _assignmentScheme;
|
||||
|
||||
[SerializeField]
|
||||
private List<ThreatData> _threatTable = new List<ThreatData>();
|
||||
private Dictionary<Threat, ThreatData> _threatDataMap = new Dictionary<Threat, ThreatData>();
|
||||
|
||||
private List<Interceptor> _assignmentQueue = new List<Interceptor>();
|
||||
|
||||
private void Awake() {
|
||||
if (Instance == null) {
|
||||
Instance = this;
|
||||
} else {
|
||||
Destroy(gameObject);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void Start() {
|
||||
SimManager.Instance.OnSimulationEnded += RegisterSimulationEnded;
|
||||
SimManager.Instance.OnNewThreat += RegisterNewThreat;
|
||||
SimManager.Instance.OnNewInterceptor += RegisterNewInterceptor;
|
||||
_assignmentScheme = new ThreatAssignment();
|
||||
|
||||
}
|
||||
|
||||
public void LateUpdate() {
|
||||
if (_assignmentQueue.Count > 0) {
|
||||
AssignInterceptorsToThreats(_assignmentQueue);
|
||||
_assignmentQueue.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void RequestThreatAssignment(List<Interceptor> interceptors) {
|
||||
_assignmentQueue.AddRange(interceptors);
|
||||
}
|
||||
|
||||
public void RequestThreatAssignment(Interceptor interceptor) {
|
||||
_assignmentQueue.Add(interceptor);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Assigns the specified list of missiles to available targets based on the assignment scheme.
|
||||
/// </summary>
|
||||
/// <param name="missilesToAssign">The list of missiles to assign.</param>
|
||||
public void AssignInterceptorsToThreats(List<Interceptor> missilesToAssign) {
|
||||
// Perform the assignment
|
||||
IEnumerable<IAssignment.AssignmentItem> assignments =
|
||||
_assignmentScheme.Assign(missilesToAssign, _threatTable);
|
||||
|
||||
// Apply the assignments to the missiles
|
||||
foreach (var assignment in assignments) {
|
||||
assignment.Interceptor.AssignTarget(assignment.Threat);
|
||||
_threatDataMap[assignment.Threat].AssignInterceptor(assignment.Interceptor);
|
||||
Debug.Log($"Interceptor {assignment.Interceptor.name} assigned to threat {assignment.Threat.name}");
|
||||
}
|
||||
|
||||
// Check if any interceptors were not assigned
|
||||
List<Interceptor> unassignedInterceptors = missilesToAssign.Where(m => !m.HasAssignedTarget()).ToList();
|
||||
|
||||
if (unassignedInterceptors.Count > 0)
|
||||
{
|
||||
string unassignedIds = string.Join(", ", unassignedInterceptors.Select(m => m.name));
|
||||
int totalInterceptors = missilesToAssign.Count;
|
||||
int assignedInterceptors = totalInterceptors - unassignedInterceptors.Count;
|
||||
|
||||
Debug.LogWarning($"Warning: {unassignedInterceptors.Count} out of {totalInterceptors} interceptors were not assigned to any threat. " +
|
||||
$"Unassigned interceptor IDs: {unassignedIds}. " +
|
||||
$"Total interceptors: {totalInterceptors}, Assigned: {assignedInterceptors}, Unassigned: {unassignedInterceptors.Count}");
|
||||
|
||||
// Log information about the assignment scheme
|
||||
Debug.Log($"Current Assignment Scheme: {_assignmentScheme.GetType().Name}");
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterNewThreat(Threat threat) {
|
||||
ThreatData threatData = new ThreatData(threat, threat.gameObject.name);
|
||||
_threatTable.Add(threatData);
|
||||
_threatDataMap.Add(threat, threatData);
|
||||
|
||||
// Subscribe to the threat's events
|
||||
// TODO: If we do not want omniscient IADS, we
|
||||
// need to model the IADS's sensors here.
|
||||
threat.OnInterceptHit += RegisterThreatHit;
|
||||
threat.OnInterceptMiss += RegisterThreatMiss;
|
||||
}
|
||||
|
||||
public void RegisterNewInterceptor(Interceptor interceptor) {
|
||||
// Placeholder
|
||||
interceptor.OnInterceptMiss += RegisterInterceptorMiss;
|
||||
interceptor.OnInterceptHit += RegisterInterceptorHit;
|
||||
}
|
||||
|
||||
private void RegisterInterceptorHit(Interceptor interceptor, Threat threat) {
|
||||
ThreatData threatData = _threatDataMap[threat];
|
||||
if (threatData != null) {
|
||||
threatData.RemoveInterceptor(interceptor);
|
||||
MarkThreatDestroyed(threatData);
|
||||
}
|
||||
}
|
||||
|
||||
private void RegisterInterceptorMiss(Interceptor interceptor, Threat threat) {
|
||||
// Remove the interceptor from the threat's assigned interceptors
|
||||
_threatDataMap[threat].RemoveInterceptor(interceptor);
|
||||
}
|
||||
private void RegisterThreatHit(Interceptor interceptor, Threat threat) {
|
||||
ThreatData threatData = _threatDataMap[threat];
|
||||
if (threatData != null) {
|
||||
threatData.RemoveInterceptor(interceptor);
|
||||
MarkThreatDestroyed(threatData);
|
||||
}
|
||||
}
|
||||
|
||||
private void MarkThreatDestroyed(ThreatData threatData) {
|
||||
if (threatData != null) {
|
||||
threatData.MarkDestroyed();
|
||||
}
|
||||
}
|
||||
|
||||
private void RegisterThreatMiss(Interceptor interceptor, Threat threat) {
|
||||
ThreatData threatData = _threatDataMap[threat];
|
||||
threatData.RemoveInterceptor(interceptor);
|
||||
}
|
||||
|
||||
private void RegisterSimulationEnded() {
|
||||
_threatTable.Clear();
|
||||
_threatDataMap.Clear();
|
||||
_assignmentQueue.Clear();
|
||||
}
|
||||
|
||||
}
|
||||
11
coverage/playmode/Assets/Scripts/IADS/IADS.cs.meta
Normal file
11
coverage/playmode/Assets/Scripts/IADS/IADS.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a5899f1049cf3d64e8c06c1db772c879
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
48
coverage/playmode/Assets/Scripts/IADS/ThreatData.cs
Normal file
48
coverage/playmode/Assets/Scripts/IADS/ThreatData.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
[System.Serializable]
|
||||
public enum ThreatStatus {
|
||||
UNASSIGNED,
|
||||
ASSIGNED,
|
||||
DESTROYED
|
||||
}
|
||||
[System.Serializable]
|
||||
public class ThreatData
|
||||
{
|
||||
public Threat Threat;
|
||||
[SerializeField]
|
||||
private ThreatStatus _status;
|
||||
public ThreatStatus Status { get { return _status; } }
|
||||
public string ThreatID;
|
||||
[SerializeField]
|
||||
private List<Interceptor> _assignedInterceptors; // Changed from property to field
|
||||
|
||||
public void AssignInterceptor(Interceptor interceptor) {
|
||||
if(Status == ThreatStatus.DESTROYED) {
|
||||
Debug.LogError($"AssignInterceptor: Threat {ThreatID} is destroyed, cannot assign interceptor");
|
||||
return;
|
||||
}
|
||||
_status = ThreatStatus.ASSIGNED;
|
||||
_assignedInterceptors.Add(interceptor);
|
||||
}
|
||||
|
||||
public void RemoveInterceptor(Interceptor interceptor) {
|
||||
_assignedInterceptors.Remove(interceptor);
|
||||
if(_assignedInterceptors.Count == 0) {
|
||||
_status = ThreatStatus.UNASSIGNED;
|
||||
}
|
||||
}
|
||||
|
||||
public void MarkDestroyed() {
|
||||
_status = ThreatStatus.DESTROYED;
|
||||
}
|
||||
// Constructor remains the same
|
||||
public ThreatData(Threat threat, string threatID)
|
||||
{
|
||||
Threat = threat;
|
||||
_status = ThreatStatus.UNASSIGNED;
|
||||
ThreatID = threatID;
|
||||
_assignedInterceptors = new List<Interceptor>(); // Initialize the list
|
||||
}
|
||||
}
|
||||
2
coverage/playmode/Assets/Scripts/IADS/ThreatData.cs.meta
Normal file
2
coverage/playmode/Assets/Scripts/IADS/ThreatData.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: adc0c5dbdb9dc7d498b50cf9a15c2db5
|
||||
27
coverage/playmode/Assets/Scripts/IADS/Vessel.cs
Normal file
27
coverage/playmode/Assets/Scripts/IADS/Vessel.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class Vessel : MonoBehaviour {
|
||||
[SerializeField]
|
||||
private List<Interceptor> missileInventory = new List<Interceptor>();
|
||||
|
||||
public void AddInterceptor(Interceptor interceptor) {
|
||||
if (interceptor != null) {
|
||||
missileInventory.Add(interceptor);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveInterceptor(Interceptor interceptor) {
|
||||
missileInventory.Remove(interceptor);
|
||||
}
|
||||
|
||||
public List<Interceptor> GetInterceptorInventory() {
|
||||
return new List<Interceptor>(missileInventory);
|
||||
}
|
||||
|
||||
public int GetInterceptorCount() {
|
||||
return missileInventory.Count;
|
||||
}
|
||||
|
||||
// Additional methods can be added here as needed
|
||||
}
|
||||
11
coverage/playmode/Assets/Scripts/IADS/Vessel.cs.meta
Normal file
11
coverage/playmode/Assets/Scripts/IADS/Vessel.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 607b5f69ae2775c4ab0089839f65fd61
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
157
coverage/playmode/Assets/Scripts/Interceptor.cs
Normal file
157
coverage/playmode/Assets/Scripts/Interceptor.cs
Normal file
@@ -0,0 +1,157 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class Interceptor : Agent {
|
||||
[SerializeField]
|
||||
protected bool _showDebugVectors = true;
|
||||
|
||||
// Return whether a target can be assigned to the interceptor.
|
||||
public override bool IsAssignable() {
|
||||
bool assignable = !HasLaunched() && !HasAssignedTarget();
|
||||
return assignable;
|
||||
}
|
||||
|
||||
// Assign the given target to the interceptor.
|
||||
public override void AssignTarget(Agent target) {
|
||||
base.AssignTarget(target);
|
||||
}
|
||||
|
||||
// Unassign the target from the interceptor.
|
||||
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 FixedUpdate() {
|
||||
base.FixedUpdate();
|
||||
if (_showDebugVectors) {
|
||||
DrawDebugVectors();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void UpdateBoost(double deltaTime) {
|
||||
// The interceptor 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);
|
||||
}
|
||||
|
||||
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 + gravity);
|
||||
float dragAcceleration = -(airDrag + liftInducedDrag);
|
||||
|
||||
// Project the drag acceleration onto the forward direction
|
||||
Vector3 dragAccelerationAlongRoll = dragAcceleration * transform.forward;
|
||||
_dragAcceleration = dragAccelerationAlongRoll;
|
||||
|
||||
return accelerationInput + gravity + dragAccelerationAlongRoll;
|
||||
}
|
||||
|
||||
private void OnTriggerEnter(Collider other) {
|
||||
if (other.gameObject.name == "Floor") {
|
||||
this.HandleInterceptMiss();
|
||||
}
|
||||
// Check if the collision is with another Agent
|
||||
Agent otherAgent = other.gameObject.GetComponentInParent<Agent>();
|
||||
if (otherAgent != null && otherAgent.GetComponent<Threat>() != 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.HandleInterceptHit(otherAgent);
|
||||
otherAgent.HandleInterceptHit(otherAgent);
|
||||
|
||||
} else {
|
||||
// Set red for miss
|
||||
markerObject.GetComponent<Renderer>().material.color = new Color(1, 0, 0, 0.15f);
|
||||
this.HandleInterceptMiss();
|
||||
// otherAgent.MarkAsMiss();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected float CalculateMaxAcceleration() {
|
||||
float maxReferenceAcceleration =
|
||||
(float)(_staticConfig.accelerationConfig.maxReferenceAcceleration * Constants.kGravity);
|
||||
float referenceSpeed = _staticConfig.accelerationConfig.referenceSpeed;
|
||||
return Mathf.Pow(GetComponent<Rigidbody>().linearVelocity.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 =
|
||||
(accelerationInput - Vector3.Dot(accelerationInput, transform.up) * transform.up).magnitude;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
coverage/playmode/Assets/Scripts/Interceptor.cs.meta
Normal file
11
coverage/playmode/Assets/Scripts/Interceptor.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 932fd95d676a82349ab18bddf929d4bb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
coverage/playmode/Assets/Scripts/Interceptors.meta
Normal file
8
coverage/playmode/Assets/Scripts/Interceptors.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6f05ad8af46b8df4c9720b5deb691295
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
53
coverage/playmode/Assets/Scripts/Interceptors/Hydra70.cs
Normal file
53
coverage/playmode/Assets/Scripts/Interceptors/Hydra70.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
using UnityEngine;
|
||||
|
||||
public class Hydra70 : Interceptor {
|
||||
private bool _submunitionsLaunched = false;
|
||||
|
||||
protected override void FixedUpdate() {
|
||||
base.FixedUpdate();
|
||||
|
||||
// Check if it's time to launch submunitions
|
||||
if (!_submunitionsLaunched &&
|
||||
(GetFlightPhase() == FlightPhase.MIDCOURSE || GetFlightPhase() == FlightPhase.BOOST) &&
|
||||
SimManager.Instance.GetElapsedSimulationTime() >=
|
||||
_agentConfig.submunitions_config.launch_config.launch_time) {
|
||||
SpawnSubmunitions();
|
||||
_submunitionsLaunched = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void UpdateMidCourse(double deltaTime) {
|
||||
Vector3 accelerationInput = Vector3.zero;
|
||||
// Calculate and set the total acceleration
|
||||
Vector3 acceleration = CalculateAcceleration(accelerationInput);
|
||||
GetComponent<Rigidbody>().AddForce(acceleration, ForceMode.Acceleration);
|
||||
}
|
||||
|
||||
protected override void DrawDebugVectors() {
|
||||
base.DrawDebugVectors();
|
||||
if (_acceleration != null) {
|
||||
Debug.DrawRay(transform.position, _acceleration * 1f, Color.green);
|
||||
}
|
||||
}
|
||||
|
||||
public void SpawnSubmunitions() {
|
||||
List<Interceptor> submunitions = new List<Interceptor>();
|
||||
switch (_agentConfig.submunitions_config.agent_config.interceptor_type) {
|
||||
case InterceptorType.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>().linearVelocity;
|
||||
Interceptor submunition = SimManager.Instance.CreateInterceptor(convertedConfig);
|
||||
submunitions.Add(submunition);
|
||||
}
|
||||
break;
|
||||
}
|
||||
IADS.Instance.RequestThreatAssignment(submunitions);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 403a04456de34694a8946c6a5084a9cc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,78 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class Micromissile : Interceptor {
|
||||
[SerializeField]
|
||||
private float _navigationGain = 3f; // 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 threat model (assuming we have a threat model)
|
||||
// TODO: Implement threat model update logic
|
||||
|
||||
// Correct the state of the threat 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 threat's actual state
|
||||
_sensorOutput = GetComponent<Sensor>().Sense(_target);
|
||||
_elapsedTime = 0;
|
||||
}
|
||||
|
||||
// Check whether the threat should be considered a miss
|
||||
SensorOutput sensorOutput = GetComponent<Sensor>().Sense(_target);
|
||||
if (sensorOutput.velocity.range > 1000f) {
|
||||
this.HandleInterceptMiss();
|
||||
}
|
||||
|
||||
// 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 craft 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 864f4855030b29d44a944391ce02342a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
coverage/playmode/Assets/Scripts/Managers.meta
Normal file
8
coverage/playmode/Assets/Scripts/Managers.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9411e01e9e0fecb45ac53c53b65fa917
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
194
coverage/playmode/Assets/Scripts/Managers/InputManager.cs
Normal file
194
coverage/playmode/Assets/Scripts/Managers/InputManager.cs
Normal file
@@ -0,0 +1,194 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using System.Linq;
|
||||
|
||||
public class InputManager : MonoBehaviour
|
||||
{
|
||||
|
||||
public static InputManager Instance { get; private set; }
|
||||
|
||||
|
||||
|
||||
public bool mouseActive = true;
|
||||
[System.Serializable]
|
||||
public struct CameraPreset
|
||||
{
|
||||
public Vector3 position;
|
||||
public Quaternion rotation;
|
||||
}
|
||||
|
||||
public bool lockUserInput = false;
|
||||
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (Instance == null) {
|
||||
Instance = this;
|
||||
DontDestroyOnLoad(gameObject);
|
||||
} else {
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
// Start is called before the first frame update
|
||||
void Start()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
HandleInput();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void HandleMouseInput()
|
||||
{
|
||||
if (Input.GetMouseButton(0))
|
||||
{
|
||||
CameraController.Instance.OrbitCamera(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y"));
|
||||
|
||||
}
|
||||
else if (Input.GetMouseButton(1))
|
||||
{
|
||||
CameraController.Instance.RotateCamera(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void HandleInput()
|
||||
{
|
||||
if (!lockUserInput)
|
||||
{
|
||||
HandleLockableInput();
|
||||
}
|
||||
HandleNonLockableInput();
|
||||
}
|
||||
|
||||
void HandleScrollWheelInput()
|
||||
{
|
||||
if (Input.GetAxis("Mouse ScrollWheel") != 0)
|
||||
{
|
||||
CameraController.Instance.ZoomCamera(Input.GetAxis("Mouse ScrollWheel") * 500);
|
||||
}
|
||||
}
|
||||
|
||||
void HandleLockableInput()
|
||||
{
|
||||
|
||||
if(mouseActive)
|
||||
{
|
||||
HandleMouseInput();
|
||||
}
|
||||
|
||||
if (Input.GetKey(KeyCode.LeftShift))
|
||||
{
|
||||
CameraController.Instance.SetCameraSpeed(CameraController.Instance.GetCameraSpeedMax());
|
||||
}
|
||||
else
|
||||
{
|
||||
CameraController.Instance.SetCameraSpeed(CameraController.Instance.GetCameraSpeedNormal());
|
||||
}
|
||||
|
||||
// TRANSLATIONAL MOVEMENT
|
||||
if (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow))
|
||||
{
|
||||
CameraController.Instance.TranslateCamera(CameraController.TranslationInput.Forward);
|
||||
}
|
||||
if (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow))
|
||||
{
|
||||
CameraController.Instance.TranslateCamera(CameraController.TranslationInput.Left);
|
||||
}
|
||||
if (Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.DownArrow))
|
||||
{
|
||||
CameraController.Instance.TranslateCamera(CameraController.TranslationInput.Back);
|
||||
}
|
||||
if (Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.RightArrow))
|
||||
{
|
||||
CameraController.Instance.TranslateCamera(CameraController.TranslationInput.Right);
|
||||
}
|
||||
if (Input.GetKey(KeyCode.Q))
|
||||
{
|
||||
CameraController.Instance.TranslateCamera(CameraController.TranslationInput.Up);
|
||||
}
|
||||
if (Input.GetKey(KeyCode.E))
|
||||
{
|
||||
CameraController.Instance.TranslateCamera(CameraController.TranslationInput.Down);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void HandleNonLockableInput()
|
||||
{
|
||||
HandleScrollWheelInput();
|
||||
|
||||
if(Input.GetKeyDown(KeyCode.Escape))
|
||||
{
|
||||
Application.Quit();
|
||||
}
|
||||
|
||||
if (Input.GetKeyDown(KeyCode.C))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
if(Input.GetKeyDown(KeyCode.R))
|
||||
{
|
||||
SimManager.Instance.RestartSimulation();
|
||||
}
|
||||
|
||||
if (Input.GetKeyDown(KeyCode.L)) // 'L' for Load
|
||||
{
|
||||
UIManager.Instance.ToggleConfigSelectorPanel();
|
||||
}
|
||||
|
||||
if (Input.GetKeyDown(KeyCode.Space))
|
||||
{
|
||||
// Pause the time
|
||||
if (SimManager.Instance.IsSimulationRunning()) {
|
||||
SimManager.Instance.PauseSimulation();
|
||||
} else {
|
||||
SimManager.Instance.ResumeSimulation();
|
||||
}
|
||||
}
|
||||
|
||||
if (Input.GetKeyDown(KeyCode.P))
|
||||
{
|
||||
CameraController.Instance.SetAutoRotate(!CameraController.Instance.IsAutoRotate());
|
||||
}
|
||||
|
||||
if (Input.GetKeyDown(KeyCode.Alpha1))
|
||||
{
|
||||
// ORIGIN
|
||||
// Set pre-set view 1
|
||||
}
|
||||
|
||||
if (Input.GetKeyDown(KeyCode.Alpha2))
|
||||
{
|
||||
// Set pre-set view 2
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (Input.GetKeyDown(KeyCode.Alpha4))
|
||||
{
|
||||
// Set pre-set view 4
|
||||
}
|
||||
|
||||
if (Input.GetKeyDown(KeyCode.Alpha5))
|
||||
{
|
||||
// Set pre-set view 5
|
||||
}
|
||||
|
||||
if (Input.GetKeyDown(KeyCode.Alpha6))
|
||||
{
|
||||
// Set pre-set view 6
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6a028bd12cca23f45a9eeb9fb0c4d1f1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
263
coverage/playmode/Assets/Scripts/Monitor.cs
Normal file
263
coverage/playmode/Assets/Scripts/Monitor.cs
Normal file
@@ -0,0 +1,263 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.IO;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
public class SimMonitor : MonoBehaviour
|
||||
{
|
||||
private const float _updateRate = 0.1f; // 100 Hz
|
||||
private string _telemetryBinPath;
|
||||
private string _eventLogPath;
|
||||
private Coroutine _monitorRoutine;
|
||||
|
||||
private string _sessionDirectory;
|
||||
|
||||
|
||||
private FileStream _telemetryFileStream;
|
||||
private BinaryWriter _telemetryBinaryWriter;
|
||||
|
||||
[SerializeField]
|
||||
private List<EventRecord> _eventLogCache;
|
||||
|
||||
[System.Serializable]
|
||||
private class EventRecord
|
||||
{
|
||||
public float Time;
|
||||
public float PositionX;
|
||||
public float PositionY;
|
||||
public float PositionZ;
|
||||
public string EventType;
|
||||
public string Details;
|
||||
}
|
||||
|
||||
private void Awake() {
|
||||
InitializeSessionDirectory();
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
SimManager.Instance.OnSimulationStarted += RegisterSimulationStarted;
|
||||
SimManager.Instance.OnSimulationEnded += RegisterSimulationEnded;
|
||||
SimManager.Instance.OnNewThreat += RegisterNewThreat;
|
||||
SimManager.Instance.OnNewInterceptor += RegisterNewInterceptor;
|
||||
}
|
||||
|
||||
private void InitializeSessionDirectory() {
|
||||
string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
|
||||
_sessionDirectory = Application.persistentDataPath + $"\\Telemetry\\Logs\\{timestamp}";
|
||||
Directory.CreateDirectory(_sessionDirectory);
|
||||
Debug.Log($"Monitoring simulation logs to {_sessionDirectory}");
|
||||
}
|
||||
|
||||
private void InitializeLogFiles()
|
||||
{
|
||||
string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
|
||||
|
||||
_eventLogPath = Path.Combine(_sessionDirectory, $"sim_events_{timestamp}.csv");
|
||||
|
||||
// Initialize the event log cache
|
||||
_eventLogCache = new List<EventRecord>();
|
||||
|
||||
_telemetryBinPath = Path.Combine(_sessionDirectory, $"sim_telemetry_{timestamp}.bin");
|
||||
|
||||
// Open the file stream and binary writer for telemetry data
|
||||
_telemetryFileStream = new FileStream(_telemetryBinPath, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
|
||||
_telemetryBinaryWriter = new BinaryWriter(_telemetryFileStream);
|
||||
|
||||
Debug.Log("Log files initialized successfully.");
|
||||
}
|
||||
|
||||
private void CloseLogFiles() {
|
||||
if (_telemetryBinaryWriter != null)
|
||||
{
|
||||
_telemetryBinaryWriter.Flush();
|
||||
_telemetryBinaryWriter.Close();
|
||||
_telemetryBinaryWriter = null;
|
||||
}
|
||||
|
||||
if (_telemetryFileStream != null)
|
||||
{
|
||||
_telemetryFileStream.Close();
|
||||
_telemetryFileStream = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private IEnumerator MonitorRoutine()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
RecordTelemetry();
|
||||
yield return new WaitForSeconds(_updateRate);
|
||||
}
|
||||
}
|
||||
|
||||
private void RecordTelemetry()
|
||||
{
|
||||
float time = (float)SimManager.Instance.GetElapsedSimulationTime();
|
||||
var agents = SimManager.Instance.GetActiveAgents();
|
||||
if(_telemetryBinaryWriter == null) {
|
||||
Debug.LogWarning("Telemetry binary writer is null");
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < agents.Count; i++)
|
||||
{
|
||||
var agent = agents[i];
|
||||
|
||||
if (!agent.gameObject.activeInHierarchy)
|
||||
continue;
|
||||
|
||||
Vector3 pos = agent.transform.position;
|
||||
|
||||
if (pos == Vector3.zero)
|
||||
continue;
|
||||
|
||||
Vector3 vel = agent.GetVelocity(); // Ensure GetVelocity() doesn't allocate
|
||||
|
||||
int agentID = agent.GetInstanceID();
|
||||
int flightPhase = (int)agent.GetFlightPhase();
|
||||
byte agentType = (byte)(agent is Threat ? 0 : 1);
|
||||
|
||||
// Write telemetry data directly to the binary file
|
||||
_telemetryBinaryWriter.Write(time);
|
||||
_telemetryBinaryWriter.Write(agentID);
|
||||
_telemetryBinaryWriter.Write(pos.x);
|
||||
_telemetryBinaryWriter.Write(pos.y);
|
||||
_telemetryBinaryWriter.Write(pos.z);
|
||||
_telemetryBinaryWriter.Write(vel.x);
|
||||
_telemetryBinaryWriter.Write(vel.y);
|
||||
_telemetryBinaryWriter.Write(vel.z);
|
||||
_telemetryBinaryWriter.Write(flightPhase);
|
||||
_telemetryBinaryWriter.Write(agentType);
|
||||
}
|
||||
}
|
||||
|
||||
public void ConvertBinaryTelemetryToCsv(string binaryFilePath, string csvFilePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
using FileStream fs = new FileStream(binaryFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
using BinaryReader reader = new BinaryReader(fs);
|
||||
using StreamWriter writer = new StreamWriter(csvFilePath, false);
|
||||
{
|
||||
// Write CSV header
|
||||
writer.WriteLine("Time,AgentID,AgentX,AgentY,AgentZ,AgentVX,AgentVY,AgentVZ,AgentState,AgentType");
|
||||
|
||||
while (reader.BaseStream.Position != reader.BaseStream.Length)
|
||||
{
|
||||
float time = reader.ReadSingle();
|
||||
int agentID = reader.ReadInt32();
|
||||
float posX = reader.ReadSingle();
|
||||
float posY = reader.ReadSingle();
|
||||
float posZ = reader.ReadSingle();
|
||||
float velX = reader.ReadSingle();
|
||||
float velY = reader.ReadSingle();
|
||||
float velZ = reader.ReadSingle();
|
||||
int flightPhase = reader.ReadInt32();
|
||||
byte agentTypeByte = reader.ReadByte();
|
||||
string agentType = agentTypeByte == 0 ? "T" : "M";
|
||||
|
||||
// Write the data to CSV
|
||||
writer.WriteLine($"{time:F2},{agentID},{posX:F2},{posY:F2},{posZ:F2},{velX:F2},{velY:F2},{velZ:F2},{flightPhase},{agentType}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
Debug.LogWarning($"An IO error occurred while converting binary telemetry to CSV: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteEventsToFile()
|
||||
{
|
||||
using (StreamWriter writer = new StreamWriter(_eventLogPath, false))
|
||||
{
|
||||
// Write CSV header
|
||||
writer.WriteLine("Time,PositionX,PositionY,PositionZ,Event,Details");
|
||||
|
||||
foreach (var record in _eventLogCache)
|
||||
{
|
||||
writer.WriteLine($"{record.Time:F2},{record.PositionX:F2},{record.PositionY:F2},{record.PositionZ:F2},{record.EventType},{record.Details}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RegisterSimulationStarted()
|
||||
{
|
||||
InitializeLogFiles();
|
||||
_monitorRoutine = StartCoroutine(MonitorRoutine());
|
||||
}
|
||||
|
||||
private void RegisterSimulationEnded()
|
||||
{
|
||||
StopCoroutine(_monitorRoutine);
|
||||
CloseLogFiles();
|
||||
WriteEventsToFile();
|
||||
StartCoroutine(ConvertBinaryTelemetryToCsvCoroutine(_telemetryBinPath, Path.ChangeExtension(_telemetryBinPath, ".csv")));
|
||||
}
|
||||
|
||||
private IEnumerator ConvertBinaryTelemetryToCsvCoroutine(string binaryFilePath, string csvFilePath)
|
||||
{
|
||||
yield return null; // Wait for the next frame to ensure RecordTelemetry() has finished
|
||||
ConvertBinaryTelemetryToCsv(binaryFilePath, csvFilePath);
|
||||
}
|
||||
|
||||
public void RegisterNewThreat(Threat threat) {
|
||||
RegisterNewAgent(threat, "NEW_THREAT");
|
||||
}
|
||||
|
||||
public void RegisterNewInterceptor(Interceptor interceptor) {
|
||||
RegisterNewAgent(interceptor, "NEW_INTERCEPTOR");
|
||||
interceptor.OnInterceptMiss += RegisterInterceptorMiss;
|
||||
interceptor.OnInterceptHit += RegisterInterceptorHit;
|
||||
}
|
||||
|
||||
private void RegisterNewAgent(Agent agent, string eventType)
|
||||
{
|
||||
float time = (float)SimManager.Instance.GetElapsedSimulationTime();
|
||||
Vector3 pos = agent.transform.position;
|
||||
var record = new EventRecord
|
||||
{
|
||||
Time = time,
|
||||
PositionX = pos.x,
|
||||
PositionY = pos.y,
|
||||
PositionZ = pos.z,
|
||||
EventType = eventType,
|
||||
Details = agent.name
|
||||
};
|
||||
_eventLogCache.Add(record);
|
||||
}
|
||||
|
||||
public void RegisterInterceptorHit(Interceptor interceptor, Threat threat) {
|
||||
RegisterInterceptEvent(interceptor, threat, true);
|
||||
}
|
||||
|
||||
public void RegisterInterceptorMiss(Interceptor interceptor, Threat threat) {
|
||||
RegisterInterceptEvent(interceptor, threat, false);
|
||||
}
|
||||
|
||||
public void RegisterInterceptEvent(Interceptor interceptor, Threat threat, bool hit)
|
||||
{
|
||||
float time = (float)SimManager.Instance.GetElapsedSimulationTime();
|
||||
Vector3 pos = interceptor.transform.position;
|
||||
string eventType = hit ? "HIT" : "MISS";
|
||||
var record = new EventRecord
|
||||
{
|
||||
Time = time,
|
||||
PositionX = pos.x,
|
||||
PositionY = pos.y,
|
||||
PositionZ = pos.z,
|
||||
EventType = eventType,
|
||||
Details = $"{interceptor.name} and {threat.name}"
|
||||
};
|
||||
_eventLogCache.Add(record);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
CloseLogFiles();
|
||||
}
|
||||
}
|
||||
11
coverage/playmode/Assets/Scripts/Monitor.cs.meta
Normal file
11
coverage/playmode/Assets/Scripts/Monitor.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d822a6be5cb96f54ca63228eeb5ebf23
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: -5
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
coverage/playmode/Assets/Scripts/Sensors.meta
Normal file
8
coverage/playmode/Assets/Scripts/Sensors.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8c45dceac2c26594d8ac013a8f72e042
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
71
coverage/playmode/Assets/Scripts/Sensors/IdealSensor.cs
Normal file
71
coverage/playmode/Assets/Scripts/Sensors/IdealSensor.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class IdealSensor : Sensor {
|
||||
protected override void Start() {
|
||||
base.Start();
|
||||
}
|
||||
|
||||
public override SensorOutput Sense(Agent target) {
|
||||
SensorOutput targetSensorOutput = new SensorOutput();
|
||||
|
||||
// Sense the target's position
|
||||
PositionOutput targetPositionSensorOutput = SensePosition(target);
|
||||
targetSensorOutput.position = targetPositionSensorOutput;
|
||||
|
||||
// Sense the target's velocity
|
||||
VelocityOutput targetVelocitySensorOutput = SenseVelocity(target);
|
||||
targetSensorOutput.velocity = targetVelocitySensorOutput;
|
||||
|
||||
return targetSensorOutput;
|
||||
}
|
||||
|
||||
protected override PositionOutput SensePosition(Agent target) {
|
||||
PositionOutput positionSensorOutput = new PositionOutput();
|
||||
|
||||
// Calculate the relative position of the target
|
||||
Vector3 relativePosition = target.transform.position - transform.position;
|
||||
|
||||
// Calculate the distance (range) to the target
|
||||
positionSensorOutput.range = relativePosition.magnitude;
|
||||
|
||||
// Calculate azimuth (horizontal angle relative to forward)
|
||||
positionSensorOutput.azimuth =
|
||||
Vector3.SignedAngle(transform.forward, relativePosition, transform.up);
|
||||
|
||||
// Calculate elevation (vertical angle relative to forward)
|
||||
Vector3 flatRelativePosition = Vector3.ProjectOnPlane(relativePosition, transform.up);
|
||||
positionSensorOutput.elevation =
|
||||
Vector3.SignedAngle(flatRelativePosition, relativePosition, transform.right);
|
||||
|
||||
return positionSensorOutput;
|
||||
}
|
||||
|
||||
protected override VelocityOutput SenseVelocity(Agent target) {
|
||||
VelocityOutput velocitySensorOutput = new VelocityOutput();
|
||||
|
||||
// Calculate relative position and velocity
|
||||
Vector3 relativePosition = target.transform.position - transform.position;
|
||||
Vector3 relativeVelocity = target.GetVelocity() - GetComponent<Rigidbody>().linearVelocity;
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
11
coverage/playmode/Assets/Scripts/Sensors/IdealSensor.cs.meta
Normal file
11
coverage/playmode/Assets/Scripts/Sensors/IdealSensor.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f9b285afc8c96ee4f9ca22836fd105d0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
70
coverage/playmode/Assets/Scripts/Sensors/Sensor.cs
Normal file
70
coverage/playmode/Assets/Scripts/Sensors/Sensor.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using UnityEngine;
|
||||
|
||||
public abstract class Sensor : MonoBehaviour {
|
||||
protected Agent _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>
|
||||
/// 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);
|
||||
}
|
||||
|
||||
public struct SensorOutput {
|
||||
public PositionOutput position;
|
||||
public VelocityOutput velocity;
|
||||
}
|
||||
|
||||
public struct PositionOutput {
|
||||
public float range;
|
||||
public float azimuth;
|
||||
public float elevation;
|
||||
}
|
||||
|
||||
public struct VelocityOutput {
|
||||
public float range;
|
||||
public float azimuth;
|
||||
public float elevation;
|
||||
}
|
||||
11
coverage/playmode/Assets/Scripts/Sensors/Sensor.cs.meta
Normal file
11
coverage/playmode/Assets/Scripts/Sensors/Sensor.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 285bc14e5585a804b9bc44b10b8c3cb1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
326
coverage/playmode/Assets/Scripts/SimManager.cs
Normal file
326
coverage/playmode/Assets/Scripts/SimManager.cs
Normal file
@@ -0,0 +1,326 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Manages the simulation by handling missiles, targets, and their assignments.
|
||||
/// Implements the Singleton pattern to ensure only one instance exists.
|
||||
/// </summary>
|
||||
public class SimManager : MonoBehaviour {
|
||||
/// <summary>
|
||||
/// Singleton instance of SimManager.
|
||||
/// </summary>
|
||||
public static SimManager Instance { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Configuration settings for the simulation.
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
public SimulationConfig simulationConfig;
|
||||
|
||||
|
||||
private List<Interceptor> _activeInterceptors = new List<Interceptor>();
|
||||
|
||||
|
||||
private List<Interceptor> _interceptorObjects = new List<Interceptor>();
|
||||
private List<Threat> _threatObjects = new List<Threat>();
|
||||
|
||||
private float _elapsedSimulationTime = 0f;
|
||||
private float endTime = 100f; // Set an appropriate end time
|
||||
private bool simulationRunning = false;
|
||||
|
||||
|
||||
|
||||
public delegate void SimulationEventHandler();
|
||||
public event SimulationEventHandler OnSimulationEnded;
|
||||
public event SimulationEventHandler OnSimulationStarted;
|
||||
|
||||
public delegate void NewThreatEventHandler(Threat threat);
|
||||
public event NewThreatEventHandler OnNewThreat;
|
||||
|
||||
|
||||
public delegate void NewInterceptorEventHandler(Interceptor interceptor);
|
||||
public event NewInterceptorEventHandler OnNewInterceptor;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the elapsed simulation time.
|
||||
/// </summary>
|
||||
/// <returns>The elapsed time in seconds.</returns>
|
||||
public double GetElapsedSimulationTime() {
|
||||
return _elapsedSimulationTime;
|
||||
}
|
||||
|
||||
public List<Interceptor> GetActiveInterceptors() {
|
||||
return _activeInterceptors;
|
||||
}
|
||||
|
||||
public List<Threat> GetActiveThreats() {
|
||||
return _threatObjects.Where(threat => !threat.IsHit()).ToList();
|
||||
}
|
||||
|
||||
public List<Agent> GetActiveAgents() {
|
||||
return _activeInterceptors.ConvertAll(interceptor => interceptor as Agent)
|
||||
.Concat(GetActiveThreats().ConvertAll(threat => threat as Agent))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
void Awake() {
|
||||
// Ensure only one instance of SimManager exists
|
||||
if (Instance == null) {
|
||||
Instance = this;
|
||||
DontDestroyOnLoad(gameObject);
|
||||
} else {
|
||||
Destroy(gameObject);
|
||||
}
|
||||
simulationConfig = ConfigLoader.LoadSimulationConfig("1_salvo_1_hydra_7_drones.json");
|
||||
Debug.Log(simulationConfig);
|
||||
}
|
||||
|
||||
void Start() {
|
||||
// Slow down time by simulationConfig.timeScale
|
||||
if (Instance == this) {
|
||||
StartSimulation();
|
||||
ResumeSimulation();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void SetTimeScale(float timeScale) {
|
||||
Time.timeScale = timeScale;
|
||||
Time.fixedDeltaTime = Time.timeScale * 0.02f;
|
||||
Time.maximumDeltaTime = Time.timeScale * 0.15f;
|
||||
}
|
||||
|
||||
public void StartSimulation() {
|
||||
OnSimulationStarted?.Invoke();
|
||||
InitializeSimulation();
|
||||
|
||||
}
|
||||
|
||||
public void PauseSimulation() {
|
||||
SetTimeScale(0);
|
||||
simulationRunning = false;
|
||||
}
|
||||
|
||||
public void ResumeSimulation() {
|
||||
SetTimeScale(simulationConfig.timeScale);
|
||||
simulationRunning = true;
|
||||
}
|
||||
|
||||
public bool IsSimulationRunning() {
|
||||
return simulationRunning;
|
||||
}
|
||||
|
||||
private void InitializeSimulation() {
|
||||
// Invoke the simulation started event to let listeners
|
||||
// know to invoke their own handler behavior
|
||||
OnSimulationStarted?.Invoke();
|
||||
List<Interceptor> missiles = new List<Interceptor>();
|
||||
// Create missiles based on config
|
||||
foreach (var swarmConfig in simulationConfig.interceptor_swarm_configs) {
|
||||
for (int i = 0; i < swarmConfig.num_agents; i++) {
|
||||
CreateInterceptor(swarmConfig.agent_config);
|
||||
}
|
||||
}
|
||||
|
||||
List<Threat> targets = new List<Threat>();
|
||||
// Create targets based on config
|
||||
foreach (var swarmConfig in simulationConfig.threat_swarm_configs) {
|
||||
for (int i = 0; i < swarmConfig.num_agents; i++) {
|
||||
CreateThreat(swarmConfig.agent_config);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void AssignInterceptorsToThreats() {
|
||||
IADS.Instance.AssignInterceptorsToThreats(_interceptorObjects);
|
||||
}
|
||||
|
||||
public void RegisterInterceptorHit(Interceptor interceptor, Threat threat) {
|
||||
if (interceptor is Interceptor missileComponent) {
|
||||
_activeInterceptors.Remove(missileComponent);
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterInterceptorMiss(Interceptor interceptor, Threat threat) {
|
||||
if (interceptor is Interceptor missileComponent) {
|
||||
_activeInterceptors.Remove(missileComponent);
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterThreatHit(Interceptor interceptor, Threat threat) {
|
||||
// Placeholder
|
||||
}
|
||||
|
||||
public void RegisterThreatMiss(Interceptor interceptor, Threat threat) {
|
||||
Debug.Log($"RegisterThreatMiss: Interceptor {interceptor.name} missed threat {threat.name}");
|
||||
// Placeholder
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates a interceptor based on the provided configuration.
|
||||
/// </summary>
|
||||
/// <param name="config">Configuration settings for the interceptor.</param>
|
||||
/// <returns>The created Interceptor instance, or null if creation failed.</returns>
|
||||
public Interceptor CreateInterceptor(AgentConfig config) {
|
||||
string prefabName = config.interceptor_type switch { InterceptorType.HYDRA_70 => "Hydra70",
|
||||
InterceptorType.MICROMISSILE => "Micromissile",
|
||||
_ => "Hydra70" };
|
||||
|
||||
GameObject interceptorObject = CreateAgent(config, prefabName);
|
||||
if (interceptorObject == null)
|
||||
return null;
|
||||
|
||||
// Interceptor-specific logic
|
||||
switch (config.dynamic_config.sensor_config.type) {
|
||||
case SensorType.IDEAL:
|
||||
interceptorObject.AddComponent<IdealSensor>();
|
||||
break;
|
||||
default:
|
||||
Debug.LogError($"Sensor type '{config.dynamic_config.sensor_config.type}' not found.");
|
||||
break;
|
||||
}
|
||||
|
||||
Interceptor interceptor = interceptorObject.GetComponent<Interceptor>();
|
||||
_interceptorObjects.Add(interceptor);
|
||||
_activeInterceptors.Add(interceptor);
|
||||
|
||||
|
||||
// Subscribe events
|
||||
interceptor.OnInterceptHit += RegisterInterceptorHit;
|
||||
interceptor.OnInterceptMiss += RegisterInterceptorMiss;
|
||||
|
||||
// Assign a unique and simple ID
|
||||
int interceptorId = _interceptorObjects.Count;
|
||||
interceptorObject.name = $"{config.interceptor_type}_Interceptor_{interceptorId}";
|
||||
|
||||
// Let listeners know a new interceptor has been created
|
||||
OnNewInterceptor?.Invoke(interceptor);
|
||||
|
||||
return interceptorObject.GetComponent<Interceptor>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a threat based on the provided configuration.
|
||||
/// </summary>
|
||||
/// <param name="config">Configuration settings for the threat.</param>
|
||||
/// <returns>The created Threat instance, or null if creation failed.</returns>
|
||||
private Threat CreateThreat(AgentConfig config) {
|
||||
string prefabName = config.threat_type switch {
|
||||
ThreatType.DRONE => "Drone", ThreatType.ANTISHIP_MISSILE => "AntishipMissile",
|
||||
_ => throw new System.ArgumentException($"Unsupported threat type: {config.threat_type}")
|
||||
};
|
||||
GameObject threatObject = CreateAgent(config, prefabName);
|
||||
if (threatObject == null)
|
||||
return null;
|
||||
|
||||
Threat threat = threatObject.GetComponent<Threat>();
|
||||
// Assign a unique and simple ID
|
||||
int targetId = _threatObjects.Count;
|
||||
threatObject.name = $"{config.threat_type}_Target_{targetId}";
|
||||
|
||||
ThreatData threatData = new ThreatData(threat, threatObject.name);
|
||||
_threatObjects.Add(threat);
|
||||
|
||||
// Subscribe events
|
||||
threat.OnInterceptHit += RegisterThreatHit;
|
||||
threat.OnInterceptMiss += RegisterThreatMiss;
|
||||
|
||||
// Let listeners know a new threat has been created
|
||||
OnNewThreat?.Invoke(threat);
|
||||
|
||||
return threatObject.GetComponent<Threat>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an agent (interceptor or threat) based on the provided configuration and prefab name.
|
||||
/// </summary>
|
||||
/// <param name="config">Configuration settings for the agent.</param>
|
||||
/// <param name="prefabName">Name of the prefab to instantiate.</param>
|
||||
/// <returns>The created GameObject instance, or null if creation failed.</returns>
|
||||
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.linearVelocity = noisyVelocity;
|
||||
|
||||
agentObject.GetComponent<Agent>().SetAgentConfig(config);
|
||||
|
||||
return agentObject;
|
||||
}
|
||||
|
||||
public void LoadNewConfig(string configFileName) {
|
||||
simulationConfig = ConfigLoader.LoadSimulationConfig(configFileName);
|
||||
if (simulationConfig != null) {
|
||||
Debug.Log($"Loaded new configuration: {configFileName}");
|
||||
RestartSimulation();
|
||||
} else {
|
||||
Debug.LogError($"Failed to load configuration: {configFileName}");
|
||||
}
|
||||
}
|
||||
|
||||
public void RestartSimulation() {
|
||||
OnSimulationEnded?.Invoke();
|
||||
Debug.Log("Simulation ended");
|
||||
// Reset simulation time
|
||||
_elapsedSimulationTime = 0f;
|
||||
simulationRunning = IsSimulationRunning();
|
||||
|
||||
// Clear existing interceptors and threats
|
||||
foreach (var interceptor in _interceptorObjects) {
|
||||
if (interceptor != null) {
|
||||
Destroy(interceptor.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var threat in _threatObjects) {
|
||||
if (threat != null) {
|
||||
Destroy(threat.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
_interceptorObjects.Clear();
|
||||
_activeInterceptors.Clear();
|
||||
_threatObjects.Clear();
|
||||
|
||||
StartSimulation();
|
||||
}
|
||||
|
||||
void Update() {
|
||||
// Check if all missiles have terminated
|
||||
bool allInterceptorsTerminated = true;
|
||||
foreach (var interceptor in _interceptorObjects) {
|
||||
if (interceptor != null && interceptor.GetFlightPhase() != Agent.FlightPhase.TERMINATED) {
|
||||
allInterceptorsTerminated = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If all missiles have terminated, restart the simulation
|
||||
if (allInterceptorsTerminated) {
|
||||
RestartSimulation();
|
||||
}
|
||||
|
||||
if (simulationRunning && _elapsedSimulationTime < endTime) {
|
||||
_elapsedSimulationTime += Time.deltaTime;
|
||||
} else if (_elapsedSimulationTime >= endTime) {
|
||||
simulationRunning = false;
|
||||
Debug.Log("Simulation completed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
11
coverage/playmode/Assets/Scripts/SimManager.cs.meta
Normal file
11
coverage/playmode/Assets/Scripts/SimManager.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 84f2990fd69b0284ca96912cbe968b62
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 100
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
coverage/playmode/Assets/Scripts/Threats.meta
Normal file
8
coverage/playmode/Assets/Scripts/Threats.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2342a881813cdd645962533af3f6f755
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
11
coverage/playmode/Assets/Scripts/Threats/AntishipMissile.cs
Normal file
11
coverage/playmode/Assets/Scripts/Threats/AntishipMissile.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class AntishipMissile : MonoBehaviour {
|
||||
// Start is called before the first frame update
|
||||
void Start() {}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update() {}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 16e40584b2154ef4a95c84e85a321999
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
21
coverage/playmode/Assets/Scripts/Threats/DroneTarget.cs
Normal file
21
coverage/playmode/Assets/Scripts/Threats/DroneTarget.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class DroneTarget : Threat {
|
||||
// Start is called before the first frame update
|
||||
protected override void Start() {
|
||||
base.Start();
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
protected override void FixedUpdate() {
|
||||
base.FixedUpdate();
|
||||
}
|
||||
|
||||
protected override void UpdateReady(double deltaTime) {}
|
||||
|
||||
protected override void UpdateBoost(double deltaTime) {}
|
||||
|
||||
protected override void UpdateMidCourse(double deltaTime) {}
|
||||
}
|
||||
11
coverage/playmode/Assets/Scripts/Threats/DroneTarget.cs.meta
Normal file
11
coverage/playmode/Assets/Scripts/Threats/DroneTarget.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f532f6ee629e87643b7b75a50e83083f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
17
coverage/playmode/Assets/Scripts/Threats/Threat.cs
Normal file
17
coverage/playmode/Assets/Scripts/Threats/Threat.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public abstract class Threat : Agent {
|
||||
public override bool IsAssignable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override void Start() {
|
||||
base.Start();
|
||||
}
|
||||
|
||||
protected override void FixedUpdate() {
|
||||
base.FixedUpdate();
|
||||
}
|
||||
}
|
||||
11
coverage/playmode/Assets/Scripts/Threats/Threat.cs.meta
Normal file
11
coverage/playmode/Assets/Scripts/Threats/Threat.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ad412917d9c68a144b3124e83ca083c1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
coverage/playmode/Assets/Scripts/UI.meta
Normal file
8
coverage/playmode/Assets/Scripts/UI.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c17270294c7dd6d45b8731a468423a83
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
490
coverage/playmode/Assets/Scripts/UI/CameraController.cs
Normal file
490
coverage/playmode/Assets/Scripts/UI/CameraController.cs
Normal file
@@ -0,0 +1,490 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class CameraController : MonoBehaviour
|
||||
{
|
||||
#region Singleton
|
||||
|
||||
/// <summary>
|
||||
/// Singleton instance of the CameraController.
|
||||
/// </summary>
|
||||
public static CameraController Instance { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Camera Settings
|
||||
|
||||
/// <summary>
|
||||
/// Determines if mouse input is active for camera control.
|
||||
/// </summary>
|
||||
public bool mouseActive = true;
|
||||
|
||||
/// <summary>
|
||||
/// Locks user input for camera control.
|
||||
/// </summary>
|
||||
public bool lockUserInput = false;
|
||||
|
||||
/// <summary>
|
||||
/// Normal speed of camera movement.
|
||||
/// </summary>
|
||||
[SerializeField] private float _cameraSpeedNormal = 100.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum speed of camera movement.
|
||||
/// </summary>
|
||||
[SerializeField] private float _cameraSpeedMax = 1000.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Current speed of camera movement.
|
||||
/// </summary>
|
||||
private float _cameraSpeed;
|
||||
|
||||
/// <summary>
|
||||
/// Horizontal rotation speed.
|
||||
/// </summary>
|
||||
public float _speedH = 2.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Vertical rotation speed.
|
||||
/// </summary>
|
||||
public float _speedV = 2.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Current yaw angle of the camera.
|
||||
/// </summary>
|
||||
private float _yaw = 0.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Current pitch angle of the camera.
|
||||
/// </summary>
|
||||
private float _pitch = 0.0f;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Orbit Settings
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the camera should auto-rotate.
|
||||
/// </summary>
|
||||
public bool _autoRotate = false;
|
||||
|
||||
/// <summary>
|
||||
/// Threat transform for orbit rotation.
|
||||
/// </summary>
|
||||
public Transform target;
|
||||
|
||||
/// <summary>
|
||||
/// Distance from the camera to the orbit target.
|
||||
/// </summary>
|
||||
[SerializeField] private float _orbitDistance = 5.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Horizontal orbit rotation speed.
|
||||
/// </summary>
|
||||
[SerializeField] private float _orbitXSpeed = 120.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Vertical orbit rotation speed.
|
||||
/// </summary>
|
||||
[SerializeField] private float _orbitYSpeed = 120.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Speed of camera zoom.
|
||||
/// </summary>
|
||||
[SerializeField] private float _zoomSpeed = 500.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum vertical angle limit for orbit.
|
||||
/// </summary>
|
||||
public float orbitYMinLimit = -20f;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum vertical angle limit for orbit.
|
||||
/// </summary>
|
||||
public float orbitYMaxLimit = 80f;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum distance for orbit.
|
||||
/// </summary>
|
||||
private float _orbitDistanceMin = 10f;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum distance for orbit.
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
private float _orbitDistanceMax = 20000f;
|
||||
|
||||
/// <summary>
|
||||
/// Current horizontal orbit angle.
|
||||
/// </summary>
|
||||
private float _orbitX = 0.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Current vertical orbit angle.
|
||||
/// </summary>
|
||||
private float _orbitY = 0.0f;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Rendering
|
||||
|
||||
/// <summary>
|
||||
/// Renderer for the orbit target.
|
||||
/// </summary>
|
||||
public Renderer targetRenderer;
|
||||
|
||||
/// <summary>
|
||||
/// Renderer for the floor.
|
||||
/// </summary>
|
||||
public Renderer floorRenderer;
|
||||
|
||||
/// <summary>
|
||||
/// Alpha value for material transparency.
|
||||
/// </summary>
|
||||
public float matAlpha;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Autoplay Settings
|
||||
|
||||
/// <summary>
|
||||
/// Speed of camera movement during autoplay.
|
||||
/// </summary>
|
||||
public float autoplayCamSpeed = 2f;
|
||||
|
||||
/// <summary>
|
||||
/// Duration of horizontal auto-rotation.
|
||||
/// </summary>
|
||||
public float xAutoRotateTime = 5f;
|
||||
|
||||
/// <summary>
|
||||
/// Duration of vertical auto-rotation.
|
||||
/// </summary>
|
||||
public float yAutoRotateTime = 5f;
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine for autoplay functionality.
|
||||
/// </summary>
|
||||
private Coroutine autoplayRoutine;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Camera Presets
|
||||
|
||||
/// <summary>
|
||||
/// Represents a preset camera position and rotation.
|
||||
/// </summary>
|
||||
[System.Serializable]
|
||||
public struct CameraPreset
|
||||
{
|
||||
public Vector3 position;
|
||||
public Quaternion rotation;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Preset camera position for key 4.
|
||||
/// </summary>
|
||||
CameraPreset fourPos = new CameraPreset();
|
||||
|
||||
/// <summary>
|
||||
/// Preset camera position for key 5.
|
||||
/// </summary>
|
||||
CameraPreset fivePos = new CameraPreset();
|
||||
|
||||
/// <summary>
|
||||
/// Preset camera position for key 6.
|
||||
/// </summary>
|
||||
CameraPreset sixPos = new CameraPreset();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Movement
|
||||
|
||||
/// <summary>
|
||||
/// Mapping of translation inputs to movement vectors.
|
||||
/// </summary>
|
||||
private Dictionary<TranslationInput, Vector3> _translationInputToVectorMap;
|
||||
|
||||
/// <summary>
|
||||
/// Forward movement vector.
|
||||
/// </summary>
|
||||
Vector3 wVector = Vector3.forward;
|
||||
|
||||
/// <summary>
|
||||
/// Left movement vector.
|
||||
/// </summary>
|
||||
Vector3 aVector = Vector3.left;
|
||||
|
||||
/// <summary>
|
||||
/// Backward movement vector.
|
||||
/// </summary>
|
||||
Vector3 sVector = Vector3.back;
|
||||
|
||||
/// <summary>
|
||||
/// Right movement vector.
|
||||
/// </summary>
|
||||
Vector3 dVector = Vector3.right;
|
||||
|
||||
/// <summary>
|
||||
/// Angle between forward vector and camera direction.
|
||||
/// </summary>
|
||||
public float forwardToCameraAngle;
|
||||
|
||||
#endregion
|
||||
|
||||
void SetCameraRotation(Quaternion rotation)
|
||||
{
|
||||
transform.rotation = rotation;
|
||||
_pitch = rotation.eulerAngles.x;
|
||||
_yaw = rotation.eulerAngles.y;
|
||||
}
|
||||
|
||||
public void SetCameraSpeed(float speed)
|
||||
{
|
||||
_cameraSpeed = speed;
|
||||
}
|
||||
|
||||
public float GetCameraSpeedMax()
|
||||
{
|
||||
return _cameraSpeedMax;
|
||||
}
|
||||
|
||||
public float GetCameraSpeedNormal()
|
||||
{
|
||||
return _cameraSpeedNormal;
|
||||
}
|
||||
|
||||
public bool IsAutoRotate()
|
||||
{
|
||||
return _autoRotate;
|
||||
}
|
||||
|
||||
public void SetAutoRotate(bool autoRotate)
|
||||
{
|
||||
if (autoRotate && !_autoRotate) {
|
||||
_autoRotate = true;
|
||||
autoplayRoutine = StartCoroutine(AutoPlayRoutine());
|
||||
} else if (!autoRotate && _autoRotate) {
|
||||
_autoRotate = false;
|
||||
StopCoroutine(autoplayRoutine);
|
||||
}
|
||||
}
|
||||
|
||||
public static float ClampAngle(float angle, float min, float max)
|
||||
{
|
||||
if (angle < -360F)
|
||||
angle += 360F;
|
||||
if (angle > 360F)
|
||||
angle -= 360F;
|
||||
return Mathf.Clamp(angle, min, max);
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (Instance == null) {
|
||||
Instance = this;
|
||||
DontDestroyOnLoad(gameObject);
|
||||
} else {
|
||||
Destroy(gameObject);
|
||||
}
|
||||
|
||||
_translationInputToVectorMap = new Dictionary<TranslationInput, Vector3> {
|
||||
{TranslationInput.Forward, wVector},
|
||||
{TranslationInput.Left, aVector},
|
||||
{TranslationInput.Back, sVector},
|
||||
{TranslationInput.Right, dVector},
|
||||
{TranslationInput.Up, Vector3.up},
|
||||
{TranslationInput.Down, Vector3.down}
|
||||
};
|
||||
}
|
||||
|
||||
// Start is called before the first frame update
|
||||
void Start()
|
||||
{
|
||||
fourPos.position = new Vector3(0, 0, 0);
|
||||
fourPos.rotation = Quaternion.Euler(0,0,0);
|
||||
fivePos.position = new Vector3(0, 0, 0);
|
||||
fivePos.rotation = Quaternion.Euler(0, 0, 0);
|
||||
sixPos.position = new Vector3(0, 0, 0);
|
||||
sixPos.rotation = Quaternion.Euler(0, 0, 0);
|
||||
|
||||
Vector3 angles = transform.eulerAngles;
|
||||
_orbitX = angles.y;
|
||||
_orbitY = angles.x;
|
||||
|
||||
UpdateTargetAlpha();
|
||||
ResetCameraTarget();
|
||||
}
|
||||
|
||||
IEnumerator AutoPlayRoutine()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
float elapsedTime = 0f;
|
||||
while (elapsedTime <= xAutoRotateTime)
|
||||
{
|
||||
_orbitX += Time.unscaledDeltaTime * autoplayCamSpeed * _orbitDistance * 0.02f;
|
||||
UpdateCamPosition(_orbitX, _orbitY);
|
||||
elapsedTime += Time.unscaledDeltaTime;
|
||||
yield return null;
|
||||
}
|
||||
elapsedTime = 0f;
|
||||
while (elapsedTime <= yAutoRotateTime)
|
||||
{
|
||||
_orbitY -= Time.unscaledDeltaTime * autoplayCamSpeed * _orbitDistance * 0.02f;
|
||||
UpdateCamPosition(_orbitX, _orbitY);
|
||||
elapsedTime += Time.unscaledDeltaTime;
|
||||
yield return null;
|
||||
}
|
||||
elapsedTime = 0f;
|
||||
while (elapsedTime <= xAutoRotateTime)
|
||||
{
|
||||
_orbitX -= Time.unscaledDeltaTime * autoplayCamSpeed * _orbitDistance * 0.02f;
|
||||
UpdateCamPosition(_orbitX, _orbitY);
|
||||
elapsedTime += Time.unscaledDeltaTime;
|
||||
yield return null;
|
||||
}
|
||||
elapsedTime = 0f;
|
||||
while (elapsedTime <= yAutoRotateTime)
|
||||
{
|
||||
_orbitY += Time.unscaledDeltaTime * autoplayCamSpeed * _orbitDistance * 0.02f;
|
||||
UpdateCamPosition(_orbitX, _orbitY);
|
||||
elapsedTime += Time.unscaledDeltaTime;
|
||||
yield return null;
|
||||
}
|
||||
yield return null;
|
||||
}
|
||||
|
||||
}
|
||||
void ResetCameraTarget()
|
||||
{
|
||||
RaycastHit hit;
|
||||
if(Physics.Raycast(transform.position, transform.forward, out hit, float.MaxValue, LayerMask.GetMask("Floor"), QueryTriggerInteraction.Ignore))
|
||||
{
|
||||
target.transform.position = hit.point;
|
||||
_orbitDistance = hit.distance;
|
||||
Vector3 angles = transform.eulerAngles;
|
||||
_orbitX = angles.y;
|
||||
_orbitY = angles.x;
|
||||
UpdateCamPosition(_orbitX, _orbitY);
|
||||
}
|
||||
else
|
||||
{
|
||||
target.transform.position = transform.position + (transform.forward * 100);
|
||||
_orbitDistance = 100;
|
||||
Vector3 angles = transform.eulerAngles;
|
||||
_orbitX = angles.y;
|
||||
_orbitY = angles.x;
|
||||
//UpdateCamPosition();
|
||||
}
|
||||
}
|
||||
|
||||
public void EnableTargetRenderer(bool enable) { targetRenderer.enabled = enable; }
|
||||
|
||||
public void EnableFloorGridRenderer(bool enable) { floorRenderer.enabled = enable; }
|
||||
|
||||
|
||||
public void OrbitCamera(float xOrbit, float yOrbit) {
|
||||
if (target)
|
||||
{
|
||||
_orbitX += xOrbit * _orbitXSpeed * _orbitDistance * 0.02f;
|
||||
_orbitY -= yOrbit * _orbitYSpeed * _orbitDistance * 0.02f;
|
||||
|
||||
_orbitY = ClampAngle(_orbitY, orbitYMinLimit, orbitYMaxLimit);
|
||||
UpdateCamPosition(_orbitX, _orbitY);
|
||||
}
|
||||
}
|
||||
|
||||
public void RotateCamera(float xRotate, float yRotate) {
|
||||
_yaw += xRotate * _speedH;
|
||||
_pitch -= yRotate * _speedV;
|
||||
transform.eulerAngles = new Vector3(_pitch, _yaw, 0.0f);
|
||||
}
|
||||
|
||||
private void UpdateCamPosition(float x, float y)
|
||||
{
|
||||
Quaternion rotation = Quaternion.Euler(y, x, 0);
|
||||
RaycastHit hit;
|
||||
//Debug.DrawLine(target.position, transform.position, Color.red);
|
||||
if (Physics.Linecast(target.position, transform.position, out hit, ~LayerMask.GetMask("Floor"), QueryTriggerInteraction.Ignore))
|
||||
{
|
||||
_orbitDistance -= hit.distance;
|
||||
}
|
||||
Vector3 negDistance = new Vector3(0.0f, 0.0f, -_orbitDistance);
|
||||
Vector3 position = rotation * negDistance + target.position;
|
||||
UpdateTargetAlpha();
|
||||
|
||||
SetCameraRotation(rotation);
|
||||
transform.position = position;
|
||||
}
|
||||
|
||||
|
||||
public void ZoomCamera(float zoom)
|
||||
{
|
||||
_orbitDistance = Mathf.Clamp(_orbitDistance - zoom * _zoomSpeed, _orbitDistanceMin, _orbitDistanceMax);
|
||||
UpdateCamPosition(_orbitX, _orbitY);
|
||||
}
|
||||
|
||||
void UpdateTargetAlpha()
|
||||
{
|
||||
matAlpha = (_orbitDistance - _orbitDistanceMin) / (_orbitDistanceMax - _orbitDistanceMin);
|
||||
matAlpha = Mathf.Max(Mathf.Sqrt(matAlpha) - 0.5f, 0);
|
||||
Color matColor = targetRenderer.material.color;
|
||||
matColor.a = matAlpha;
|
||||
targetRenderer.material.color = matColor;
|
||||
}
|
||||
|
||||
void UpdateDirectionVectors()
|
||||
{
|
||||
Vector3 cameraToTarget = target.position - transform.position;
|
||||
cameraToTarget.y = 0;
|
||||
forwardToCameraAngle = Vector3.SignedAngle(Vector3.forward, cameraToTarget, Vector3.down);
|
||||
|
||||
if(forwardToCameraAngle >-45f && forwardToCameraAngle <= 45f)
|
||||
{
|
||||
_translationInputToVectorMap[TranslationInput.Forward] = Vector3.forward;
|
||||
_translationInputToVectorMap[TranslationInput.Left] = Vector3.left;
|
||||
_translationInputToVectorMap[TranslationInput.Back] = Vector3.back;
|
||||
_translationInputToVectorMap[TranslationInput.Right] = Vector3.right;
|
||||
}
|
||||
else if(forwardToCameraAngle > 45f && forwardToCameraAngle <= 135f)
|
||||
{
|
||||
_translationInputToVectorMap[TranslationInput.Forward] = Vector3.left;
|
||||
_translationInputToVectorMap[TranslationInput.Left] = Vector3.back;
|
||||
_translationInputToVectorMap[TranslationInput.Back] = Vector3.right;
|
||||
_translationInputToVectorMap[TranslationInput.Right] = Vector3.forward;
|
||||
}
|
||||
else if(forwardToCameraAngle > 135f || forwardToCameraAngle <= -135f)
|
||||
{
|
||||
_translationInputToVectorMap[TranslationInput.Forward] = Vector3.back;
|
||||
_translationInputToVectorMap[TranslationInput.Left] = Vector3.right;
|
||||
_translationInputToVectorMap[TranslationInput.Back] = Vector3.forward;
|
||||
_translationInputToVectorMap[TranslationInput.Right] = Vector3.left;
|
||||
}
|
||||
else if(forwardToCameraAngle > -135f && forwardToCameraAngle <= -45f)
|
||||
{
|
||||
_translationInputToVectorMap[TranslationInput.Forward] = Vector3.right;
|
||||
_translationInputToVectorMap[TranslationInput.Left] = Vector3.forward;
|
||||
_translationInputToVectorMap[TranslationInput.Back] = Vector3.left;
|
||||
_translationInputToVectorMap[TranslationInput.Right] = Vector3.back;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public enum TranslationInput {
|
||||
Forward,
|
||||
Left,
|
||||
Back,
|
||||
Right,
|
||||
Up,
|
||||
Down
|
||||
}
|
||||
|
||||
public void TranslateCamera(TranslationInput input) {
|
||||
UpdateDirectionVectors();
|
||||
target.Translate(_translationInputToVectorMap[input] * Time.unscaledDeltaTime * _cameraSpeed);
|
||||
UpdateCamPosition(_orbitX, _orbitY);
|
||||
}
|
||||
|
||||
}
|
||||
11
coverage/playmode/Assets/Scripts/UI/CameraController.cs.meta
Normal file
11
coverage/playmode/Assets/Scripts/UI/CameraController.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b9e222cf9df32df4196f08036d68d740
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
coverage/playmode/Assets/Scripts/UI/Dialogs.meta
Normal file
8
coverage/playmode/Assets/Scripts/UI/Dialogs.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 27829404610ae9a479fd6f44c6da81ca
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class BotStatusDialog : UIDialog
|
||||
{
|
||||
// Start is called before the first frame update
|
||||
public override void Start()
|
||||
{
|
||||
base.Start();
|
||||
|
||||
|
||||
UISelectableEntry missiles = CreateSelectableEntry();
|
||||
missiles.SetTextContent(new List<string>(new string[] { "Interceptors" }));
|
||||
missiles.SetIsSelectable(false);
|
||||
|
||||
UISelectableEntry submunitions = CreateSelectableEntry();
|
||||
submunitions.SetTextContent(new List<string>(new string[] { "Submunitions" }));
|
||||
submunitions.SetIsSelectable(false);
|
||||
|
||||
UISelectableEntry targets = CreateSelectableEntry();
|
||||
targets.SetTextContent(new List<string>(new string[] { "Threats" }));
|
||||
targets.SetIsSelectable(false);
|
||||
|
||||
SetDialogEntries(new List<UISelectableEntry>(new UISelectableEntry[] { missiles, submunitions, targets }));
|
||||
|
||||
AddDialogTab("All", () => { });
|
||||
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a404f59a5c2ba814a9c1ca121b596df9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,23 @@
|
||||
using UnityEngine;
|
||||
public static class RectTransformExtensions
|
||||
{
|
||||
public static void SetLeft(this RectTransform rt, float left)
|
||||
{
|
||||
rt.offsetMin = new Vector2(left, rt.offsetMin.y);
|
||||
}
|
||||
|
||||
public static void SetRight(this RectTransform rt, float right)
|
||||
{
|
||||
rt.offsetMax = new Vector2(-right, rt.offsetMax.y);
|
||||
}
|
||||
|
||||
public static void SetTop(this RectTransform rt, float top)
|
||||
{
|
||||
rt.offsetMax = new Vector2(rt.offsetMax.x, -top);
|
||||
}
|
||||
|
||||
public static void SetBottom(this RectTransform rt, float bottom)
|
||||
{
|
||||
rt.offsetMin = new Vector2(rt.offsetMin.x, bottom);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c991d4191b4586742a5d8dfc8ece23bb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
18
coverage/playmode/Assets/Scripts/UI/UIBuildButton.cs
Normal file
18
coverage/playmode/Assets/Scripts/UI/UIBuildButton.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class UIBuildButton : MonoBehaviour
|
||||
{
|
||||
// Start is called before the first frame update
|
||||
void Start()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
11
coverage/playmode/Assets/Scripts/UI/UIBuildButton.cs.meta
Normal file
11
coverage/playmode/Assets/Scripts/UI/UIBuildButton.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 56ea3cedd2ebcbc42a98ea19ebc61632
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
213
coverage/playmode/Assets/Scripts/UI/UIDialog.cs
Normal file
213
coverage/playmode/Assets/Scripts/UI/UIDialog.cs
Normal file
@@ -0,0 +1,213 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using TMPro;
|
||||
using System;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class UIDialog : MonoBehaviour
|
||||
{
|
||||
[SerializeField]
|
||||
private string dialogTitle;
|
||||
|
||||
[SerializeField]
|
||||
private TextMeshProUGUI dialogTitleHandle;
|
||||
[SerializeField]
|
||||
private RectTransform contentHandle;
|
||||
|
||||
/// TABS
|
||||
[SerializeField]
|
||||
private float tabWidth = 50f;
|
||||
[SerializeField]
|
||||
private float tabHeight = 16f;
|
||||
// List of dialog tabs
|
||||
private List<GameObject> dialogTabs;
|
||||
|
||||
/// ENTRIES
|
||||
private List<UISelectableEntry> entries;
|
||||
|
||||
private float entryHeight = 20f;
|
||||
private float entryIndentWidth= 10f;
|
||||
|
||||
private List<UISelectableEntry> cleanupPool;
|
||||
|
||||
|
||||
private bool isOpen;
|
||||
|
||||
|
||||
|
||||
|
||||
// Start is called before the first frame update
|
||||
public virtual void Start()
|
||||
{
|
||||
dialogTitleHandle.text = dialogTitle;
|
||||
dialogTitleHandle.font = UIManager.Instance.Font;
|
||||
isOpen = gameObject.activeSelf;
|
||||
dialogTabs = new List<GameObject>();
|
||||
entries = new List<UISelectableEntry>();
|
||||
cleanupPool = new List<UISelectableEntry>();
|
||||
}
|
||||
|
||||
internal RectTransform GetContentHandle()
|
||||
{
|
||||
return contentHandle;
|
||||
}
|
||||
|
||||
public bool IsOpen()
|
||||
{
|
||||
return isOpen;
|
||||
}
|
||||
|
||||
protected virtual void OnEnable() { isOpen = true; }
|
||||
protected virtual void OnDisable() { isOpen = false; }
|
||||
|
||||
public float GetTabWidth() { return tabWidth; }
|
||||
public float GetTabHeight() { return tabHeight; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the height of the dialog title bar
|
||||
/// </summary>
|
||||
public float GetTitleBarHeight() { return dialogTitleHandle.rectTransform.sizeDelta.y; }
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new tab to the dialog, when clicked it will call the given callback
|
||||
/// </summary>
|
||||
public void AddDialogTab(string tabName, Action onClick)
|
||||
{
|
||||
dialogTabs.Add(AddTabButton(tabName, onClick));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add the tab button to the right of the existing tabs
|
||||
/// </summary>
|
||||
private GameObject AddTabButton(string tabName, Action onClick)
|
||||
{
|
||||
GameObject tabButton = new GameObject("TabButton", typeof(RectTransform));
|
||||
tabButton.transform.SetParent(transform); // worldPositionStays ?
|
||||
// RectTransform anchors to the right of the content handle
|
||||
RectTransform rTransform = tabButton.GetComponent<RectTransform>();
|
||||
rTransform.anchorMin = new Vector2(0, 1);
|
||||
rTransform.anchorMax = new Vector2(0, 1);
|
||||
rTransform.pivot = new Vector2(0, 1);
|
||||
rTransform.sizeDelta = new Vector2(tabWidth, tabHeight);
|
||||
// Count tabs * tabSize to get the position from the left
|
||||
rTransform.anchoredPosition = new Vector2(tabWidth * dialogTabs.Count, -(GetTitleBarHeight()));
|
||||
|
||||
// Add the onClick callback to the button
|
||||
Button button = tabButton.AddComponent<Button>();
|
||||
button.onClick.AddListener(() => onClick());
|
||||
// Add the image to the button and link it to the tab
|
||||
button.targetGraphic = tabButton.AddComponent<Image>();
|
||||
|
||||
AddTabText(tabName, tabButton);
|
||||
return tabButton;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add text as a child of the tab's button object
|
||||
/// </summary>
|
||||
private void AddTabText(string tabName, GameObject tabButton)
|
||||
{
|
||||
GameObject tabText = new GameObject("TabText", typeof(RectTransform));
|
||||
tabText.transform.SetParent(tabButton.transform);
|
||||
// RectTransform anchors to the center of the button
|
||||
RectTransform textRectTransform = tabText.GetComponent<RectTransform>();
|
||||
textRectTransform.anchorMin = new Vector2(0.5f, 0.5f);
|
||||
textRectTransform.anchorMax = new Vector2(0.5f, 0.5f);
|
||||
textRectTransform.pivot = new Vector2(0.5f, 0.5f);
|
||||
textRectTransform.sizeDelta = new Vector2(tabWidth, tabHeight);
|
||||
// Text position
|
||||
textRectTransform.anchoredPosition = new Vector2(0, 0);
|
||||
|
||||
TextMeshProUGUI buttonText = tabText.AddComponent<TextMeshProUGUI>();
|
||||
buttonText.text = tabName;
|
||||
buttonText.font = UIManager.Instance.Font;
|
||||
buttonText.fontSize = 12;
|
||||
buttonText.color = Color.black;
|
||||
buttonText.alignment = TextAlignmentOptions.Center;
|
||||
buttonText.verticalAlignment = VerticalAlignmentOptions.Middle;
|
||||
}
|
||||
|
||||
public virtual UISelectableEntry CreateSelectableEntry()
|
||||
{
|
||||
// Create a new entry object with content handle as parent
|
||||
GameObject go = Instantiate(Resources.Load<GameObject>("Prefabs/EmptyObject"), contentHandle);
|
||||
go.name = "UISelectableEntry";
|
||||
UISelectableEntry entry = go.AddComponent<UISelectableEntry>();
|
||||
entry.SetParent(this);
|
||||
// add to cleanup pool so we can clear later in clear dialog entries
|
||||
cleanupPool.Add(entry);
|
||||
return entry;
|
||||
}
|
||||
|
||||
public void ClearDialogEntries()
|
||||
{
|
||||
if (cleanupPool == null)
|
||||
return;
|
||||
foreach (UISelectableEntry entry in cleanupPool)
|
||||
{
|
||||
GameObject.Destroy(entry.gameObject);
|
||||
}
|
||||
cleanupPool.Clear();
|
||||
if (entries != null)
|
||||
entries.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears, sets, and prints the dialog entries in the order they were added
|
||||
/// </summary>
|
||||
public virtual void SetDialogEntries(List<UISelectableEntry> entries)
|
||||
{
|
||||
this.entries = entries;
|
||||
// calculate total height of the content
|
||||
float heightHead = -1*GetTabHeight();
|
||||
int count = 0;
|
||||
foreach (UISelectableEntry entry in this.entries)
|
||||
{
|
||||
(heightHead, count) = RecursiveContentPrint(entry, 1, heightHead, count);
|
||||
}
|
||||
contentHandle.sizeDelta = new Vector2(contentHandle.sizeDelta.x, count * entryHeight + Mathf.Abs(heightHead));
|
||||
}
|
||||
|
||||
|
||||
public virtual void SetDialogEntries(UISelectableEntry entry)
|
||||
{
|
||||
SetDialogEntries(new List<UISelectableEntry>() { entry });
|
||||
}
|
||||
|
||||
|
||||
private (float, int) RecursiveContentPrint(UISelectableEntry entry, int depth, float heightHead, int count)
|
||||
{
|
||||
RectTransform rTransform = entry.GetComponent<RectTransform>();
|
||||
rTransform.anchorMin = new Vector2(0, 1);
|
||||
rTransform.anchorMax = new Vector2(1, 1);
|
||||
rTransform.pivot = new Vector2(0.5f, 1f);
|
||||
|
||||
rTransform.anchoredPosition = new Vector2(0, heightHead); // positioning from top
|
||||
rTransform.sizeDelta = new Vector2(0, entryHeight);
|
||||
float padding = 5f;
|
||||
rTransform.SetRight(padding);
|
||||
rTransform.SetLeft(padding);
|
||||
// actually indent the text
|
||||
entry.GetTextTransform().anchoredPosition = new Vector2(entryIndentWidth * depth, 0);
|
||||
heightHead -= entryHeight;
|
||||
count++;
|
||||
// Print the children
|
||||
if (entry.GetChildEntries() != null)
|
||||
{
|
||||
foreach (UISelectableEntry child in entry.GetChildEntries())
|
||||
{
|
||||
(heightHead, count) = RecursiveContentPrint(child, depth + 1, heightHead, count);
|
||||
}
|
||||
}
|
||||
return (heightHead, count);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
11
coverage/playmode/Assets/Scripts/UI/UIDialog.cs.meta
Normal file
11
coverage/playmode/Assets/Scripts/UI/UIDialog.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 28b8de234a5dab849bce341b844ea8e1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
15
coverage/playmode/Assets/Scripts/UI/UIElementDragger.cs
Normal file
15
coverage/playmode/Assets/Scripts/UI/UIElementDragger.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
[RequireComponent(typeof(UIElementMouseCapturer))]
|
||||
public class UIElementDragger : EventTrigger
|
||||
{
|
||||
public override void OnDrag(PointerEventData eventData)
|
||||
{
|
||||
transform.position = new Vector2(Input.mousePosition.x, Input.mousePosition.y);
|
||||
}
|
||||
|
||||
}
|
||||
11
coverage/playmode/Assets/Scripts/UI/UIElementDragger.cs.meta
Normal file
11
coverage/playmode/Assets/Scripts/UI/UIElementDragger.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 08d6199088fb8554fa7335c18db3ace4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,25 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
public class UIElementMouseCapturer : EventTrigger
|
||||
{
|
||||
public override void OnPointerEnter(PointerEventData eventData)
|
||||
{
|
||||
InputManager.Instance.mouseActive = false;
|
||||
base.OnPointerEnter(eventData);
|
||||
}
|
||||
|
||||
public override void OnPointerExit(PointerEventData eventData)
|
||||
{
|
||||
InputManager.Instance.mouseActive = true;
|
||||
base.OnPointerExit(eventData);
|
||||
}
|
||||
|
||||
public void OnDisable()
|
||||
{
|
||||
OnPointerExit(null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8b4602e26cd6ff342a532880fc8ac6b8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
127
coverage/playmode/Assets/Scripts/UI/UIManager.cs
Normal file
127
coverage/playmode/Assets/Scripts/UI/UIManager.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using TMPro;
|
||||
|
||||
public class UIManager : MonoBehaviour
|
||||
{
|
||||
|
||||
public static UIManager Instance { get; private set; }
|
||||
|
||||
|
||||
[SerializeField]
|
||||
private GameObject _agentStatusPanel;
|
||||
[SerializeField]
|
||||
private GameObject _configSelectorPanel;
|
||||
private TMP_Dropdown _configDropdown;
|
||||
public TextMeshProUGUI agentPanelText;
|
||||
public TextMeshProUGUI simTimeText;
|
||||
|
||||
public TMP_FontAsset Font;
|
||||
|
||||
|
||||
private UIMode curMode = UIMode.NONE;
|
||||
|
||||
|
||||
// Start is called before the first frame update
|
||||
void Awake()
|
||||
{
|
||||
// singleton
|
||||
if (Instance == null)
|
||||
Instance = this;
|
||||
else
|
||||
Destroy(gameObject);
|
||||
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
_configSelectorPanel.SetActive(false);
|
||||
SetupConfigSelectorPanel();
|
||||
//inputManager = InputManager.Instance;
|
||||
//worldManager = WorldManager.Instance;
|
||||
}
|
||||
|
||||
public void ToggleConfigSelectorPanel(){
|
||||
_configSelectorPanel.SetActive(!_configSelectorPanel.activeSelf);
|
||||
}
|
||||
|
||||
private void SetupConfigSelectorPanel(){
|
||||
_configSelectorPanel.GetComponentInChildren<Button>().onClick.AddListener(delegate {
|
||||
LoadSelectedConfig();
|
||||
});
|
||||
_configDropdown = _configSelectorPanel.GetComponentInChildren<TMP_Dropdown>();
|
||||
PopulateConfigDropdown();
|
||||
}
|
||||
|
||||
private void PopulateConfigDropdown(){
|
||||
_configDropdown.ClearOptions();
|
||||
string configPath = Path.Combine(Application.streamingAssetsPath, "Configs");
|
||||
string[] configFiles = Directory.GetFiles(configPath, "*.json");
|
||||
|
||||
List<string> configFileNames = new List<string>();
|
||||
foreach (string configFile in configFiles)
|
||||
{
|
||||
configFileNames.Add(Path.GetFileName(configFile));
|
||||
}
|
||||
_configDropdown.AddOptions(configFileNames);
|
||||
}
|
||||
private void LoadSelectedConfig(){
|
||||
string selectedConfig = _configDropdown.options[_configDropdown.value].text;
|
||||
SimManager.Instance.LoadNewConfig(selectedConfig);
|
||||
_configSelectorPanel.SetActive(false);
|
||||
//if(!InputManager.Instance.mouseActive){
|
||||
// InputManager.Instance.mouseActive = true;
|
||||
//}
|
||||
}
|
||||
|
||||
public void SetUIMode(UIMode mode){
|
||||
curMode = mode;
|
||||
}
|
||||
|
||||
public UIMode GetUIMode(){
|
||||
return curMode;
|
||||
}
|
||||
|
||||
public void SetAgentPanelText(string text)
|
||||
{
|
||||
agentPanelText.text = text;
|
||||
}
|
||||
|
||||
public string GetAgentPanelText()
|
||||
{
|
||||
return agentPanelText.text;
|
||||
}
|
||||
|
||||
|
||||
private void UpdateAgentPanel()
|
||||
{
|
||||
string agentPanelText = "";
|
||||
foreach(Agent agent in SimManager.Instance.GetActiveAgents())
|
||||
{
|
||||
string jobText = agent.name + "| Phase: " + agent.GetFlightPhase().ToString();
|
||||
agentPanelText += jobText + "\n";
|
||||
}
|
||||
SetAgentPanelText(agentPanelText);
|
||||
}
|
||||
|
||||
private void UpdateSimTimeText()
|
||||
{
|
||||
simTimeText.text = "Elapsed Sim Time: " + SimManager.Instance.GetElapsedSimulationTime().ToString("F2");
|
||||
}
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
//UpdateAgentPanel();
|
||||
UpdateSimTimeText();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public enum UIMode {
|
||||
NONE,
|
||||
BUILD,
|
||||
MINE
|
||||
}
|
||||
11
coverage/playmode/Assets/Scripts/UI/UIManager.cs.meta
Normal file
11
coverage/playmode/Assets/Scripts/UI/UIManager.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b078e3e02b9c70645a97175561678909
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
119
coverage/playmode/Assets/Scripts/UI/UISelectableEntry.cs
Normal file
119
coverage/playmode/Assets/Scripts/UI/UISelectableEntry.cs
Normal file
@@ -0,0 +1,119 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using TMPro;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
public class UISelectableEntry : EventTrigger {
|
||||
private List<UISelectableEntry> children;
|
||||
private List<string> textContent;
|
||||
|
||||
private UIDialog parentDialog;
|
||||
|
||||
private RectTransform rectTransform;
|
||||
|
||||
private CanvasRenderer canvasRenderer;
|
||||
|
||||
private Image image;
|
||||
|
||||
private TextMeshProUGUI textHandle;
|
||||
|
||||
private bool isSelectable = true;
|
||||
|
||||
private static Color baseColor = new Color32(31, 31, 45, 140);
|
||||
|
||||
private Action<object> OnClickCallback;
|
||||
private object callbackArgument;
|
||||
|
||||
|
||||
public void Awake() {
|
||||
rectTransform = gameObject.AddComponent<RectTransform>();
|
||||
textHandle = Instantiate(Resources.Load<GameObject>("Prefabs/EmptyObject"), rectTransform).AddComponent<TextMeshProUGUI>();
|
||||
textHandle.gameObject.name = "UISelectableEntry::Text";
|
||||
textHandle.fontSize = 12;
|
||||
textHandle.font = UIManager.Instance.Font;
|
||||
textHandle.alignment = TextAlignmentOptions.MidlineLeft;
|
||||
textHandle.GetComponent<RectTransform>().anchorMin = new Vector2(0, 0.5f);
|
||||
textHandle.GetComponent<RectTransform>().anchorMax = new Vector2(1, 0.5f);
|
||||
textHandle.GetComponent<RectTransform>().pivot = new Vector2(0.5f, 0.5f);
|
||||
textHandle.GetComponent<RectTransform>().anchoredPosition = new Vector2(0, 0);
|
||||
textHandle.GetComponent<RectTransform>().sizeDelta = new Vector2(0, 20);
|
||||
|
||||
image = gameObject.AddComponent<Image>();
|
||||
image.type = Image.Type.Sliced;
|
||||
image.color = baseColor;
|
||||
}
|
||||
|
||||
public void SetClickCallback(Action<object> callback, object argument)
|
||||
{
|
||||
OnClickCallback = callback;
|
||||
callbackArgument = argument;
|
||||
}
|
||||
|
||||
public override void OnPointerEnter(PointerEventData eventData)
|
||||
{
|
||||
if(isSelectable)
|
||||
image.color = baseColor + new Color32(20, 20, 20, 40);
|
||||
base.OnPointerEnter(eventData);
|
||||
}
|
||||
|
||||
public override void OnPointerDown(PointerEventData eventData)
|
||||
{
|
||||
if(isSelectable && OnClickCallback != null)
|
||||
{
|
||||
OnClickCallback(callbackArgument);
|
||||
image.color = baseColor + new Color32(40, 40, 40, 40);
|
||||
|
||||
}
|
||||
base.OnPointerClick(eventData);
|
||||
}
|
||||
|
||||
public override void OnPointerExit(PointerEventData eventData)
|
||||
{
|
||||
if(isSelectable)
|
||||
image.color = baseColor;
|
||||
base.OnPointerExit(eventData);
|
||||
}
|
||||
|
||||
public void SetIsSelectable(bool isSelectable) {
|
||||
if(isSelectable)
|
||||
image.enabled = true;
|
||||
else
|
||||
image.enabled = false;
|
||||
this.isSelectable = isSelectable;
|
||||
}
|
||||
|
||||
public bool GetIsSelectable() {
|
||||
return isSelectable;
|
||||
}
|
||||
|
||||
public void AddChildEntry(UISelectableEntry child) {
|
||||
if (children == null) {
|
||||
children = new List<UISelectableEntry>();
|
||||
}
|
||||
children.Add(child);
|
||||
}
|
||||
|
||||
public void SetParent(UIDialog parentDialog) {
|
||||
this.parentDialog = parentDialog;
|
||||
}
|
||||
|
||||
public void SetChildEntries(List<UISelectableEntry> children) {
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
// Get the children of this entry
|
||||
public List<UISelectableEntry> GetChildEntries() {
|
||||
return children;
|
||||
}
|
||||
|
||||
public void SetTextContent(List<string> textContent) {
|
||||
this.textContent = textContent;
|
||||
textHandle.text = string.Join("\t", textContent);
|
||||
}
|
||||
|
||||
public RectTransform GetTextTransform() {
|
||||
return textHandle.GetComponent<RectTransform>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b1f58db020114a64fb94cf47d775a20f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
coverage/playmode/Assets/Scripts/UI/Windows.meta
Normal file
8
coverage/playmode/Assets/Scripts/UI/Windows.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5dfa88d362db21f42acf701d3d3e326a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
108
coverage/playmode/Assets/Scripts/UI/Windows/UIWindow.cs
Normal file
108
coverage/playmode/Assets/Scripts/UI/Windows/UIWindow.cs
Normal file
@@ -0,0 +1,108 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using TMPro;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
[RequireComponent(typeof(UIElementMouseCapturer))]
|
||||
[RequireComponent(typeof(UIElementDragger))]
|
||||
[RequireComponent(typeof(Image))]
|
||||
public class UIWindow : MonoBehaviour
|
||||
{
|
||||
// Window title
|
||||
[SerializeField]
|
||||
private string windowTitle = "Window";
|
||||
|
||||
// Close button
|
||||
private GameObject closeButton;
|
||||
[SerializeField]
|
||||
private CloseButtonCallback closeButtonCallback;
|
||||
[Serializable]
|
||||
private enum CloseButtonCallback
|
||||
{
|
||||
CLOSE_WINDOW,
|
||||
TOGGLE_WINDOW
|
||||
}
|
||||
|
||||
// IsOpen property
|
||||
private bool isOpen;
|
||||
private void OnEnable() { isOpen = true; }
|
||||
private void OnDisable() { isOpen = false; }
|
||||
|
||||
public void ToggleWindow()
|
||||
{
|
||||
gameObject.SetActive(!gameObject.activeSelf);
|
||||
}
|
||||
|
||||
public void CloseWindow()
|
||||
{
|
||||
Destroy(gameObject);
|
||||
isOpen = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the UIWindow component is created in the editor
|
||||
/// We will use it to configure the image component
|
||||
/// </summary>
|
||||
private void Reset()
|
||||
{
|
||||
// 18 16 28 125
|
||||
GetComponent<Image>().color = new Color32(18, 16, 28, 125);
|
||||
}
|
||||
|
||||
public virtual void Start()
|
||||
{
|
||||
isOpen = gameObject.activeSelf;
|
||||
CreateCloseButton();
|
||||
CreateWindowTitle();
|
||||
if (closeButtonCallback == CloseButtonCallback.CLOSE_WINDOW)
|
||||
closeButton.AddComponent<Button>().onClick.AddListener(CloseWindow);
|
||||
else if (closeButtonCallback == CloseButtonCallback.TOGGLE_WINDOW)
|
||||
closeButton.AddComponent<Button>().onClick.AddListener(ToggleWindow);
|
||||
}
|
||||
|
||||
private void CreateWindowTitle()
|
||||
{
|
||||
GameObject windowTitleObject = new GameObject("WindowTitle", typeof(RectTransform));
|
||||
windowTitleObject.transform.SetParent(transform);
|
||||
TextMeshProUGUI windowTitleHandle = windowTitleObject.AddComponent<TextMeshProUGUI>();
|
||||
windowTitleHandle.text = windowTitle;
|
||||
windowTitleHandle.font = UIManager.Instance.Font;
|
||||
windowTitleHandle.fontSize = 14;
|
||||
windowTitleHandle.color = Color.white;
|
||||
windowTitleHandle.alignment = TextAlignmentOptions.Left;
|
||||
windowTitleHandle.rectTransform.anchorMin = new Vector2(0, 1);
|
||||
windowTitleHandle.rectTransform.anchorMax = new Vector2(1, 1);
|
||||
windowTitleHandle.rectTransform.pivot = new Vector2(0, 1);
|
||||
windowTitleHandle.rectTransform.sizeDelta = new Vector2(0, 30);
|
||||
windowTitleHandle.rectTransform.anchoredPosition = new Vector2(5, 0);
|
||||
windowTitleHandle.rectTransform.SetRight(30); // Give spacing to the close button
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create the close [x] button in the top right corner of the window
|
||||
/// </summary>
|
||||
private void CreateCloseButton()
|
||||
{
|
||||
closeButton = new GameObject("CloseButton", typeof(RectTransform));
|
||||
RectTransform buttonTransform = closeButton.GetComponent<RectTransform>();
|
||||
buttonTransform.SetParent(transform);
|
||||
// anchor top right
|
||||
buttonTransform.anchorMin = new Vector2(1, 1);
|
||||
buttonTransform.anchorMax = new Vector2(1, 1);
|
||||
buttonTransform.pivot = new Vector2(1, 1);
|
||||
// position top right
|
||||
buttonTransform.anchoredPosition = new Vector2(0, 0);
|
||||
// size
|
||||
buttonTransform.sizeDelta = new Vector2(30, 30);
|
||||
// add button component
|
||||
TextMeshProUGUI textbox = closeButton.AddComponent<TextMeshProUGUI>();
|
||||
textbox.text = "X";
|
||||
textbox.font = UIManager.Instance.Font;
|
||||
textbox.fontSize = 12;
|
||||
textbox.alignment = TextAlignmentOptions.Center;
|
||||
textbox.verticalAlignment = VerticalAlignmentOptions.Middle;
|
||||
}
|
||||
}
|
||||
11
coverage/playmode/Assets/Scripts/UI/Windows/UIWindow.cs.meta
Normal file
11
coverage/playmode/Assets/Scripts/UI/Windows/UIWindow.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0399a7d56c753d241812497084561817
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
9
coverage/playmode/Assets/Scripts/Utilities.cs
Normal file
9
coverage/playmode/Assets/Scripts/Utilities.cs
Normal file
@@ -0,0 +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));
|
||||
}
|
||||
}
|
||||
11
coverage/playmode/Assets/Scripts/Utilities.cs.meta
Normal file
11
coverage/playmode/Assets/Scripts/Utilities.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: caa881af3be57584a91f17ed3683fed7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
17
coverage/playmode/Assets/Scripts/bamlab.micromissiles.asmdef
Normal file
17
coverage/playmode/Assets/Scripts/bamlab.micromissiles.asmdef
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "bamlab.micromissiles",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"GUID:6055be8ebefd69e48b49212b09b47b2f",
|
||||
"GUID:c668b7a00ffe56a498ddb1fc9f96f4e6"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: af8bba08a36038347823e2f46bdc9857
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user