Compare commits

..

6 Commits

74 changed files with 1773 additions and 4361 deletions

View File

@ -9,8 +9,6 @@ on:
pull_request:
branches:
- release
schedule:
- cron: '0 2 * * *' # Run at 2 AM UTC every day
jobs:
buildForAllSupportedPlatforms:
@ -43,9 +41,6 @@ jobs:
buildName: micromissiles-${{ github.ref_name }}-${{ matrix.targetPlatform }}
versioning: Semantic
targetPlatform: ${{ matrix.targetPlatform }}
- name: Copy Tools Directory
run: |
sudo cp -r Tools/ build/${{ matrix.targetPlatform }}/
- if: matrix.targetPlatform == 'StandaloneWindows64'
run: cd build/${{ matrix.targetPlatform }} && sudo zip -r ../build-${{ matrix.targetPlatform }}.zip * && cd -
- if: matrix.targetPlatform == 'StandaloneWindows64'

View File

@ -1,47 +0,0 @@
# .github/workflows/docs.yaml
name: Deploy Documentation
on:
push:
branches:
- master
pull_request:
branches:
- master
workflow_dispatch: # Allows manual triggering
permissions:
contents: write
pages: write
id-token: write
jobs:
build-deploy:
permissions:
contents: write
pages: write
id-token: write
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18' # Use Node.js 18
- name: Install dependencies
run: npm ci
- name: Build documentation
run: npm run docs:build
- name: Deploy Documentation
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./docs/.vitepress/dist
allow_empty_commit: true
keep_files: true
force_orphan: false

27
.github/workflows/release.md vendored Normal file
View File

@ -0,0 +1,27 @@
# micromissiles-unity
## Build ${{ github.ref }}
## Instructions
Download and extract the archive corresponding to your platform (Windows or Mac) from the binaries listed below.
## Windows
1. Download the zip file for Windows: `micromissiles-${{ github.ref }}-windows_x86_64.zip`.
2. Unzip the zip file. The zip file should contain a single directory called `micromissiles-${{ github.ref }}-windows_x86_64`.
3. Run `micromissiles-<version>-StandaloneWindows64.exe`.
## Mac
1. Download the zip file for Darwin: `micromissiles-${{ github.ref }}-darwin.zip`.
2. Unzip the zip file. The zip file should contain a single app file.
3. Change the permission of the app file recursively by running:
```bash
chmod -R +x micromissiles-<version>-StandaloneOSX.app
```
4. Open the app file.
5. If you get a warning that Apple cannot check the application for malicious software:
* Open `System Preferences`.
* Navigate to `Privacy & Security`.
* Click on `Open Anyway` to bypass Apple's developer check.

View File

@ -18,19 +18,14 @@ jobs:
ref: ${{ github.event.workflow_run.head_branch }}
fetch-depth: 0
lfs: false
- name: Get latest tag
id: get_latest_tag
run: |
latest_tag=$(git describe --tags --abbrev=0)
echo "LATEST_TAG=${latest_tag}" >> $GITHUB_OUTPUT
- name: Create release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ steps.get_latest_tag.outputs.LATEST_TAG }}
release_name: ${{ steps.get_latest_tag.outputs.LATEST_TAG }}
tag_name: ${{ github.event.workflow_run.head_branch }}
release_name: ${{ github.event.workflow_run.head_branch }}
body_path: RELEASE.md
draft: false
prerelease: false

View File

@ -1,69 +0,0 @@
# .github/workflows/test.yaml
name: Test
on:
schedule:
- cron: '0 2 * * *' # Runs at 2 AM UTC every day
pull_request:
branches:
- master
workflow_dispatch:
jobs:
testAllModes:
name: Test in ${{ matrix.testMode }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
testMode:
- playmode
- editmode
steps:
- uses: actions/checkout@v4
with:
lfs: true
- uses: actions/cache@v4
with:
path: ${{ matrix.projectPath }}/Library
key: Library-${{ matrix.projectPath }}
restore-keys: |
Library-
- uses: game-ci/unity-test-runner@v4
id: tests
env:
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
with:
projectPath: ${{ matrix.projectPath }}
testMode: ${{ matrix.testMode }}
artifactsPath: ${{ matrix.testMode }}-artifacts
githubToken: ${{ secrets.GITHUB_TOKEN }}
checkName: ${{ matrix.testMode }} Test Results
coverageOptions: 'generateAdditionalMetrics;generateHtmlReport;generateBadgeReport;assemblyFilters:+bamlab.*'
- uses: actions/upload-artifact@v4
if: always()
with:
name: Test results for ${{ matrix.testMode }}
path: ${{ steps.tests.outputs.artifactsPath }}
- uses: actions/upload-artifact@v4
if: always()
with:
name: Coverage results for ${{ matrix.testMode }}
path: ${{ steps.tests.outputs.coveragePath }}
- name: Deploy Coverage Report
if: always()
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ${{ steps.tests.outputs.coveragePath }}
destination_dir: coverage/${{ matrix.testMode }}
allow_empty_commit: true
keep_files: true
force_orphan: false

2
.gitignore vendored
View File

@ -81,5 +81,3 @@ crashlytics-build.properties
# Telemetry Logs
Telemetry/Logs/
node_modules/

View File

@ -98,7 +98,6 @@ Material:
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _AddPrecomputedVelocity: 0
- _AlphaClip: 0
- _AlphaToMask: 0
- _Blend: 0
@ -136,8 +135,8 @@ Material:
- _WorkflowMode: 1
- _ZWrite: 1
m_Colors:
- _BaseColor: {r: 0.6679245, g: 0, b: 0, a: 1}
- _Color: {r: 0.66792446, g: 0, b: 0, a: 1}
- _BaseColor: {r: 1, g: 0, b: 0, a: 1}
- _Color: {r: 1, g: 0, b: 0, a: 1}
- _EmissionColor: {r: 0.9622642, g: 0, b: 0, a: 1}
- _SpecColor: {r: 0.5, g: 0.5, b: 0.5, a: 0.5}
m_BuildTextureStacks: []

View File

@ -109,7 +109,7 @@ MonoBehaviour:
m_PrefilterDebugKeywords: 1
m_PrefilterWriteRenderingLayers: 1
m_PrefilterHDROutput: 1
m_PrefilterAlphaOutput: 1
m_PrefilterAlphaOutput: 0
m_PrefilterSSAODepthNormals: 1
m_PrefilterSSAOSourceDepthLow: 1
m_PrefilterSSAOSourceDepthMedium: 1

View File

@ -0,0 +1,361 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &295999596028346972
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 9192475791974545516}
- component: {fileID: 3681380462727606042}
- component: {fileID: 5038767565634652910}
m_Layer: 0
m_Name: Cone
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &9192475791974545516
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 295999596028346972}
serializedVersion: 2
m_LocalRotation: {x: 0.7071068, y: 0, z: 0, w: 0.7071068}
m_LocalPosition: {x: 0, y: 0, z: -3.89}
m_LocalScale: {x: 20, y: 20, z: 20}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 8462434346230391091}
m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0}
--- !u!33 &3681380462727606042
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 295999596028346972}
m_Mesh: {fileID: 4300000, guid: 36b72d5a34c22d94c88a068dfcbdf27a, type: 2}
--- !u!23 &5038767565634652910
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 295999596028346972}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: 061083197c659dc44983633a84d39cea, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!1 &4390675743535140416
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 585396986125101381}
- component: {fileID: 4620404264009677289}
m_Layer: 0
m_Name: TrailRenderer
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &585396986125101381
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4390675743535140416}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 10, y: 10, z: 10}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 8462434346230391091}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!96 &4620404264009677289
TrailRenderer:
serializedVersion: 3
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4390675743535140416}
m_Enabled: 1
m_CastShadows: 0
m_ReceiveShadows: 0
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 0
m_LightProbeUsage: 0
m_ReflectionProbeUsage: 0
m_RayTracingMode: 0
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: 0e42500808910f24cb6626a81760c995, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_Time: 5
m_PreviewTimeScale: 1
m_Parameters:
serializedVersion: 3
widthMultiplier: 5
widthCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 1
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
colorGradient:
serializedVersion: 2
key0: {r: 1, g: 0, b: 0, a: 1}
key1: {r: 1, g: 1, b: 1, a: 1}
key2: {r: 0, g: 0, b: 0, a: 0}
key3: {r: 0, g: 0, b: 0, a: 0}
key4: {r: 0, g: 0, b: 0, a: 0}
key5: {r: 0, g: 0, b: 0, a: 0}
key6: {r: 0, g: 0, b: 0, a: 0}
key7: {r: 0, g: 0, b: 0, a: 0}
ctime0: 0
ctime1: 65535
ctime2: 0
ctime3: 0
ctime4: 0
ctime5: 0
ctime6: 0
ctime7: 0
atime0: 0
atime1: 65535
atime2: 0
atime3: 0
atime4: 0
atime5: 0
atime6: 0
atime7: 0
m_Mode: 0
m_ColorSpace: 0
m_NumColorKeys: 2
m_NumAlphaKeys: 2
numCornerVertices: 0
numCapVertices: 0
alignment: 0
textureMode: 0
textureScale: {x: 1, y: 1}
shadowBias: 0.5
generateLightingData: 0
m_MinVertexDistance: 0.1
m_MaskInteraction: 0
m_Autodestruct: 0
m_Emitting: 1
m_ApplyActiveColorSpace: 0
--- !u!1 &4590233640347492898
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 8315564871113561163}
- component: {fileID: 6203868598056538240}
m_Layer: 0
m_Name: Collider
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &8315564871113561163
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4590233640347492898}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 8462434346230391091}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!135 &6203868598056538240
SphereCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4590233640347492898}
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_IsTrigger: 1
m_ProvidesContacts: 0
m_Enabled: 1
serializedVersion: 3
m_Radius: 10
m_Center: {x: 0, y: 0, z: 0}
--- !u!1 &6438458936967544359
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 8462434346230391091}
- component: {fileID: 4451965530778273955}
- component: {fileID: 4194966225731047199}
m_Layer: 0
m_Name: FixedWingMissileThreat
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &8462434346230391091
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6438458936967544359}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 585396986125101381}
- {fileID: 8315564871113561163}
- {fileID: 9192475791974545516}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!54 &4451965530778273955
Rigidbody:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6438458936967544359}
serializedVersion: 4
m_Mass: 1
m_Drag: 0
m_AngularDrag: 0
m_CenterOfMass: {x: 0, y: 0, z: 0}
m_InertiaTensor: {x: 1, y: 1, z: 1}
m_InertiaRotation: {x: 0, y: 0, z: 0, w: 1}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_ImplicitCom: 1
m_ImplicitTensor: 1
m_UseGravity: 0
m_IsKinematic: 0
m_Interpolate: 0
m_Constraints: 0
m_CollisionDetection: 0
--- !u!114 &4194966225731047199
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6438458936967544359}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 4e6c2fbd1e492be448760f5045b13b2e, type: 3}
m_Name:
m_EditorClassIdentifier:
_flightPhase: 3
_velocity: {x: 0, y: 0, z: 0}
_acceleration: {x: 0, y: 0, z: 0}
_dragAcceleration: {x: 0, y: 0, z: 0}
_target: {fileID: 0}
staticConfigFile: generic_static_config.json

View File

@ -1,6 +1,6 @@
fileFormatVersion: 2
guid: af8bba08a36038347823e2f46bdc9857
AssemblyDefinitionImporter:
guid: 9c1ccc1cc7ad8cb4aa0b59a0afd50568
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:

View File

@ -0,0 +1,361 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &295999596028346972
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 9192475791974545516}
- component: {fileID: 3681380462727606042}
- component: {fileID: 5038767565634652910}
m_Layer: 0
m_Name: Cone
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &9192475791974545516
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 295999596028346972}
serializedVersion: 2
m_LocalRotation: {x: 0.7071068, y: 0, z: 0, w: 0.7071068}
m_LocalPosition: {x: 0, y: 0, z: -3.89}
m_LocalScale: {x: 20, y: 20, z: 20}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 8462434346230391091}
m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0}
--- !u!33 &3681380462727606042
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 295999596028346972}
m_Mesh: {fileID: 4300000, guid: 36b72d5a34c22d94c88a068dfcbdf27a, type: 2}
--- !u!23 &5038767565634652910
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 295999596028346972}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: 061083197c659dc44983633a84d39cea, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!1 &4390675743535140416
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 585396986125101381}
- component: {fileID: 4620404264009677289}
m_Layer: 0
m_Name: TrailRenderer
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &585396986125101381
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4390675743535140416}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 10, y: 10, z: 10}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 8462434346230391091}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!96 &4620404264009677289
TrailRenderer:
serializedVersion: 3
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4390675743535140416}
m_Enabled: 1
m_CastShadows: 0
m_ReceiveShadows: 0
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 0
m_LightProbeUsage: 0
m_ReflectionProbeUsage: 0
m_RayTracingMode: 0
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: 0e42500808910f24cb6626a81760c995, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_Time: 5
m_PreviewTimeScale: 1
m_Parameters:
serializedVersion: 3
widthMultiplier: 5
widthCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 1
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
colorGradient:
serializedVersion: 2
key0: {r: 1, g: 0, b: 0, a: 1}
key1: {r: 1, g: 1, b: 1, a: 1}
key2: {r: 0, g: 0, b: 0, a: 0}
key3: {r: 0, g: 0, b: 0, a: 0}
key4: {r: 0, g: 0, b: 0, a: 0}
key5: {r: 0, g: 0, b: 0, a: 0}
key6: {r: 0, g: 0, b: 0, a: 0}
key7: {r: 0, g: 0, b: 0, a: 0}
ctime0: 0
ctime1: 65535
ctime2: 0
ctime3: 0
ctime4: 0
ctime5: 0
ctime6: 0
ctime7: 0
atime0: 0
atime1: 65535
atime2: 0
atime3: 0
atime4: 0
atime5: 0
atime6: 0
atime7: 0
m_Mode: 0
m_ColorSpace: 0
m_NumColorKeys: 2
m_NumAlphaKeys: 2
numCornerVertices: 0
numCapVertices: 0
alignment: 0
textureMode: 0
textureScale: {x: 1, y: 1}
shadowBias: 0.5
generateLightingData: 0
m_MinVertexDistance: 0.1
m_MaskInteraction: 0
m_Autodestruct: 0
m_Emitting: 1
m_ApplyActiveColorSpace: 0
--- !u!1 &4590233640347492898
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 8315564871113561163}
- component: {fileID: 6203868598056538240}
m_Layer: 0
m_Name: Collider
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &8315564871113561163
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4590233640347492898}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 8462434346230391091}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!135 &6203868598056538240
SphereCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4590233640347492898}
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_IsTrigger: 1
m_ProvidesContacts: 0
m_Enabled: 1
serializedVersion: 3
m_Radius: 10
m_Center: {x: 0, y: 0, z: 0}
--- !u!1 &6438458936967544359
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 8462434346230391091}
- component: {fileID: -2759790757235477429}
- component: {fileID: 4451965530778273955}
m_Layer: 0
m_Name: RollStabilizedMissileThreat
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &8462434346230391091
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6438458936967544359}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 585396986125101381}
- {fileID: 8315564871113561163}
- {fileID: 9192475791974545516}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &-2759790757235477429
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6438458936967544359}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 16e40584b2154ef4a95c84e85a321999, type: 3}
m_Name:
m_EditorClassIdentifier:
_flightPhase: 3
_velocity: {x: 0, y: 0, z: 0}
_acceleration: {x: 0, y: 0, z: 0}
_dragAcceleration: {x: 0, y: 0, z: 0}
_target: {fileID: 0}
staticConfigFile: base_roll_stabilized_missile.json
--- !u!54 &4451965530778273955
Rigidbody:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6438458936967544359}
serializedVersion: 4
m_Mass: 1
m_Drag: 0
m_AngularDrag: 0
m_CenterOfMass: {x: 0, y: 0, z: 0}
m_InertiaTensor: {x: 1, y: 1, z: 1}
m_InertiaRotation: {x: 0, y: 0, z: 0, w: 1}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_ImplicitCom: 1
m_ImplicitTensor: 1
m_UseGravity: 0
m_IsKinematic: 0
m_Interpolate: 0
m_Constraints: 0
m_CollisionDetection: 0

View File

@ -1,6 +1,6 @@
fileFormatVersion: 2
guid: d15c92e585e721749b63d85007276dbe
AssemblyDefinitionImporter:
guid: a4172237088e66b4190f0126eef67443
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:

View File

@ -13,7 +13,7 @@ OcclusionCullingSettings:
--- !u!104 &2
RenderSettings:
m_ObjectHideFlags: 0
serializedVersion: 10
serializedVersion: 9
m_Fog: 0
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
m_FogMode: 3
@ -43,6 +43,7 @@ RenderSettings:
LightmapSettings:
m_ObjectHideFlags: 0
serializedVersion: 12
m_GIWorkflowMode: 1
m_GISettings:
serializedVersion: 2
m_BounceScale: 1
@ -65,6 +66,9 @@ LightmapSettings:
m_LightmapParameters: {fileID: 0}
m_LightmapsBakeMode: 1
m_TextureCompression: 1
m_FinalGather: 0
m_FinalGatherFiltering: 1
m_FinalGatherRayCount: 256
m_ReflectionCompression: 2
m_MixedBakeMode: 2
m_BakeBackend: 1
@ -203,8 +207,9 @@ Light:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 50643631}
m_Enabled: 1
serializedVersion: 11
serializedVersion: 10
m_Type: 1
m_Shape: 0
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_Intensity: 0.75
m_Range: 10
@ -254,12 +259,8 @@ Light:
m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
m_UseBoundingSphereOverride: 0
m_UseViewFrustumForShadowCasterCull: 1
m_ForceVisible: 0
m_ShadowRadius: 0
m_ShadowAngle: 0
m_LightUnit: 1
m_LuxAtDistance: 1
m_EnableSpotReflector: 1
--- !u!4 &50643634
Transform:
m_ObjectHideFlags: 0
@ -348,9 +349,6 @@ MeshRenderer:
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
@ -457,9 +455,6 @@ MeshRenderer:
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
@ -566,9 +561,6 @@ MeshRenderer:
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
@ -675,9 +667,6 @@ MeshRenderer:
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
@ -874,9 +863,6 @@ MeshRenderer:
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
@ -1010,17 +996,15 @@ MonoBehaviour:
m_lineSpacingMax: 0
m_paragraphSpacing: 0
m_charWidthMaxAdj: 0
m_TextWrappingMode: 1
m_enableWordWrapping: 1
m_wordWrappingRatios: 0.4
m_overflowMode: 0
m_linkedTextComponent: {fileID: 0}
parentLinkedComponent: {fileID: 0}
m_enableKerning: 1
m_ActiveFontFeatures: 6e72656b
m_enableExtraPadding: 0
checkPaddingRequired: 0
m_isRichText: 1
m_EmojiFallbackSupport: 1
m_parseCtrlCharacters: 1
m_isOrthographic: 1
m_isCullingEnabled: 0
@ -1119,9 +1103,6 @@ MeshRenderer:
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
@ -1438,18 +1419,14 @@ MonoBehaviour:
m_ItemText: {fileID: 1985109736}
m_ItemImage: {fileID: 0}
m_Value: 0
m_MultiSelect: 0
m_Options:
m_Options:
- m_Text: Option A
m_Image: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
- m_Text: Option B
m_Image: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
- m_Text: Option C
m_Image: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_OnValueChanged:
m_PersistentCalls:
m_Calls: []
@ -1592,17 +1569,15 @@ MonoBehaviour:
m_lineSpacingMax: 0
m_paragraphSpacing: 0
m_charWidthMaxAdj: 0
m_TextWrappingMode: 1
m_enableWordWrapping: 1
m_wordWrappingRatios: 0.4
m_overflowMode: 0
m_linkedTextComponent: {fileID: 0}
parentLinkedComponent: {fileID: 0}
m_enableKerning: 1
m_ActiveFontFeatures: 6e72656b
m_enableExtraPadding: 0
checkPaddingRequired: 0
m_isRichText: 1
m_EmojiFallbackSupport: 1
m_parseCtrlCharacters: 1
m_isOrthographic: 1
m_isCullingEnabled: 0
@ -1728,17 +1703,15 @@ MonoBehaviour:
m_lineSpacingMax: 0
m_paragraphSpacing: 0
m_charWidthMaxAdj: 0
m_TextWrappingMode: 1
m_enableWordWrapping: 1
m_wordWrappingRatios: 0.4
m_overflowMode: 0
m_linkedTextComponent: {fileID: 0}
parentLinkedComponent: {fileID: 0}
m_enableKerning: 1
m_ActiveFontFeatures: 6e72656b
m_enableExtraPadding: 0
checkPaddingRequired: 0
m_isRichText: 1
m_EmojiFallbackSupport: 1
m_parseCtrlCharacters: 1
m_isOrthographic: 0
m_isCullingEnabled: 0
@ -1776,9 +1749,6 @@ MeshRenderer:
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
@ -1881,9 +1851,6 @@ MeshRenderer:
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
@ -1966,8 +1933,9 @@ Light:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 396716023}
m_Enabled: 1
serializedVersion: 11
serializedVersion: 10
m_Type: 1
m_Shape: 0
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_Intensity: 0.75
m_Range: 10
@ -2017,12 +1985,8 @@ Light:
m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
m_UseBoundingSphereOverride: 0
m_UseViewFrustumForShadowCasterCull: 1
m_ForceVisible: 0
m_ShadowRadius: 0
m_ShadowAngle: 0
m_LightUnit: 1
m_LuxAtDistance: 1
m_EnableSpotReflector: 1
--- !u!4 &396716026
Transform:
m_ObjectHideFlags: 0
@ -2111,9 +2075,6 @@ MeshRenderer:
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
@ -2224,9 +2185,6 @@ MeshRenderer:
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
@ -2340,8 +2298,9 @@ Light:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 566761696}
m_Enabled: 1
serializedVersion: 11
serializedVersion: 10
m_Type: 1
m_Shape: 0
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_Intensity: 0.75
m_Range: 10
@ -2391,12 +2350,8 @@ Light:
m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
m_UseBoundingSphereOverride: 0
m_UseViewFrustumForShadowCasterCull: 1
m_ForceVisible: 0
m_ShadowRadius: 0
m_ShadowAngle: 0
m_LightUnit: 1
m_LuxAtDistance: 1
m_EnableSpotReflector: 1
--- !u!4 &566761699
Transform:
m_ObjectHideFlags: 0
@ -2519,17 +2474,15 @@ MonoBehaviour:
m_lineSpacingMax: 0
m_paragraphSpacing: 0
m_charWidthMaxAdj: 0
m_TextWrappingMode: 1
m_enableWordWrapping: 1
m_wordWrappingRatios: 0.4
m_overflowMode: 0
m_linkedTextComponent: {fileID: 0}
parentLinkedComponent: {fileID: 0}
m_enableKerning: 1
m_ActiveFontFeatures: 6e72656b
m_enableExtraPadding: 0
checkPaddingRequired: 0
m_isRichText: 1
m_EmojiFallbackSupport: 1
m_parseCtrlCharacters: 1
m_isOrthographic: 1
m_isCullingEnabled: 0
@ -3069,17 +3022,15 @@ MonoBehaviour:
m_lineSpacingMax: 0
m_paragraphSpacing: 0
m_charWidthMaxAdj: 0
m_TextWrappingMode: 1
m_enableWordWrapping: 1
m_wordWrappingRatios: 0.4
m_overflowMode: 0
m_linkedTextComponent: {fileID: 0}
parentLinkedComponent: {fileID: 0}
m_enableKerning: 1
m_ActiveFontFeatures: 6e72656b
m_enableExtraPadding: 0
checkPaddingRequired: 0
m_isRichText: 1
m_EmojiFallbackSupport: 1
m_parseCtrlCharacters: 1
m_isOrthographic: 0
m_isCullingEnabled: 0
@ -3117,9 +3068,6 @@ MeshRenderer:
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
@ -3320,17 +3268,15 @@ MonoBehaviour:
m_lineSpacingMax: 0
m_paragraphSpacing: 0
m_charWidthMaxAdj: 0
m_TextWrappingMode: 1
m_enableWordWrapping: 1
m_wordWrappingRatios: 0.4
m_overflowMode: 0
m_linkedTextComponent: {fileID: 0}
parentLinkedComponent: {fileID: 0}
m_enableKerning: 1
m_ActiveFontFeatures: 6e72656b
m_enableExtraPadding: 0
checkPaddingRequired: 0
m_isRichText: 1
m_EmojiFallbackSupport: 1
m_parseCtrlCharacters: 1
m_isOrthographic: 1
m_isCullingEnabled: 0
@ -3464,51 +3410,6 @@ CanvasRenderer:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1071150555}
m_CullTransparentMesh: 1
--- !u!1 &1134242713
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1134242715}
- component: {fileID: 1134242714}
m_Layer: 0
m_Name: IADS
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &1134242714
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1134242713}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a5899f1049cf3d64e8c06c1db772c879, type: 3}
m_Name:
m_EditorClassIdentifier:
_threatTable: []
--- !u!4 &1134242715
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1134242713}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1254435375
GameObject:
m_ObjectHideFlags: 0
@ -3577,9 +3478,6 @@ MeshRenderer:
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
@ -3701,9 +3599,6 @@ MeshRenderer:
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
@ -3837,17 +3732,15 @@ MonoBehaviour:
m_lineSpacingMax: 0
m_paragraphSpacing: 0
m_charWidthMaxAdj: 0
m_TextWrappingMode: 1
m_enableWordWrapping: 1
m_wordWrappingRatios: 0.4
m_overflowMode: 0
m_linkedTextComponent: {fileID: 0}
parentLinkedComponent: {fileID: 0}
m_enableKerning: 1
m_ActiveFontFeatures: 6e72656b
m_enableExtraPadding: 0
checkPaddingRequired: 0
m_isRichText: 1
m_EmojiFallbackSupport: 1
m_parseCtrlCharacters: 1
m_isOrthographic: 0
m_isCullingEnabled: 0
@ -3885,9 +3778,6 @@ MeshRenderer:
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
@ -4317,17 +4207,15 @@ MonoBehaviour:
m_lineSpacingMax: 0
m_paragraphSpacing: 0
m_charWidthMaxAdj: 0
m_TextWrappingMode: 1
m_enableWordWrapping: 1
m_wordWrappingRatios: 0.4
m_overflowMode: 0
m_linkedTextComponent: {fileID: 0}
parentLinkedComponent: {fileID: 0}
m_enableKerning: 1
m_ActiveFontFeatures: 6e72656b
m_enableExtraPadding: 0
checkPaddingRequired: 0
m_isRichText: 1
m_EmojiFallbackSupport: 1
m_parseCtrlCharacters: 1
m_isOrthographic: 1
m_isCullingEnabled: 0
@ -4501,9 +4389,6 @@ MeshRenderer:
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
@ -4726,17 +4611,15 @@ MonoBehaviour:
m_lineSpacingMax: 0
m_paragraphSpacing: 0
m_charWidthMaxAdj: 0
m_TextWrappingMode: 1
m_enableWordWrapping: 1
m_wordWrappingRatios: 0.4
m_overflowMode: 0
m_linkedTextComponent: {fileID: 0}
parentLinkedComponent: {fileID: 0}
m_enableKerning: 1
m_ActiveFontFeatures: 6e72656b
m_enableExtraPadding: 0
checkPaddingRequired: 0
m_isRichText: 1
m_EmojiFallbackSupport: 1
m_parseCtrlCharacters: 1
m_isOrthographic: 0
m_isCullingEnabled: 0
@ -4774,9 +4657,6 @@ MeshRenderer:
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
@ -4978,17 +4858,15 @@ MonoBehaviour:
m_lineSpacingMax: 0
m_paragraphSpacing: 0
m_charWidthMaxAdj: 0
m_TextWrappingMode: 1
m_enableWordWrapping: 1
m_wordWrappingRatios: 0.4
m_overflowMode: 0
m_linkedTextComponent: {fileID: 0}
parentLinkedComponent: {fileID: 0}
m_enableKerning: 1
m_ActiveFontFeatures: 00000000
m_enableExtraPadding: 0
checkPaddingRequired: 0
m_isRichText: 1
m_EmojiFallbackSupport: 1
m_parseCtrlCharacters: 1
m_isOrthographic: 1
m_isCullingEnabled: 0
@ -5583,4 +5461,3 @@ SceneRoots:
- {fileID: 566761699}
- {fileID: 50643634}
- {fileID: 396716026}
- {fileID: 1134242715}

View File

@ -33,16 +33,16 @@ public abstract class Agent : MonoBehaviour {
protected StaticConfig _staticConfig;
// Define delegates
public delegate void InterceptHitEventHandler(Interceptor interceptor, Threat target);
public delegate void InterceptMissEventHandler(Interceptor interceptor, Threat target);
public delegate void AgentHitEventHandler(Agent agent);
public delegate void AgentMissEventHandler(Agent agent);
// Define events
public event InterceptHitEventHandler OnInterceptHit;
public event InterceptMissEventHandler OnInterceptMiss;
public event AgentHitEventHandler OnAgentHit;
public event AgentMissEventHandler OnAgentMiss;
public void SetFlightPhase(FlightPhase flightPhase) {
Debug.Log(
$"Setting flight phase to {flightPhase} at time {SimManager.Instance.GetElapsedSimulationTime()}");
$"Setting {name} flight phase to {flightPhase} at time {SimManager.Instance.GetElapsedSimulationTime()}");
_timeInPhase = 0;
_flightPhase = flightPhase;
}
@ -94,6 +94,10 @@ public abstract class Agent : MonoBehaviour {
return _isHit;
}
public bool IsMiss() {
return _isMiss;
}
public void TerminateAgent() {
_flightPhase = FlightPhase.TERMINATED;
transform.position = new Vector3(0, 0, 0);
@ -101,23 +105,16 @@ public abstract class Agent : MonoBehaviour {
}
// Mark the agent as having hit the target or been hit.
public void HandleInterceptHit(Agent otherAgent) {
public void MarkAsHit() {
_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);
}
OnAgentHit?.Invoke(this);
TerminateAgent();
}
public void HandleInterceptMiss() {
public void MarkAsMiss() {
_isMiss = true;
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);
}
OnAgentMiss?.Invoke(this);
_target = null;
}
TerminateAgent();
@ -131,6 +128,10 @@ public abstract class Agent : MonoBehaviour {
return GetComponent<Rigidbody>().linearVelocity;
}
public Vector3 GetAcceleration() {
return _acceleration;
}
public double GetDynamicPressure() {
var airDensity = Constants.CalculateAirDensityAtAltitude(transform.position.y);
var flowSpeed = GetSpeed();

View File

@ -1,9 +1,6 @@
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 {
@ -11,30 +8,39 @@ public interface IAssignment {
// 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 int InterceptorIndex;
public int ThreatIndex;
public AssignmentItem(Interceptor interceptor, Threat threat) {
Interceptor = interceptor;
Threat = threat;
public AssignmentItem(int missileIndex, int threatIndex) {
InterceptorIndex = missileIndex;
ThreatIndex = threatIndex;
}
}
// 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);
public abstract IEnumerable<AssignmentItem> Assign(List<Agent> missiles, List<Agent> targets);
// Get the list of assignable interceptor indices.
[Pure]
protected static List<Interceptor> GetAssignableInterceptors(in IReadOnlyList<Interceptor> interceptors) {
return interceptors.Where(interceptor => interceptor.IsAssignable()).ToList();
protected static List<int> GetAssignableInterceptorIndices(List<Agent> missiles) {
List<int> assignableInterceptorIndices = new List<int>();
for (int missileIndex = 0; missileIndex < missiles.Count; missileIndex++) {
if (missiles[missileIndex].IsAssignable()) {
assignableInterceptorIndices.Add(missileIndex);
}
}
return assignableInterceptorIndices;
}
// 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();
// Get the list of active target indices.
protected static List<int> GetActiveThreatIndices(List<Agent> threats) {
List<int> activeThreatIndices = new List<int>();
for (int threatIndex = 0; threatIndex < threats.Count; threatIndex++) {
if (!threats[threatIndex].IsHit()) {
activeThreatIndices.Add(threatIndex);
}
}
return activeThreatIndices;
}
}

View File

@ -1,43 +1,39 @@
using System;
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.
// The round-robin assignment class assigns missiles to the targets in a
// round-robin order.
public class RoundRobinAssignment : IAssignment {
// Previous target index that was assigned.
private int prevTargetIndex = -1;
// 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;
// Assign a target to each interceptor that has not been assigned a target yet.
public IEnumerable<IAssignment.AssignmentItem> Assign(List<Agent> missiles, List<Agent> targets) {
List<IAssignment.AssignmentItem> assignments = new List<IAssignment.AssignmentItem>();
List<int> assignableInterceptorIndices = IAssignment.GetAssignableInterceptorIndices(missiles);
if (assignableInterceptorIndices.Count == 0) {
return assignments;
}
List<int> activeThreatIndices = IAssignment.GetActiveThreatIndices(targets);
if (activeThreatIndices.Count == 0) {
return assignments;
}
foreach (int missileIndex in assignableInterceptorIndices) {
int nextActiveTargetIndex = activeThreatIndices.FindIndex(index => index > prevTargetIndex);
if (nextActiveTargetIndex == -1) {
nextActiveTargetIndex = 0;
}
int nextTargetIndex = activeThreatIndices[nextActiveTargetIndex];
assignments.Add(new IAssignment.AssignmentItem(missileIndex, nextTargetIndex));
prevTargetIndex = nextTargetIndex;
}
return assignments;
}
}

View File

@ -3,64 +3,70 @@ 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
// The threat assignment class assigns missiles to the targets based
// on the threat level of the targets.
public class ThreatAssignment : IAssignment {
// Assign a target to each interceptor that has not been assigned a target yet.
[Pure]
public IEnumerable<IAssignment.AssignmentItem> Assign(in IReadOnlyList<Interceptor> interceptors, in IReadOnlyList<ThreatData> targets) {
public IEnumerable<IAssignment.AssignmentItem> Assign(List<Agent> missiles, List<Agent> 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");
List<int> assignableInterceptorIndices = IAssignment.GetAssignableInterceptorIndices(missiles);
if (assignableInterceptorIndices.Count == 0) {
return assignments;
}
List<ThreatData> activeThreats = IAssignment.GetActiveThreats(targets);
if (activeThreats.Count == 0) {
Debug.LogWarning("No active threats found");
List<int> activeThreatIndices = IAssignment.GetActiveThreatIndices(targets);
if (activeThreatIndices.Count == 0) {
return assignments;
}
Vector3 positionToDefend = Vector3.zero;
List<ThreatInfo> threatInfos =
CalculateThreatLevels(activeThreats, positionToDefend);
CalculateThreatLevels(targets, activeThreatIndices, 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();
foreach (int missileIndex in assignableInterceptorIndices) {
if (missiles[missileIndex].HasAssignedTarget())
continue;
if (threatInfos.Count == 0)
break;
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;
// Find the optimal target for this interceptor based on distance and threat
ThreatInfo optimalTarget = null;
float optimalScore = float.MinValue;
foreach (ThreatInfo threat in threatInfos) {
float distance = Vector3.Distance(missiles[missileIndex].transform.position,
targets[threat.TargetIndex].transform.position);
float score = threat.ThreatLevel / distance; // Balance threat level with proximity
if (score > optimalScore) {
optimalScore = score;
optimalTarget = threat;
}
}
if (optimalTarget != null) {
assignments.Add(new IAssignment.AssignmentItem(missileIndex, optimalTarget.TargetIndex));
threatInfos.Remove(optimalTarget);
}
}
return assignments;
}
private List<ThreatInfo> CalculateThreatLevels(List<ThreatData> threatTable,
Vector3 defensePosition) {
private List<ThreatInfo> CalculateThreatLevels(List<Agent> targets, List<int> activeThreatIndices,
Vector3 missilesMeanPosition) {
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;
foreach (int targetIndex in activeThreatIndices) {
Agent target = targets[targetIndex];
float distanceToMean = Vector3.Distance(target.transform.position, missilesMeanPosition);
float velocityMagnitude = target.GetVelocity().magnitude;
// Calculate threat level based on proximity and velocity
float threatLevel = (1 / distanceToMean) * velocityMagnitude;
threatInfos.Add(new ThreatInfo(threatData, threatLevel));
threatInfos.Add(new ThreatInfo(targetIndex, threatLevel));
}
// Sort threats in descending order
@ -68,11 +74,11 @@ public class ThreatAssignment : IAssignment {
}
private class ThreatInfo {
public ThreatData ThreatData { get; }
public int TargetIndex { get; }
public float ThreatLevel { get; }
public ThreatInfo(ThreatData threatData, float threatLevel) {
ThreatData = threatData;
public ThreatInfo(int targetIndex, float threatLevel) {
TargetIndex = targetIndex;
ThreatLevel = threatLevel;
}
}

View File

@ -4,115 +4,121 @@ using UnityEngine;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
[Serializable]
public class SimulationConfig {
[Header("Simulation Settings")]
public float timeScale = 0.05f;
[Header("Simulation Settings")]
public float timeScale = 0.05f;
[Header("Interceptor Swarm Configurations")]
public List<SwarmConfig> interceptor_swarm_configs = new List<SwarmConfig>();
// [Header("Defense Points")]
// public SwarmConfig defense_points_config;
[Header("Threat Swarm Configurations")]
public List<SwarmConfig> threat_swarm_configs = new List<SwarmConfig>();
[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;
public LaunchConfig launch_config;
public SensorConfig sensor_config;
}
[Serializable]
[Serializable]
public class SwarmConfig {
public int num_agents;
public AgentConfig agent_config;
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 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
};
}
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;
public Vector3 position;
public Vector3 rotation;
public Vector3 velocity;
}
[Serializable]
public class StandardDeviation {
public Vector3 position;
public Vector3 velocity;
public Vector3 position;
public Vector3 velocity;
}
[Serializable]
public class LaunchConfig {
public float launch_time;
public float launch_time;
}
[Serializable]
public class PlottingConfig {
public ConfigColor color;
public LineStyle linestyle;
public Marker marker;
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;
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;
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;
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;
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 }
public enum ThreatType {
DRONE,
FIXED_WING_MISSILE,
ROLL_STABILIZED_MISSILE
} // Add ballistic later -michael
[JsonConverter(typeof(StringEnumConverter))]
public enum ConfigColor { BLUE, GREEN, RED }
[JsonConverter(typeof(StringEnumConverter))]

View File

@ -0,0 +1,28 @@
using UnityEngine;
/// <summary>
/// Static targetable point to defend
/// </summary>
public class DefendPoint : Agent {
// Currently just initializes to the origin
public DefendPoint() {
// Set the initial state
this.transform.position = Vector3.zero;
}
protected override void FixedUpdate() {
return;
}
protected override void UpdateBoost(double deltaTime) {
return;
}
protected override void UpdateMidCourse(double deltaTime) {
return;
}
protected override void UpdateReady(double deltaTime) {
return;
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 7eed014a152a65043be19cd044faa5d2

View File

@ -3,7 +3,6 @@ using UnityEditor;
using System;
using System.Collections.Generic;
#if UNITY_EDITOR
public class GenerateCone : EditorWindow {
private int sides = 16;
private float baseRadius = 1f;
@ -141,4 +140,3 @@ public class GenerateCone : EditorWindow {
return sum / vertices.Count;
}
}
#endif

View File

@ -1,146 +1,30 @@
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Collections;
using System;
// Integrated Air Defense System
public class IADS : MonoBehaviour {
public enum TargetStatus { UNASSIGNED, ASSIGNED, HIT, DEGRADED, DESTROYED }
public enum ThreatAssignmentStyle {
ONE_TIME,
CONTINUOUS
}
// Look up threat status by unique threat ID
public Dictionary<string, TargetStatus> _targetStatusDictionary;
public static IADS Instance { get; private set; }
private IAssignment _assignmentScheme;
private List<Threat> _threats;
[SerializeField]
private List<ThreatData> _threatTable = new List<ThreatData>();
private Dictionary<Threat, ThreatData> _threatDataMap = new Dictionary<Threat, ThreatData>();
private List<Interceptor> _interceptors;
private List<Interceptor> _assignmentQueue = new List<Interceptor>();
private List<Vessel> _vessels;
private void Awake() {
if (Instance == null) {
Instance = this;
} else {
Destroy(gameObject);
}
public delegate void RegisterNewThreatDelegate(Threat threat);
public event RegisterNewThreatDelegate OnRegisterNewThreat;
}
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}");
}
void Start() {
_threats = new List<Threat>();
}
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;
_threats.Add(threat);
OnRegisterNewThreat?.Invoke(threat);
}
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();
}
}

View File

@ -1,48 +0,0 @@
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
}
}

View File

@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: adc0c5dbdb9dc7d498b50cf9a15c2db5

View File

@ -74,7 +74,7 @@ public class Interceptor : Agent {
private void OnTriggerEnter(Collider other) {
if (other.gameObject.name == "Floor") {
this.HandleInterceptMiss();
this.MarkAsMiss();
}
// Check if the collision is with another Agent
Agent otherAgent = other.gameObject.GetComponentInParent<Agent>();
@ -87,13 +87,13 @@ public class Interceptor : Agent {
// 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);
this.MarkAsHit();
otherAgent.MarkAsHit();
} else {
// Set red for miss
markerObject.GetComponent<Renderer>().material.color = new Color(1, 0, 0, 0.15f);
this.HandleInterceptMiss();
this.MarkAsMiss();
// otherAgent.MarkAsMiss();
}
}

View File

@ -48,6 +48,6 @@ public class Hydra70 : Interceptor {
}
break;
}
IADS.Instance.RequestThreatAssignment(submunitions);
SimManager.Instance.AssignInterceptorsToThreats(submunitions);
}
}

View File

@ -28,7 +28,7 @@ public class Micromissile : Interceptor {
// Check whether the threat should be considered a miss
SensorOutput sensorOutput = GetComponent<Sensor>().Sense(_target);
if (sensorOutput.velocity.range > 1000f) {
this.HandleInterceptMiss();
this.MarkAsMiss();
}
// Calculate the acceleration input

View File

@ -3,261 +3,70 @@ 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 const float UpdateRate = 0.01f; // 100 Hz
private StreamWriter writer;
private Coroutine monitorRoutine;
private void Start()
{
SimManager.Instance.OnSimulationStarted += RegisterSimulationStarted;
SimManager.Instance.OnSimulationEnded += RegisterSimulationEnded;
SimManager.Instance.OnNewThreat += RegisterNewThreat;
SimManager.Instance.OnNewInterceptor += RegisterNewInterceptor;
InitializeFile();
monitorRoutine = StartCoroutine(MonitorRoutine());
}
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()
private void InitializeFile()
{
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;
}
string fileName = $"sim_telemetry_{timestamp}.csv";
string directory = Application.persistentDataPath + "/Telemetry/Logs/";
Directory.CreateDirectory(directory);
string path = Path.Combine(directory, fileName);
writer = new StreamWriter(path, false);
writer.WriteLine("Time,AgentID,AgentX,AgentY,AgentZ,AgentVX,AgentVY,AgentVZ,AgentState,AgentType");
Debug.Log($"Monitoring simulation data to {path}");
}
private IEnumerator MonitorRoutine()
{
while (true)
{
RecordTelemetry();
yield return new WaitForSeconds(_updateRate);
ExportTelemetry();
yield return new WaitForSeconds(UpdateRate);
}
}
private void RecordTelemetry()
private void ExportTelemetry()
{
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++)
foreach (var agent in SimManager.Instance.GetActiveThreats().Cast<Agent>().Concat(SimManager.Instance.GetActiveInterceptors().Cast<Agent>()))
{
var agent = agents[i];
if (!agent.gameObject.activeInHierarchy)
continue;
Vector3 pos = agent.transform.position;
if (pos == Vector3.zero)
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}");
}
}
Vector3 vel = agent.GetComponent<Rigidbody>().linearVelocity;
string type = agent is Threat ? "T" : "M";
writer.WriteLine($"{time:F2},{agent.name},{pos.x:F2},{pos.y:F2},{pos.z:F2},{vel.x:F2},{vel.y:F2},{vel.z:F2},{(int)agent.GetFlightPhase()},{type}");
}
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());
writer.Flush();
}
private void RegisterSimulationEnded()
{
StopCoroutine(_monitorRoutine);
CloseLogFiles();
WriteEventsToFile();
StartCoroutine(ConvertBinaryTelemetryToCsvCoroutine(_telemetryBinPath, Path.ChangeExtension(_telemetryBinPath, ".csv")));
writer.Close();
StopCoroutine(monitorRoutine);
}
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();
if (writer != null)
{
writer.Close();
}
}
}

View File

@ -4,7 +4,7 @@ MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: -5
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:

View File

@ -1,3 +1,4 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
@ -19,30 +20,21 @@ public class SimManager : MonoBehaviour {
[SerializeField]
public SimulationConfig simulationConfig;
private List<Interceptor> _interceptors = new List<Interceptor>();
private List<Interceptor> _activeInterceptors = new List<Interceptor>();
private List<Interceptor> _interceptorObjects = new List<Interceptor>();
private List<Threat> _threatObjects = new List<Threat>();
private List<Threat> _unassignedThreats = new List<Threat>();
private List<Threat> _threats = new List<Threat>();
private List<Threat> _activeThreats = new List<Threat>();
private float _elapsedSimulationTime = 0f;
private float endTime = 100f; // Set an appropriate end time
private bool simulationRunning = false;
private IAssignment _assignmentScheme;
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>
@ -56,12 +48,12 @@ public class SimManager : MonoBehaviour {
}
public List<Threat> GetActiveThreats() {
return _threatObjects.Where(threat => !threat.IsHit()).ToList();
return _activeThreats;
}
public List<Agent> GetActiveAgents() {
return _activeInterceptors.ConvertAll(interceptor => interceptor as Agent)
.Concat(GetActiveThreats().ConvertAll(threat => threat as Agent))
.Concat(_activeThreats.ConvertAll(threat => threat as Agent))
.ToList();
}
@ -83,7 +75,6 @@ public class SimManager : MonoBehaviour {
StartSimulation();
ResumeSimulation();
}
}
public void SetTimeScale(float timeScale) {
@ -93,9 +84,8 @@ public class SimManager : MonoBehaviour {
}
public void StartSimulation() {
OnSimulationStarted?.Invoke();
InitializeSimulation();
OnSimulationStarted?.Invoke();
}
public void PauseSimulation() {
@ -113,14 +103,13 @@ public class SimManager : MonoBehaviour {
}
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);
var interceptor = CreateInterceptor(swarmConfig.agent_config);
interceptor.OnAgentHit += RegisterInterceptorHit;
interceptor.OnAgentMiss += RegisterInterceptorMiss;
}
}
@ -128,38 +117,78 @@ public class SimManager : MonoBehaviour {
// 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);
var threat = CreateThreat(swarmConfig.agent_config);
if (threat == null) {
Debug.LogError($"Failed to create threat: {swarmConfig.agent_config}");
continue;
}
threat.OnAgentHit += RegisterThreatHit;
threat.OnAgentMiss += RegisterThreatMiss;
}
}
_assignmentScheme = new ThreatAssignment();
// Invoke the simulation started event to let listeners
// know to invoke their own handler behavior
OnSimulationStarted?.Invoke();
}
public void AssignInterceptorsToThreats() {
IADS.Instance.AssignInterceptorsToThreats(_interceptorObjects);
AssignInterceptorsToThreats(_interceptors);
}
public void RegisterInterceptorHit(Interceptor interceptor, Threat threat) {
public void RegisterInterceptorHit(Agent interceptor) {
if (interceptor is Interceptor missileComponent) {
_activeInterceptors.Remove(missileComponent);
}
}
public void RegisterInterceptorMiss(Interceptor interceptor, Threat threat) {
public void RegisterInterceptorMiss(Agent interceptor) {
if (interceptor is Interceptor missileComponent) {
_activeInterceptors.Remove(missileComponent);
}
}
public void RegisterThreatHit(Interceptor interceptor, Threat threat) {
// Placeholder
public void RegisterThreatHit(Agent threat) {
if (threat is Threat targetComponent) {
_activeThreats.Remove(targetComponent);
}
}
public void RegisterThreatMiss(Interceptor interceptor, Threat threat) {
Debug.Log($"RegisterThreatMiss: Interceptor {interceptor.name} missed threat {threat.name}");
// Placeholder
public void RegisterThreatMiss(Agent threat) {
if (threat is Threat targetComponent) {
_unassignedThreats.Add(targetComponent);
}
}
/// <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) {
// Convert Interceptor and Threat lists to Agent lists
List<Agent> missileAgents = new List<Agent>(missilesToAssign.ConvertAll(m => m as Agent));
// Convert Threat list to Agent list, excluding already assigned targets
List<Agent> targetAgents = _unassignedThreats.ToList<Agent>();
// Perform the assignment
IEnumerable<IAssignment.AssignmentItem> assignments =
_assignmentScheme.Assign(missileAgents, targetAgents);
// Apply the assignments to the missiles
foreach (var assignment in assignments) {
if (assignment.InterceptorIndex < missilesToAssign.Count) {
Interceptor interceptor = missilesToAssign[assignment.InterceptorIndex];
Threat threat = _unassignedThreats[assignment.ThreatIndex];
interceptor.AssignTarget(threat);
Debug.Log($"Interceptor {interceptor.name} assigned to threat {threat.name}");
}
}
// TODO this whole function should be optimized
_unassignedThreats.RemoveAll(
threat => missilesToAssign.Any(interceptor => interceptor.GetAssignedTarget() == threat));
}
/// <summary>
/// Creates a interceptor based on the provided configuration.
@ -167,41 +196,32 @@ public class SimManager : MonoBehaviour {
/// <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" };
string prefabName =
config.interceptor_type switch { InterceptorType.HYDRA_70 => "Hydra70",
InterceptorType.MICROMISSILE => "Micromissile",
_ => "Hydra70" };
GameObject interceptorObject = CreateAgent(config, prefabName);
if (interceptorObject == null)
GameObject missileObject = CreateAgent(config, prefabName);
if (missileObject == null)
return null;
// Interceptor-specific logic
switch (config.dynamic_config.sensor_config.type) {
case SensorType.IDEAL:
interceptorObject.AddComponent<IdealSensor>();
missileObject.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;
_interceptors.Add(missileObject.GetComponent<Interceptor>());
_activeInterceptors.Add(missileObject.GetComponent<Interceptor>());
// 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>();
int missileId = _interceptors.Count;
missileObject.name = $"{config.interceptor_type}_Interceptor_{missileId}";
return missileObject.GetComponent<Interceptor>();
}
/// <summary>
@ -211,28 +231,23 @@ public class SimManager : MonoBehaviour {
/// <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",
ThreatType.DRONE => "Drone", ThreatType.FIXED_WING_MISSILE => "FixedWingMissileThreat",
ThreatType.ROLL_STABILIZED_MISSILE => "RollStabilizedMissileThreat",
_ => throw new System.ArgumentException($"Unsupported threat type: {config.threat_type}")
};
GameObject threatObject = CreateAgent(config, prefabName);
if (threatObject == null)
if (threatObject == null) {
Debug.LogError($"Failed to create threat for {prefabName}.");
return null;
}
_threats.Add(threatObject.GetComponent<Threat>());
_activeThreats.Add(threatObject.GetComponent<Threat>());
_unassignedThreats.Add(threatObject.GetComponent<Threat>());
Threat threat = threatObject.GetComponent<Threat>();
// Assign a unique and simple ID
int targetId = _threatObjects.Count;
int targetId = _threats.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>();
}
@ -282,22 +297,22 @@ public class SimManager : MonoBehaviour {
_elapsedSimulationTime = 0f;
simulationRunning = IsSimulationRunning();
// Clear existing interceptors and threats
foreach (var interceptor in _interceptorObjects) {
// Clear existing missiles and targets
foreach (var interceptor in _interceptors) {
if (interceptor != null) {
Destroy(interceptor.gameObject);
}
}
foreach (var threat in _threatObjects) {
foreach (var threat in _threats) {
if (threat != null) {
Destroy(threat.gameObject);
}
}
_interceptorObjects.Clear();
_activeInterceptors.Clear();
_threatObjects.Clear();
_interceptors.Clear();
_threats.Clear();
_unassignedThreats.Clear();
StartSimulation();
}
@ -305,8 +320,8 @@ public class SimManager : MonoBehaviour {
void Update() {
// Check if all missiles have terminated
bool allInterceptorsTerminated = true;
foreach (var interceptor in _interceptorObjects) {
if (interceptor != null && interceptor.GetFlightPhase() != Agent.FlightPhase.TERMINATED) {
foreach (var interceptor in _interceptors) {
if (interceptor != null && !interceptor.IsHit() && !interceptor.IsMiss()) {
allInterceptorsTerminated = false;
break;
}

View File

@ -4,7 +4,7 @@ MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 100
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:

View File

@ -1,11 +0,0 @@
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() {}
}

View File

@ -2,7 +2,7 @@ using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DroneTarget : Threat {
public class DroneThreat : Threat {
// Start is called before the first frame update
protected override void Start() {
base.Start();

View File

@ -0,0 +1,41 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FixedWingMissileThreat : Threat {
protected override void UpdateBoost(double deltaTime) {
throw new System.NotImplementedException();
}
protected override void UpdateMidCourse(double deltaTime) {
throw new System.NotImplementedException();
}
/// <summary>
/// Strategy for moving the Threat in a straight path towards its target.
/// </summary>
public class DirectPathStrategy : NavigationStrategy {
public override void Execute(Threat threat, List<Threat> swarmMates, FlightPhase flightPhase,
List<Interceptor> interceptors, double deltaTime) {
throw new System.NotImplementedException();
}
}
/// <summary>
/// Strategy for moving the Threat in an S-curve towards a predefined target.
/// </summary>
public class SlalomStrategy : NavigationStrategy {
private float maxAmplitude;
private float periodDistance;
public SlalomStrategy(float maxAmplitude, float periodDistance) {
this.maxAmplitude = maxAmplitude;
this.periodDistance = periodDistance;
}
public override void Execute(Threat threat, List<Threat> swarmMates, FlightPhase flightPhase,
List<Interceptor> interceptors, double deltaTime) {
throw new System.NotImplementedException();
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 4e6c2fbd1e492be448760f5045b13b2e

View File

@ -0,0 +1,204 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// Base class for missile targets. Uses same set of flight phases as base Hydra-70.
/// </summary>
public class RollStabilizedMissileThreat : Threat {
protected float boostAcceleration = 20;
protected float midcourseAcceleration = 0;
protected float terminalAcceleration = 22;
protected float maxAmplitude = 22;
public RollStabilizedMissileThreat() {
strategy = new DirectPathStrategy();
}
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) {
Vector3 accelerationInput = Vector3.zero;
// Calculate and set the total acceleration
Vector3 acceleration = CalculateAcceleration(accelerationInput);
GetComponent<Rigidbody>().AddForce(acceleration, ForceMode.Acceleration);
}
protected Vector3 CalculateAcceleration(Vector3 accelerationInput,
bool compensateForGravity = false) {
Vector3 gravity = Physics.gravity;
if (compensateForGravity) {
Vector3 gravityProjection = CalculateGravityProjectionOnPitchAndYaw();
accelerationInput -= gravityProjection;
}
float airDrag = CalculateDrag();
float liftInducedDrag = CalculateLiftInducedDrag(accelerationInput);
float dragAcceleration = -(airDrag + liftInducedDrag);
// Project the drag acceleration onto the forward direction
Vector3 dragAccelerationAlongRoll = dragAcceleration * transform.forward;
_dragAcceleration = dragAccelerationAlongRoll;
return accelerationInput + gravity + dragAccelerationAlongRoll;
}
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);
}
// ===========================================================
// STRATEGIES
// ===========================================================
/// <summary>
/// Strategy for moving the Threat in a straight path towards its target.
/// </summary>
public class DirectPathStrategy : NavigationStrategy {
DefendPoint target = new DefendPoint();
private float _navigationGain = 3f; // Typically 3-5
private SensorOutput _sensorOutput;
private Vector3 _accelerationCommand;
private double _elapsedTime = 0;
public override void Execute(Threat threat, List<Threat> swarmMates, FlightPhase flightPhase,
List<Interceptor> interceptors, double deltaTime) {
RollStabilizedMissileThreat missileThreat = threat as RollStabilizedMissileThreat;
if (missileThreat == null) {
Debug.LogError("DirectPathStrategy can only be used with RollStabilizedMissileThreat");
return;
}
_elapsedTime += deltaTime;
Vector3 accelerationInput = Vector3.zero;
if (target != null) {
// Correct the state of the threat model at the sensor frequency
float sensorUpdatePeriod =
1f / missileThreat._agentConfig.dynamic_config.sensor_config.frequency;
if (_elapsedTime >= sensorUpdatePeriod) {
_sensorOutput = new SensorOutput();
missileThreat.GetComponent<Sensor>().Sense(target);
Debug.Log(_sensorOutput.velocity.range);
_elapsedTime = 0;
}
// Check whether the threat should be considered a miss
SensorOutput sensorOutput = missileThreat.GetComponent<Sensor>().Sense(target);
if (sensorOutput.velocity.range > 1000f) {
missileThreat.MarkAsMiss();
}
// Calculate the acceleration input
accelerationInput = CalculateAccelerationCommand(missileThreat, _sensorOutput);
} else {
Debug.LogError("DirectPathStrategy requires a target to be set");
}
// Calculate and set the total acceleration
Vector3 acceleration =
missileThreat.CalculateAcceleration(accelerationInput, compensateForGravity: true);
missileThreat.GetComponent<Rigidbody>().AddForce(acceleration, ForceMode.Acceleration);
}
private Vector3 CalculateAccelerationCommand(Threat threat, SensorOutput sensorOutput) {
RollStabilizedMissileThreat missileThreat = threat as RollStabilizedMissileThreat;
// Implement Proportional Navigation guidance law
Vector3 accelerationCommand;
// 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 =
missileThreat.transform.right * acc_az + missileThreat.transform.up * acc_el;
// Clamp the acceleration command to the maximum acceleration
float maxAcceleration = missileThreat.CalculateMaxAcceleration();
accelerationCommand = Vector3.ClampMagnitude(accelerationCommand, maxAcceleration);
// Update the stored acceleration command for debugging
_accelerationCommand = accelerationCommand;
return accelerationCommand;
}
}
/// <summary>
/// Strategy for moving the Threat in a spiral towards a predefined target.
/// </summary>
public class SpiralStrategy : NavigationStrategy {
private float spiralRadius;
private float periodDistance;
public SpiralStrategy(float spiralRadius, float periodDistance) {
this.spiralRadius = spiralRadius;
this.periodDistance = periodDistance;
}
public override void Execute(Threat threat, List<Threat> swarmMates, FlightPhase flightPhase,
List<Interceptor> interceptors, double deltaTime) {
throw new System.NotImplementedException();
}
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 0f46139bb9c99e9499af0597ad648f3e
guid: 0d798867791c1444b985d8807b144572
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@ -3,6 +3,29 @@ using System.Collections.Generic;
using UnityEngine;
public abstract class Threat : Agent {
/// <summary>
/// Strategy for moving the Threat towards a predefined target.
/// </summary>
public abstract class NavigationStrategy {
/// <summary>
/// Execute one timestep of the strategy for the given threat and flight phase. Should only
/// apply normal forces to the threat. Can also change the threat's flight phase. Should NOT
/// manage thrust.
/// </summary>
/// <param name="threat">Parent Threat object</param>
/// <param name="swarmMates">List of other threats in the swarm</param>
/// <param name="flightPhase">Current flight phase</param>
/// <param name="interceptors">List of active interceptors</param>
/// <param name="deltaTime">Timestep</param>
public abstract void Execute(Threat threat, List<Threat> swarmMates, FlightPhase flightPhase,
List<Interceptor> interceptors, double deltaTime);
}
/// <summary>
/// Navigation strategy that this threat will use to move towards its target.
/// </summary>
public NavigationStrategy strategy;
public override bool IsAssignable() {
return false;
}
@ -13,5 +36,18 @@ public abstract class Threat : Agent {
protected override void FixedUpdate() {
base.FixedUpdate();
// NOTE: no swarm-mates for now
strategy.Execute(this, new List<Threat>(), GetFlightPhase(),
SimManager.Instance.GetActiveInterceptors(), Time.fixedDeltaTime);
}
/// <summary>
/// OVERRIDE: THIS SHOULD NEVER BE CALLED; threats always start in midcourse or (at the very
/// earliest) boost phase.
/// </summary>
/// <param name="deltaTime"></param>
/// <exception cref="System.NotImplementedException"></exception>
protected override void UpdateReady(double deltaTime) {
throw new System.NotImplementedException();
}
}

View File

@ -1,17 +0,0 @@
{
"name": "bamlab.micromissiles",
"rootNamespace": "",
"references": [
"GUID:6055be8ebefd69e48b49212b09b47b2f",
"GUID:c668b7a00ffe56a498ddb1fc9f96f4e6"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@ -0,0 +1,146 @@
{
"timeScale": 1,
"interceptor_swarm_configs": [
{
"num_agents": 1,
"agent_config": {
"interceptor_type": "HYDRA_70",
"threat_type": "ROLL_STABILIZED_MISSILE",
"initial_state": {
"position": {
"x": 0,
"y": 20,
"z": 0
},
"rotation": {
"x": -45,
"y": 0,
"z": 0
},
"velocity": {
"x": 0,
"y": 10,
"z": 10
}
},
"standard_deviation": {
"position": {
"x": 10,
"y": 0,
"z": 10
},
"velocity": {
"x": 5,
"y": 0,
"z": 1
}
},
"dynamic_config": {
"launch_config": {
"launch_time": 0
},
"sensor_config": {
"type": "IDEAL",
"frequency": 100
}
},
"submunitions_config": {
"num_submunitions": 7,
"launch_config": {
"launch_time": 4
},
"agent_config": {
"interceptor_type": "MICROMISSILE",
"initial_state": {
"position": {
"x": 0,
"y": 0,
"z": 0
},
"rotation": {
"x": 0,
"y": 0,
"z": 0
},
"velocity": {
"x": 0,
"y": 0,
"z": 0
}
},
"standard_deviation": {
"position": {
"x": 5,
"y": 5,
"z": 5
},
"velocity": {
"x": 0,
"y": 0,
"z": 0
}
},
"dynamic_config": {
"launch_config": {
"launch_time": 0
},
"sensor_config": {
"type": "IDEAL",
"frequency": 100
}
}
}
}
}
}
],
"threat_swarm_configs": [
{
"num_agents": 7,
"agent_config": {
"threat_type": "ROLL_STABILIZED_MISSILE",
"initial_state": {
"position": {
"x": 0,
"y": 600,
"z": 6000
},
"rotation": {
"x": 90,
"y": 50,
"z": 0
},
"velocity": {
"x": 0,
"y": 0,
"z": -200
}
},
"standard_deviation": {
"position": {
"x": 1000,
"y": 200,
"z": 100
},
"velocity": {
"x": 0,
"y": 0,
"z": 25
}
},
"dynamic_config": {
"launch_config": {
"launch_time": 0
},
"sensor_config": {
"type": "IDEAL",
"frequency": 0
}
},
"submunitions_config": {
"num_submunitions": 0
}
}
}
]
}

View File

@ -1,6 +1,5 @@
fileFormatVersion: 2
guid: b00bb4a2208bd164392fce8408d145e7
folderAsset: yes
guid: 22c1e00f13b5ec6468892b8c6df37933
DefaultImporter:
externalObjects: {}
userData:

View File

@ -0,0 +1,25 @@
{
"accelerationConfig": {
"maxReferenceAcceleration": 300,
"referenceSpeed": 1000
},
"boostConfig": {
"boostTime": 0.3,
"boostAcceleration": 350
},
"liftDragConfig": {
"liftCoefficient": 0.2,
"dragCoefficient": 0.7,
"liftDragRatio": 5
},
"bodyConfig": {
"mass": 0.37,
"crossSectionalArea": 0.0003,
"finArea": 0.0006,
"bodyArea": 0.01
},
"hitConfig": {
"hitRadius": 1,
"killProbability": 0.9
}
}

View File

@ -1,6 +1,5 @@
fileFormatVersion: 2
guid: bd2143c1edb61ab4ab876add0f4ab9f2
folderAsset: yes
guid: 72573475cbd372d4dbb25291cc6545b0
DefaultImporter:
externalObjects: {}
userData:

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 5ab47dc725c65cb4cb1fa6948fb0c9a7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: c80e4daffe8e47e4b8e151adf0b21a86
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,25 +0,0 @@
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
using System.Collections;
public class SanityTest
{
[Test]
public void SanityTestSimplePasses()
{
// Use the Assert class to test conditions
Assert.Pass("This test passes.");
}
// A UnityTest behaves like a coroutine in Play Mode. In Edit Mode you can use
// `yield return null;` to skip a frame.
[UnityTest]
public IEnumerator SanityTestWithEnumeratorPasses()
{
// Use the Assert class to test conditions.
// Use yield to skip a frame.
yield return null;
Assert.Pass("This test passes after skipping a frame.");
}
}

View File

@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: b48cf6e434723a449a49186eeda32d3f

View File

@ -1,138 +0,0 @@
using NUnit.Framework;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.TestTools;
using System.Linq;
public class ThreatAssignmentTests
{
[Test]
public void Assign_Should_Assign_All_Interceptors_And_Threats()
{
// Arrange
ThreatAssignment threatAssignment = new ThreatAssignment();
// Create interceptors
List<Interceptor> interceptors = new List<Interceptor>
{
new GameObject("Interceptor 1").AddComponent<Micromissile>(),
new GameObject("Interceptor 2").AddComponent<Micromissile>(),
new GameObject("Interceptor 3").AddComponent<Micromissile>()
};
// Create threats
Threat threat1 = new GameObject("Threat 1").AddComponent<DroneTarget>();
Threat threat2 = new GameObject("Threat 2").AddComponent<DroneTarget>();
Threat threat3 = new GameObject("Threat 3").AddComponent<DroneTarget>();
// Add Rigidbody components to threats to set velocities
Rigidbody rb1 = threat1.gameObject.AddComponent<Rigidbody>();
Rigidbody rb2 = threat2.gameObject.AddComponent<Rigidbody>();
Rigidbody rb3 = threat3.gameObject.AddComponent<Rigidbody>();
// Set positions and velocities
threat1.transform.position = Vector3.forward * -20f;
threat2.transform.position = Vector3.forward * -20f;
threat3.transform.position = Vector3.forward * -20f;
rb1.linearVelocity = Vector3.forward * 5f;
rb2.linearVelocity = Vector3.forward * 10f;
rb3.linearVelocity = Vector3.forward * 15f;
// Create threat data
List<ThreatData> threats = new List<ThreatData>
{
new ThreatData(threat1, "Threat1ID"),
new ThreatData(threat2, "Threat2ID"),
new ThreatData(threat3, "Threat3ID")
};
// Act
IEnumerable<IAssignment.AssignmentItem> assignments = threatAssignment.Assign(interceptors, threats);
// Assert
Assert.AreEqual(3, assignments.Count(), "All interceptors should be assigned");
HashSet<Interceptor> assignedInterceptors = new HashSet<Interceptor>();
HashSet<Threat> assignedThreats = new HashSet<Threat>();
foreach (var assignment in assignments)
{
Assert.IsNotNull(assignment.Interceptor, "Interceptor should not be null");
Assert.IsNotNull(assignment.Threat, "Threat should not be null");
assignedInterceptors.Add(assignment.Interceptor);
assignedThreats.Add(assignment.Threat);
}
Assert.AreEqual(3, assignedInterceptors.Count, "All interceptors should be unique");
Assert.AreEqual(3, assignedThreats.Count, "All threats should be assigned");
// Verify that threats are assigned in order of their threat level (based on velocity and distance)
var orderedAssignments = assignments.OrderByDescending(a => a.Threat.GetVelocity().magnitude / Vector3.Distance(a.Threat.transform.position, Vector3.zero)).ToList();
Assert.AreEqual(threat3, orderedAssignments[0].Threat, "Highest threat should be assigned first");
Assert.AreEqual(threat2, orderedAssignments[1].Threat, "Second highest threat should be assigned second");
Assert.AreEqual(threat1, orderedAssignments[2].Threat, "Lowest threat should be assigned last");
}
[Test]
public void Assign_Should_Handle_More_Interceptors_Than_Threats()
{
// Arrange
ThreatAssignment threatAssignment = new ThreatAssignment();
// Create interceptors
List<Interceptor> interceptors = new List<Interceptor>
{
new GameObject("Interceptor 1").AddComponent<Micromissile>(),
new GameObject("Interceptor 2").AddComponent<Micromissile>(),
new GameObject("Interceptor 3").AddComponent<Micromissile>()
};
// Create threats
Threat threat1 = new GameObject("Threat 1").AddComponent<DroneTarget>();
Threat threat2 = new GameObject("Threat 2").AddComponent<DroneTarget>();
// Add Rigidbody components to threats to set velocities
Rigidbody rb1 = threat1.gameObject.AddComponent<Rigidbody>();
Rigidbody rb2 = threat2.gameObject.AddComponent<Rigidbody>();
// Set positions and velocities
threat1.transform.position = Vector3.up * 10f;
threat2.transform.position = Vector3.right * 5f;
rb1.linearVelocity = Vector3.forward * 10f;
rb2.linearVelocity = Vector3.forward * 15f;
// Create threat data
List<ThreatData> threats = new List<ThreatData>
{
new ThreatData(threat1, "Threat1ID"),
new ThreatData(threat2, "Threat2ID")
};
// Act
IEnumerable<IAssignment.AssignmentItem> assignments = threatAssignment.Assign(interceptors, threats);
// Assert
Assert.AreEqual(2, assignments.Count(), "All threats should be assigned");
HashSet<Interceptor> assignedInterceptors = new HashSet<Interceptor>();
HashSet<Threat> assignedThreats = new HashSet<Threat>();
foreach (var assignment in assignments)
{
Assert.IsNotNull(assignment.Interceptor, "Interceptor should not be null");
Assert.IsNotNull(assignment.Threat, "Threat should not be null");
assignedInterceptors.Add(assignment.Interceptor);
assignedThreats.Add(assignment.Threat);
}
Assert.AreEqual(2, assignedInterceptors.Count, "Two interceptors should be assigned");
Assert.AreEqual(2, assignedThreats.Count, "Both threats should be assigned");
// Verify that threats are assigned in order of their threat level (based on velocity and distance)
var orderedAssignments = assignments.OrderByDescending(a => a.Threat.GetVelocity().magnitude / Vector3.Distance(a.Threat.transform.position, Vector3.zero)).ToList();
Assert.AreEqual(threat2, orderedAssignments[0].Threat, "Higher threat should be assigned first");
Assert.AreEqual(threat1, orderedAssignments[1].Threat, "Lower threat should be assigned second");
}
}

View File

@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 7ddf271569a78ee4e995192d4df0ef3f

View File

@ -1,24 +0,0 @@
{
"name": "bamlab.test.editmode",
"rootNamespace": "",
"references": [
"UnityEngine.TestRunner",
"UnityEditor.TestRunner",
"bamlab.micromissiles"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": true,
"precompiledReferences": [
"nunit.framework.dll"
],
"autoReferenced": false,
"defineConstraints": [
"UNITY_INCLUDE_TESTS"
],
"versionDefines": [],
"noEngineReferences": false
}

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: fb45bee6d70202941a0bab2fe23f0ffb
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,25 +0,0 @@
using System.Collections;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
public class SanityTest
{
[UnityTest]
public IEnumerator SanityCheck()
{
// Arrange
GameObject testObject = new GameObject("TestObject");
// Act
testObject.AddComponent<BoxCollider>();
// Assert
Assert.IsTrue(testObject.GetComponent<BoxCollider>() != null, "BoxCollider should be added to the test object");
// Clean up
Object.Destroy(testObject);
yield return null;
}
}

View File

@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 2e04183212e40bf43a2625427494a839

View File

@ -1,22 +0,0 @@
{
"name": "bamlab.test.playmode",
"rootNamespace": "",
"references": [
"UnityEngine.TestRunner",
"UnityEditor.TestRunner",
"bamlab.micromissiles"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": true,
"precompiledReferences": [
"nunit.framework.dll"
],
"autoReferenced": false,
"defineConstraints": [
"UNITY_INCLUDE_TESTS"
],
"versionDefines": [],
"noEngineReferences": false
}

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: e06647a307e3eb742b52b0482094df98
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -55,18 +55,7 @@ MonoBehaviour:
- rid: 162646946419048467
- rid: 162646946419048468
m_RuntimeSettings:
m_List:
- rid: 162646946419048448
- rid: 162646946419048450
- rid: 162646946419048451
- rid: 162646946419048453
- rid: 162646946419048455
- rid: 162646946419048457
- rid: 162646946419048458
- rid: 162646946419048460
- rid: 162646946419048461
- rid: 162646946419048465
- rid: 162646946419048468
m_List: []
m_AssetVersion: 8
m_ObsoleteDefaultVolumeProfile: {fileID: 0}
m_RenderingLayerNames:

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8accc326a00806044a0099b7d98eb9cc
guid: f034d27b4aab67a47865af3d624c4375
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@ -1,26 +1,5 @@
{
"m_Dictionary": {
"m_DictionaryValues": [
{
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
"key": "IncludeAssemblies",
"value": "{\"m_Value\":\"bamlab.micromissiles,bamlab.test\"}"
},
{
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
"key": "Path",
"value": "{\"m_Value\":\"{ProjectPath}\"}"
},
{
"type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
"key": "HistoryPath",
"value": "{\"m_Value\":\"{ProjectPath}\"}"
},
{
"type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
"key": "EnableCodeCoverage",
"value": "{\"m_Value\":true}"
}
]
"m_DictionaryValues": []
}
}

View File

@ -61,11 +61,6 @@
"type": "UnityEngine.PhysicMaterial",
"defaultInstantiationMode": 0
},
{
"userAdded": false,
"type": "UnityEngine.PhysicsMaterial",
"defaultInstantiationMode": 0
},
{
"userAdded": false,
"type": "UnityEngine.PhysicsMaterial2D",

101
README.md
View File

@ -2,15 +2,6 @@
![Sim Salvo Animation](docs/images/sim_salvo_animation.gif)
[![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/PisterLab/micromissiles-unity/build.yaml?link=https%3A%2F%2Fgithub.com%2FPisterLab%2Fmicromissiles-unity%2Factions%2Fworkflows%2Fbuild.yaml)](https://github.com/PisterLab/micromissiles-unity/actions/workflows/build.yaml)
[![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/PisterLab/micromissiles-unity/test.yaml?label=tests&link=https%3A%2F%2Fgithub.com%2FPisterLab%2Fmicromissiles-unity%2Factions%2Fworkflows%2Ftest.yaml)](https://github.com/PisterLab/micromissiles-unity/actions/workflows/test.yaml)
[![GitHub Release](https://img.shields.io/github/v/release/PisterLab/micromissiles-unity?link=https%3A%2F%2Fgithub.com%2FPisterLab%2Fmicromissiles-unity%2Freleases%2Flatest)](https://github.com/PisterLab/micromissiles-unity/releases/latest)
[![Static Badge](https://img.shields.io/badge/%F0%9F%93%93-Documentation-blue?labelColor=white)](https://pisterlab.github.io/micromissiles-unity/)
# Documentation
Documentation is hosted on the [micromissiles-unity GitHub Pages site](https://pisterlab.github.io/micromissiles-unity/).
# Quick Start
We generate pre-built standalone binaries for Windows and Mac users from the `release` branch. These binaries are intended for non-development users who just want to run the application and modify a few configurations along the way.
@ -33,9 +24,91 @@ You can find the latest release [here](https://github.com/PisterLab/micromissile
* Navigate to `Privacy & Security`.
* Click on `Open Anyway` to bypass Apple's developer check.
# Next Steps
# Development
- To get started with development, see the [**Development Guide**](https://pisterlab.github.io/micromissiles-unity/Development_Guide.html).
- Familiarize yourself with the [**Keybinds and Controls**](https://pisterlab.github.io/micromissiles-unity/Keybinds_and_Controls.html) to navigate and interact with the simulation.
- Learn how to configure the simulation settings by reading the [**Simulation Configuration Guide**](https://pisterlab.github.io/micromissiles-unity/Simulation_Config_Guide.html).
- Learn how to analyze the simulation logs by reading the [**Simulation Logging Guide**](https://pisterlab.github.io/micromissiles-unity/Simulation_Logging.html).
This guide will help you set up and run the project in development mode. You'll learn how to install Unity Hub, open the project, and navigate the main scene.
## Table of Contents
- [Prerequisites](#prerequisites)
- [Installation Steps](#installation-steps)
- [1. Install Unity Hub](#1-install-unity-hub)
- [2. Clone the Project Repository](#2-clone-the-project-repository)
- [3. Launch the Project via Unity Hub](#3-launch-the-project-via-unity-hub)
- [4. Open the Main Scene](#4-open-the-main-scene)
- [Next Steps](#next-steps)
- [Additional Resources](#additional-resources)
## Prerequisites
- A computer with internet access.
- Administrative privileges to install software.
- [Git](https://git-scm.com/downloads) installed on your system (optional, for cloning the repository).
## Installation Steps
### 1. Install Unity Hub
Unity Hub is a desktop application that manages your Unity projects and installations. It simplifies the process of installing different Unity versions and launching projects.
**Steps to Install Unity Hub:**
1. Visit the [Unity Download Page](https://unity3d.com/get-unity/download).
2. Click on **"Download Unity Hub"**.
3. Run the downloaded installer and follow the on-screen instructions to complete the installation.
### 2. Clone the Project Repository
Obtain the project source code by cloning the repository from GitHub.
```bash
git clone https://github.com/PisterLab/micromissiles-unity.git
```
Alternatively, you can download the repository as a ZIP file and extract it to a preferred location.
### 3. Launch the Project via Unity Hub
![Unity Hub](docs/images/unity_hub.png)
1. **Open Unity Hub**.
2. **Add the Project to Unity Hub**:
- Navigate to the **"Projects"** tab.
- Click on the **"ADD"** button.
- Browse to the folder where you cloned or extracted the project.
- Select the folder containing the `Assets` folder and click on **"Select Folder"**.
3. **Install the Required Unity Version**:
- Unity Hub will detect if the project requires a Unity version that is not currently installed.
- A notification or warning icon may appear next to the project name.
- Click on the notification and select **"Install Unity **[version]**"**.
- Unity Hub will download and install the required Unity version automatically.
4. **Open the Project**:
- Once the required Unity version is installed, click on the project name in Unity Hub to open it.
### 4. Open the Main Scene
After the project opens in Unity:
1. In the **Project** window (usually located at the bottom), navigate to:
```
Assets/Scenes/
```
2. Find the main scene file, usually named `MainScene.unity`.
3. Double-click on `MainScene.unity` to open it.
4. The scene will load in the **Scene** view. You can now run the simulation by clicking the **Play** button at the top of the Unity Editor.
## Next Steps
- Familiarize yourself with the [**Keybinds and Controls**](docs/Keybinds_and_Controls.md) to navigate and interact with the simulation.
- Learn how to configure the simulation settings by reading the [**Simulation Configuration Guide**](docs/Simulation_Config_Guide.md).
## Additional Resources
- [Keybinds and Controls](docs/Keybinds_and_Controls.md)
- [Simulation Configuration Guide](docs/Simulation_Config_Guide.md)

View File

@ -0,0 +1,82 @@
import os
import glob
import argparse
import pandas as pd
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
def find_latest_telemetry_file(directory='./Logs/'):
list_of_files = glob.glob(os.path.join(directory, 'sim_telemetry_*.csv'))
if not list_of_files:
print(f"No telemetry files found in {directory}")
return None
latest_file = max(list_of_files, key=os.path.getctime)
print(f"Using latest telemetry file: {latest_file}")
return latest_file
def plot_telemetry(file_path):
# Read the telemetry CSV file
df = pd.read_csv(file_path)
# Create a 3D plot
fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot(111, projection='3d')
# Define colors for different agent types
colors = {'T': 'red', 'M': 'blue'}
# Group data by AgentID
for agent_id, agent_data in df.groupby('AgentID'):
agent_type = agent_data['AgentType'].iloc[0]
color = colors.get(agent_type, 'black')
downsampled = agent_data.iloc[::10]
ax.plot(
downsampled['AgentX'],
downsampled['AgentZ'],
downsampled['AgentY'],
color=color,
alpha=0.5,
linewidth=0.5,
label=f"{agent_type}"
)
ax.set_xlabel('X (m)')
ax.set_ylabel('Z (m)')
ax.set_zlabel('Y (m)')
ax.view_init(elev=20, azim=45)
# Add a ground plane
x_min, x_max = ax.get_xlim()
z_min, z_max = ax.get_ylim()
xx, zz = np.meshgrid(np.linspace(x_min, x_max, 2), np.linspace(z_min, z_max, 2))
yy = np.zeros_like(xx)
ax.plot_surface(xx, zz, yy, alpha=0.2, color='green')
plt.title('Agents Trajectories (X: Right, Z: Forward, Y: Up)')
legend = [
plt.Line2D([0], [0], color='red', lw=2, label='Threat'),
plt.Line2D([0], [0], color='blue', lw=2, label='Interceptor')
]
plt.legend(handles=legend)
plt.tight_layout()
plt.show()
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Visualize telemetry data.')
parser.add_argument('file', nargs='?', default=None, help='Path to telemetry CSV file.')
args = parser.parse_args()
if args.file:
file_path = args.file
else:
file_path = find_latest_telemetry_file()
if file_path is None:
exit(1)
plot_telemetry(file_path)

View File

@ -1,198 +0,0 @@
import os
import glob
import argparse
import platform
import pandas as pd
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
def get_logs_directory():
if platform.system() == "Windows":
return os.path.expandvars(r"%USERPROFILE%\AppData\LocalLow\BAMLAB\micromissiles\Telemetry\Logs")
elif platform.system() == "Darwin": # macOS
return os.path.expanduser("~/Library/Application Support/BAMLAB/micromissiles/Telemetry/Logs")
else:
raise NotImplementedError(f"Unsupported platform: {platform.system()}")
def find_latest_file(directory, file_pattern):
list_of_files = glob.glob(os.path.join(directory, file_pattern))
if not list_of_files:
print(f"No files matching '{file_pattern}' found in {directory}")
return None
latest_file = max(list_of_files, key=os.path.getctime)
print(f"Using latest file: {latest_file}")
return latest_file
def find_latest_telemetry_file():
logs_dir = get_logs_directory()
latest_log_dir = max(glob.glob(os.path.join(logs_dir, "*")), key=os.path.getctime)
return find_latest_file(latest_log_dir, 'sim_telemetry_*.csv')
def find_latest_event_log():
latest_telemetry_file = find_latest_telemetry_file()
if latest_telemetry_file:
return latest_telemetry_file.replace('sim_telemetry_', 'sim_events_')
else:
return None
def plot_telemetry(telemetry_file_path, event_file_path):
# Read the telemetry CSV file
df = pd.read_csv(telemetry_file_path)
# Read the event CSV file
event_df = pd.read_csv(event_file_path)
# Sanitize the 'Event' column to ensure consistency
event_df['Event'] = event_df['Event'].str.upper().str.strip()
# Debugging: Print unique event types to verify correct parsing
unique_events = event_df['Event'].unique()
print(f"Unique Events Found: {unique_events}")
# Create a 3D plot
fig = plt.figure(figsize=(14, 10))
ax = fig.add_subplot(111, projection='3d')
# Define colors for different agent types
colors = {'T': 'red', 'M': 'blue'}
# Group data by AgentID
agent_types = set()
for agent_id, agent_data in df.groupby('AgentID'):
agent_type = agent_data['AgentType'].iloc[0]
color = colors.get(agent_type, 'black')
downsampled = agent_data.iloc[::10]
ax.plot(
downsampled['AgentX'],
downsampled['AgentZ'],
downsampled['AgentY'],
color=color,
alpha=0.5,
linewidth=0.5,
label=f"Agent Type: {agent_type}" # Optional: More descriptive labels
)
agent_types.add(agent_type)
# Define event markers with higher zorder for visibility
event_markers = {
'HIT': ('o', 'green', 'Hit'),
'MISS': ('x', 'red', 'Miss'),
'NEW_THREAT': ('^', 'orange', 'New Threat'),
'NEW_INTERCEPTOR': ('s', 'blue', 'New Interceptor')
}
# Plot events
for event_type, (marker, color, label) in event_markers.items():
event_data = event_df[event_df['Event'] == event_type]
if not event_data.empty:
ax.scatter(
event_data['PositionX'],
event_data['PositionZ'],
event_data['PositionY'],
c=color,
marker=marker,
s=100, # Increased marker size for better visibility
label=label,
edgecolors='k', # Adding black edges for contrast
depthshade=True,
zorder=5 # Ensure markers are on top
)
# Set labels
ax.set_xlabel('X (m)', fontsize=12)
ax.set_ylabel('Z (m)', fontsize=12)
ax.set_zlabel('Y (m)', fontsize=12)
# Set view angle for better visualization
ax.view_init(elev=20, azim=45)
# Add a ground plane for reference
x_min, x_max = ax.get_xlim()
z_min, z_max = ax.get_ylim()
xx, zz = np.meshgrid(np.linspace(x_min, x_max, 2), np.linspace(z_min, z_max, 2))
yy = np.zeros_like(xx)
ax.plot_surface(xx, zz, yy, alpha=0.2, color='green')
plt.title('Agents Trajectories and Events (X: Right, Z: Forward, Y: Up)', fontsize=14)
# Optimize legend to prevent overcrowding
handles, labels = ax.get_legend_handles_labels()
# Remove duplicate labels
unique = dict(zip(labels, handles))
ax.legend(unique.values(), unique.keys(), loc='upper left', bbox_to_anchor=(1, 1), fontsize=10)
plt.tight_layout()
plt.show()
def print_summary(telemetry_file_path, event_file_path):
# Read the telemetry CSV file
df = pd.read_csv(telemetry_file_path)
# Read the event CSV file
event_df = pd.read_csv(event_file_path)
# Sanitize the 'Event' column to ensure consistency
event_df['Event'] = event_df['Event'].str.upper().str.strip()
# Print total number of events
total_events = len(event_df)
print(f"Total number of events: {total_events}")
# Print counts of each event type
event_counts = event_df['Event'].value_counts()
print("\nEvent Counts:")
for event_type, count in event_counts.items():
print(f" {event_type}: {count}")
# Calculate the time duration of the events
if 'Time' in event_df.columns:
start_time = event_df['Time'].min()
end_time = event_df['Time'].max()
duration = end_time - start_time
print(f"\nTotal duration of events: {duration:.2f} seconds (from {start_time:.2f} to {end_time:.2f})")
else:
print("\n'Time' column not found in event data.")
if 'Time' in event_df.columns:
hits = event_df[event_df['Event'] == 'HIT']
misses = event_df[event_df['Event'] == 'MISS']
if not hits.empty:
first_hit_time = hits['Time'].min()
last_hit_time = hits['Time'].max()
print(f"\nFirst hit at {first_hit_time:.2f} seconds, last hit at {last_hit_time:.2f} seconds")
else:
print("\nNo hits recorded.")
if not misses.empty:
first_miss_time = misses['Time'].min()
last_miss_time = misses['Time'].max()
print(f"First miss at {first_miss_time:.2f} seconds, last miss at {last_miss_time:.2f} seconds")
else:
print("No misses recorded.")
else:
print("\n'Time' column not found in event data.")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Visualize telemetry data and events.')
parser.add_argument('telemetry_file', nargs='?', default=None, help='Path to telemetry CSV file.')
parser.add_argument('event_file', nargs='?', default=None, help='Path to event CSV file.')
args = parser.parse_args()
if args.telemetry_file and args.event_file:
telemetry_file_path = args.telemetry_file
event_file_path = args.event_file
else:
telemetry_file_path = find_latest_telemetry_file()
event_file_path = find_latest_event_log()
if telemetry_file_path is None or event_file_path is None:
exit(1)
print_summary(telemetry_file_path, event_file_path)
plot_telemetry(telemetry_file_path, event_file_path)

5
docs/.gitignore vendored
View File

@ -1,5 +0,0 @@
node_modules/
package-lock.json
.vuepress/dist/
.vitepress/cache/
.vitepress/dist/

View File

@ -1,40 +0,0 @@
import { defineConfig } from 'vitepress'
// https://vitepress.dev/reference/site-config
export default defineConfig({
title: "micromissiles-unity",
description: "Swarm-on-swarm simulator using micromissiles for point defense",
base: '/micromissiles-unity/',
themeConfig: {
// https://vitepress.dev/reference/default-theme-config
nav: [
{ text: 'Home', link: '/' },
{ text: 'Documentation', link: '/Keybinds_and_Controls' },
{ text: 'Development Guide', link: '/Development_Guide' }
],
sidebar: [
{
text: 'Documentation',
items: [
{ text: 'Keybinds and Controls', link: '/Keybinds_and_Controls' },
{ text: 'Simulation Configuration Guide', link: '/Simulation_Config_Guide' },
{ text: 'Simulation Logging', link: '/Simulation_Logging' },
{ text: 'Coverage Reports', link: '/coverage/editmode/Report/' },
{ text: 'Development Guide', link: '/Development_Guide' }
]
}
],
socialLinks: [
{ icon: 'github', link: 'https://github.com/PisterLab/micromissiles-unity' }
],
search: {
provider: 'local'
},
footer: {
message: 'Released under the <a href="https://github.com/PisterLab/micromissiles-unity/blob/main/LICENSE">BSD-3-Clause License</a>.',
copyright: 'Copyright © 2024-present, The Regents of the University of California (Regents). All Rights Reserved.'
}
}
})

View File

@ -1,84 +0,0 @@
# Development Guide
This guide will help you set up and run the project in development mode. You'll learn how to install Unity Hub, open the project, and navigate the main scene.
## Table of Contents
- [Prerequisites](#prerequisites)
- [Installation Steps](#installation-steps)
- [1. Install Unity Hub](#1-install-unity-hub)
- [2. Clone the Project Repository](#2-clone-the-project-repository)
- [3. Launch the Project via Unity Hub](#3-launch-the-project-via-unity-hub)
- [4. Open the Main Scene](#4-open-the-main-scene)
- [Next Steps](#next-steps)
- [Additional Resources](#additional-resources)
## Prerequisites
- A computer with internet access.
- Administrative privileges to install software.
- [Git](https://git-scm.com/downloads) installed on your system (optional, for cloning the repository).
## Installation Steps
### 1. Install Unity Hub
Unity Hub is a desktop application that manages your Unity projects and installations. It simplifies the process of installing different Unity versions and launching projects.
**Steps to Install Unity Hub:**
1. Visit the [Unity Download Page](https://unity3d.com/get-unity/download).
2. Click on **"Download Unity Hub"**.
3. Run the downloaded installer and follow the on-screen instructions to complete the installation.
### 2. Clone the Project Repository
Obtain the project source code by cloning the repository from GitHub.
```bash
git clone https://github.com/PisterLab/micromissiles-unity.git
```
Alternatively, you can download the repository as a ZIP file and extract it to a preferred location.
### 3. Launch the Project via Unity Hub
![Unity Hub](./images/unity_hub.png)
1. **Open Unity Hub**.
2. **Add the Project to Unity Hub**:
- Navigate to the **"Projects"** tab.
- Click on the **"ADD"** button.
- Browse to the folder where you cloned or extracted the project.
- Select the folder containing the `Assets` folder and click on **"Select Folder"**.
3. **Install the Required Unity Version**:
- Unity Hub will detect if the project requires a Unity version that is not currently installed.
- A notification or warning icon may appear next to the project name.
- Click on the notification and select **"Install Unity **[version]**"**.
- Unity Hub will download and install the required Unity version automatically.
4. **Open the Project**:
- Once the required Unity version is installed, click on the project name in Unity Hub to open it.
### 4. Open the Main Scene
After the project opens in Unity:
1. In the **Project** window (usually located at the bottom), navigate to:
```
Assets/Scenes/
```
2. Find the main scene file, usually named `MainScene.unity`.
3. Double-click on `MainScene.unity` to open it.
4. The scene will load in the **Scene** view. You can now run the simulation by clicking the **Play** button at the top of the Unity Editor.
## Additional Resources
- [Keybinds and Controls](Keybinds_and_Controls.md)
- [Simulation Configuration Guide](Simulation_Config_Guide.md)

View File

@ -1,85 +0,0 @@
# Markdown Extension Examples
This page demonstrates some of the built-in markdown extensions provided by VitePress.
## Syntax Highlighting
VitePress provides Syntax Highlighting powered by [Shiki](https://github.com/shikijs/shiki), with additional features like line-highlighting:
**Input**
````md
```js{4}
export default {
data () {
return {
msg: 'Highlighted!'
}
}
}
```
````
**Output**
```js{4}
export default {
data () {
return {
msg: 'Highlighted!'
}
}
}
```
## Custom Containers
**Input**
```md
::: info
This is an info box.
:::
::: tip
This is a tip.
:::
::: warning
This is a warning.
:::
::: danger
This is a dangerous warning.
:::
::: details
This is a details block.
:::
```
**Output**
::: info
This is an info box.
:::
::: tip
This is a tip.
:::
::: warning
This is a warning.
:::
::: danger
This is a dangerous warning.
:::
::: details
This is a details block.
:::
## More
Check out the documentation for the [full list of markdown extensions](https://vitepress.dev/guide/markdown).

View File

@ -9,13 +9,13 @@ The main configuration files you will work with are located in the `Assets/Strea
- **Simulation Configurations**:
- **`1_salvo_1_hydra_7_drones.json`**: A simple, barebones example of a simulation configuration featuring a single salvo in a 7-on-7 scenario.
- **`3_salvo_10_hydra_200_drones.json`**: A more complex example with three salvos, illustrating a 210-on-200 scenario. This demonstrates how to set up multiple salvos within the simulation.
- **C# Script**: [`SimulationConfig.cs`](https://github.com/PisterLab/micromissiles-unity/blob/master/Assets/Scripts/Config/StaticConfig.cs)
- **C# Script**: [`SimulationConfig.cs`](Assets/Scripts/Config/SimulationConfig.cs)
- **Model Configurations** (found in `Assets/StreamingAssets/Configs/Models/`):
- **`micromissile.json`**
- **`hydra70.json`**
- **`drone.json`**
- **C# Script**: [`StaticConfig.cs`](https://github.com/PisterLab/micromissiles-unity/blob/master/Assets/Scripts/Config/StaticConfig.cs)
- **C# Script**: [`StaticConfig.cs`](Assets/Scripts/Config/StaticConfig.cs)
### File Locations
@ -37,7 +37,7 @@ The simulation configurations are defined in JSON files that specify the initial
This is a basic configuration featuring a single salvo with one interceptor type (`HYDRA_70`) and seven threat drones.
```json
```json:Assets/StreamingAssets/Configs/1_salvo_1_hydra_7_drones.json
{
"timeScale": 1,
"interceptor_swarm_configs": [
@ -89,7 +89,7 @@ This is a basic configuration featuring a single salvo with one interceptor type
This configuration demonstrates a more complex scenario with three salvos, each launching ten `HYDRA_70` missiles at different times against 200 threat drones. This results in a total of 210 missiles (including submunitions) engaging 200 targets.
```json
```json:Assets/StreamingAssets/Configs/3_salvo_10_hydra_200_drones.json
{
"timeScale": 1,
"interceptor_swarm_configs": [
@ -265,7 +265,7 @@ To define a new inte or threat model:
This script defines the data structures used to interpret the JSON simulation configuration files.
[Assets/Scripts/Config/SimulationConfig.cs](https://github.com/PisterLab/micromissiles-unity/blob/master/Assets/Scripts/Config/SimulationConfig.cs)
[Assets/Scripts/Config/SimulationConfig.cs](../../Assets/Scripts/Config/SimulationConfig.cs)
**Classes**:
@ -281,10 +281,10 @@ This script defines the data structures used to interpret the JSON simulation co
This script defines the classes corresponding to the model configuration JSON structure.
[Assets/Scripts/Config/StaticConfig.cs](https://github.com/PisterLab/micromissiles-unity/blob/master/Assets/Scripts/Config/StaticConfig.cs)
[Assets/Scripts/Config/StaticConfig.cs](../../Assets/Scripts/Config/StaticConfig.cs)
For example:
```csharp
```csharp:Assets/Scripts/Config/StaticConfig.cs
[Serializable]
public class StaticConfig {
[Serializable]
@ -335,8 +335,8 @@ While the simulation is running, you can load a new Simulation Configuration JSO
For further assistance, refer to the comments and documentation within the code files:
- [`SimManager.cs`](https://github.com/PisterLab/micromissiles-unity/blob/master/Assets/Scripts/SimManager.cs): Manages simulation state and agent creation.
- [`InputManager.cs`](https://github.com/PisterLab/micromissiles-unity/blob/master/Assets/Scripts/Managers/InputManager.cs): Handles user input and interactions.
- [`SimManager.cs`](Assets/Scripts/SimManager.cs): Manages simulation state and agent creation.
- [`InputManager.cs`](Assets/Scripts/Managers/InputManager.cs): Handles user input and interactions.
---

View File

@ -1,259 +1,12 @@
# Simulation Logging
This guide provides instructions on how to access and interpret the simulation logs, how they are structured by the `SimMonitor` class, and how to utilize the provided `visualize_log.py` script to analyze simulation data. Additionally, it offers guidance on creating your own scripts for custom analysis.
## Overview
![Python simulation log visualizer](./images/sim_visualizer.png)
Simulation logs capture detailed telemetry and event data from each simulation run. These logs are essential for debugging, performance analysis, and understanding the behavior of agents within the simulation.
Logs are exported to the `Telemetry/Logs` folder in your operating system's [persistent data path](https://docs.unity3d.com/ScriptReference/Application-persistentDataPath.html).
For example, on Windows, the logs are exported to:
For example, on Windows, the logs will be exported to
`C:\Users\<user>\AppData\LocalLow\BAMLAB\micromissiles\Telemetry`.
```
C:\Users\<user>\AppData\LocalLow\BAMLAB\micromissiles\Telemetry\Logs
```
On MacOS, the logs will be exported to
`~/Library/Application Support/BAMLAB/micromissiles/Telemetry`.
On macOS, the logs are exported to:
```
~/Library/Application Support/BAMLAB/micromissiles/Telemetry/Logs
```
`visualize_log.py` is an example script provided to help visualize and interpret the simulation logs. It is included in the `Tools` directory of the release download.
## Understanding Log Files and Directory Structure
### Log Directory Structure
Simulation logs are organized into timestamped directories within the `Logs` folder. Each simulation run generates a new directory named with the timestamp of the run.
For example:
```
Telemetry/
└── Logs/
├── 20241002_101305/
│ ├── sim_telemetry_20241002_101311.csv
│ ├── sim_events_20241002_101311.csv
│ │
│ ├── sim_telemetry_20241002_101306.csv
│ └── sim_events_20241002_101306.csv
├── 20241002_012122/
│ ├── sim_telemetry_20241002_012122.csv
│ └── sim_events_20241002_012122.csv
└── ...
```
Each simulation run produces two main CSV files:
- **Telemetry Log (`sim_telemetry_*.csv`)**: Contains detailed state information for each agent at each time step.
- **Event Log (`sim_events_*.csv`)**: Records significant events such as hits, misses, agent creation, and destruction.
### Log Files Generated by `SimMonitor`
The logging system is managed by the `SimMonitor` class in the simulation codebase.
```csharp
public class SimMonitor : MonoBehaviour
{
// Responsible for logging simulation data
// ...
}
```
**Key Responsibilities of `SimMonitor`:**
- Collecting agent state data at each simulation step.
- Writing telemetry data to `sim_telemetry_*.csv`.
- Recording significant events to `sim_events_*.csv`.
- Organizing logs into timestamped directories for each simulation run.
### Telemetry Log Structure
The telemetry log provides a snapshot of the simulation at each time step for every agent. Key columns include:
- **`Time`**: Simulation time at the log entry.
- **`AgentID`**: Unique identifier for each agent.
- **`AgentType`**: Type of the agent (e.g., interceptor, threat).
- **`AgentX`**, **`AgentY`**, **`AgentZ`**: Position coordinates of the agent.
- **`AgentVelocityX`**, **`AgentVelocityY`**, **`AgentVelocityZ`**: Velocity components.
- **`AgentStatus`**: Current status of the agent (e.g., active, destroyed).
### Event Log Structure
The event log records significant occurrences within the simulation. Key columns include:
- **`Time`**: Time when the event occurred.
- **`PositionX`**, **`PositionY`**, **`PositionZ`**: Position where the event occurred.
- **`EventType`**: Type of event (e.g., `HIT`, `MISS`, `NEW_THREAT`, `NEW_INTERCEPTOR`).
- **`Details`**: Additional details about the event.
## Running the `visualize_log.py` Script
The `visualize_log.py` script helps visualize agent trajectories and significant events in a 3D plot.
### Locating the Script
After downloading and extracting the release package, you can find the script at:
```
Tools/visualize_log.py
```
Make sure you have Python 3 installed on your system, along with the required libraries to run the script.
### Required Python Libraries
The script depends on the following Python libraries:
- **`pandas`**
- **`matplotlib`**
- **`numpy`**
You can install them using `pip`:
```bash
pip install pandas matplotlib numpy
```
### Usage
#### Navigate to the Tools Directory
Open a terminal or command prompt and navigate to the `Tools` directory:
```bash
cd path/to/Tools/
```
#### Run the Script
To visualize the most recent simulation logs:
```bash
python visualize_log.py
```
**What the Script Does:**
- **Automatically Finds the Latest Logs**: If no arguments are provided, it locates the most recent `sim_telemetry_*.csv` and `sim_events_*.csv` files.
- **Prints a Summary**: Outputs a summary of events, including total counts and timing of hits and misses.
- **Generates a 3D Plot**: Displays agent trajectories and marks events such as hits and misses.
#### Specifying Log Files Manually
You can also provide specific file paths as arguments:
```bash
python visualize_log.py path/to/sim_telemetry_file.csv path/to/sim_events_file.csv
```
### Example Output
```
Total number of events: 150
Event Counts:
HIT: 120
MISS: 30
First hit at 5.00 seconds, last hit at 15.00 seconds
[3D plot window opens showing trajectories and events]
```
### Interpreting the Plot
The 3D plot displays:
- **Agent Trajectories**: Lines representing the paths of interceptors and threats.
- **Colors** indicate agent types (e.g., blue for interceptors, red for threats).
- **Event Markers**: Symbols marking where events occurred.
- **Hits**: Marked with green circles.
- **Misses**: Marked with red crosses.
### Adjusting the Visualization
- **View Angle**: You can rotate the 3D plot by clicking and dragging to view the simulation from different perspectives.
- **Zoom**: Use the scroll wheel to zoom in and out.
## Writing Your Own Scripts
The simulation logs are in CSV format, making them accessible for custom analysis and visualization.
### Getting Started
- **Choose a Programming Language**: Python or MATLAB are recommended for ease-of-use and data analysis capabilities.
For example, using Python and the `pandas` library, you can load the telemetry and event data like this:
```python
import pandas as pd
telemetry_df = pd.read_csv('path/to/sim_telemetry_*.csv')
event_df = pd.read_csv('path/to/sim_events_*.csv')
```
### Visualization
- **2D Plots**: Use `matplotlib` to create time-series plots:
```python
import matplotlib.pyplot as plt
plt.plot(telemetry_df['Time'], telemetry_df['AgentY'])
plt.xlabel('Time (s)')
plt.ylabel('Altitude (m)')
plt.title('Agent Altitude Over Time')
plt.show()
```
- **3D Plots**: Use `mpl_toolkits.mplot3d` for 3D trajectory plots.
### Sample Script: Calculating Miss Distances
```python
import pandas as pd
import numpy as np
# Load telemetry and event data
telemetry_df = pd.read_csv('path/to/sim_telemetry_*.csv')
event_df = pd.read_csv('path/to/sim_events_*.csv')
# Filter miss events
miss_events = event_df[event_df['Event'] == 'MISS']
# Calculate miss distances
miss_distances = []
for idx, miss in miss_events.iterrows():
agent_id = miss['AgentID']
time = miss['Time']
# Get agent position at the time of miss
agent_state = telemetry_df[(telemetry_df['AgentID'] == agent_id) & (telemetry_df['Time'] == time)]
if not agent_state.empty:
x = agent_state['AgentX'].values[0]
y = agent_state['AgentY'].values[0]
z = agent_state['AgentZ'].values[0]
# Calculate distance to target or predefined point
distance = np.sqrt(x**2 + y**2 + z**2)
miss_distances.append(distance)
# Output average miss distance
average_miss_distance = np.mean(miss_distances)
print(f'Average Miss Distance: {average_miss_distance:.2f} meters')
```
### Suggestions for Analysis
- **Performance Metrics**: Determine interception success rates, average time to intercept, or hit accuracy.
- **Behavioral Analysis**: Examine how changes in simulation configurations affect agent behavior.
- **Batch Processing**: Automate analysis over multiple simulation runs to compare different scenarios.
## Additional Resources
- **Python Documentation**: [pandas](https://pandas.pydata.org/), [matplotlib](https://matplotlib.org/), [NumPy](https://numpy.org/)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 254 KiB

View File

@ -1,41 +0,0 @@
---
# https://vitepress.dev/reference/default-theme-home-page
layout: home
hero:
name: "micromissiles-unity"
tagline: "Swarm-on-swarm simulator using micromissiles for point defense"
actions:
- theme: brand
text: Documentation
link: /Keybinds_and_Controls
- theme: alt
text: Development Guide
link: /Development_Guide
---
# Quick Start
We generate pre-built standalone binaries for Windows and Mac users from the `release` branch. These binaries are intended for non-development users who just want to run the application and modify a few configurations along the way.
You can find the latest release [here](https://github.com/PisterLab/micromissiles-unity/releases/latest).
## Windows
1. Download the zip file for Windows: `micromissiles-<version>-windows_x86_64.zip`.
2. Unzip the zip file. The zip file should contain a single directory called `micromissiles-<version>-windows_x86_64`.
3. In the `micromissiles-<version>-windows_x86_64` directory, run `micromissiles-<version>-StandaloneWindows64.exe`.
## Mac
1. Download the tarball file for Darwin: `micromissiles-<version>-darwin.tar.gz`.
2. Untar the tarball. The tarball should contain a single directory called `micromissiles-<version>-darwin`.
3. In the `micromissiles-<version>-darwin` directory, run the app file.
# Next Steps
- Familiarize yourself with the [**Keybinds and Controls**](Keybinds_and_Controls.md) to navigate and interact with the simulation.
- Learn how to configure the simulation settings by reading the [**Simulation Configuration Guide**](Simulation_Config_Guide.md).

2346
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +0,0 @@
{
"scripts": {
"docs:dev": "vitepress dev docs",
"docs:build": "vitepress build docs",
"docs:preview": "vitepress preview docs"
},
"dependencies": {
"vue": "^3.x.x",
"vitepress": "^1.3.4"
}
}