Compare commits

..

51 Commits

Author SHA1 Message Date
Daniel Lovell 54c7e5fbc7 Fix readme, add footer 2024-10-02 13:43:04 -07:00
Daniel Lovell 256f845c18 Fix dead links 2024-10-02 13:39:13 -07:00
Daniel Lovell ae5798081a Fix dead links, improve README 2024-10-02 13:36:19 -07:00
Daniel Lovell eeb82a9539 Don't use public/ directory for images 2024-10-02 13:30:37 -07:00
Daniel Lovell 1cec2fab7d Fix image link in Dev Guide 2024-10-02 13:18:07 -07:00
Daniel Lovell ecfdb490ae Add Simulation_Logging guide, reorg docs for dev guide 2024-10-02 13:12:56 -07:00
Daniel Lovell 45cdf740b1 build.yaml sudo mkdir and build 2024-10-02 12:26:43 -07:00
Daniel Lovell 090c3168ec test.yaml keep files and force orphans 2024-10-02 02:32:47 -07:00
Daniel Lovell 4768cffe8a Rename d -> index.md 2024-10-02 02:28:46 -07:00
Daniel Lovell 9c5dfe9b07 Rename INDEX -> d 2024-10-02 02:28:31 -07:00
Daniel Lovell f13103a9e5 Add SanityTest to bamlab.test.playmode 2024-10-02 02:23:30 -07:00
Daniel Lovell 4b72ed9c27 Separate playmode/editmode test assemblies 2024-10-02 02:16:10 -07:00
Daniel Lovell 6c88a881f2 Edit test directories 2024-10-02 02:08:33 -07:00
Daniel Lovell 3e59ede2ed Fix base for pages 2024-10-02 01:48:33 -07:00
Daniel Lovell aba01e9a4e Keep files and dont force orphan 2024-10-02 01:41:40 -07:00
Daniel Lovell 05ef40aa94 Force fresh docs page 2024-10-02 01:41:09 -07:00
Daniel Lovell 4a79b77b41 Overhaul vitepress site 2024-10-02 01:36:42 -07:00
Daniel Lovell 494cb8591f Fix bad image link in README.md 2024-10-02 00:52:03 -07:00
Daniel Lovell dc811e0185 Fix VitePress site hierarchy 2024-10-02 00:51:39 -07:00
Daniel Lovell 78b349610f build.yaml copy Tools/ dir into ZIP/TAR, schedule nightly 2024-10-02 00:51:24 -07:00
Daniel Lovell 6ada38fb73 Update image locations 2024-10-02 00:51:06 -07:00
Daniel Lovell 269fc479cc Move python scripts to Tools/ directory 2024-10-02 00:50:46 -07:00
Daniel Lovell 99d617f142 Python parse/visualize script uses event system
visualize_log.py should auto-locate log files on both Windows and Mac systems
2024-10-02 00:31:29 -07:00
Daniel Lovell 05400f318a New Event monitoring and telemetry optimizations 2024-10-02 00:30:21 -07:00
Daniel Lovell 17987c2c5f Implement Event monitoring system
Events are tracked and exported
Monitoring of telemetry performance greatly optimized
Assignment and threat table tracking fixed
2024-10-02 00:30:01 -07:00
Daniel Lovell 0c7d69d632 IADS split out from SimManager
IADS handles the threattable and assigning targets
IADS now implements batched threat assignments
2024-10-01 19:10:47 -07:00
Daniel Lovell 2d97112e51 Remove debug LogWarning from SimManager 2024-09-30 11:47:09 -07:00
Daniel Lovell 64b542a866
Fix links in README 2024-09-30 03:52:14 -07:00
Daniel Lovell 9bd3e513ca
Update README.md 2024-09-30 03:45:16 -07:00
Daniel Lovell fab0c769d1
Update README.md 2024-09-30 03:39:34 -07:00
Daniel Lovell f4faafcc24 Undo keep_files/force_orphan 2024-09-30 03:17:50 -07:00
Daniel Lovell 46fe48afdb Add workflow_dispatch trigger to test 2024-09-30 03:17:04 -07:00
Daniel Lovell 91587afce5 Remove destination_dir in docs.yaml, remove. in config.js 2024-09-30 03:05:55 -07:00
Daniel Lovell 3293d65ccb Don't keep files and do force orphan 2024-09-30 02:57:29 -07:00
Daniel Lovell f6d1935694 Attempt to fix base 2024-09-30 02:54:18 -07:00
Daniel Lovell 07838e79b4 Nightly CI tests 2024-09-30 02:47:47 -07:00
Daniel Lovell 4cf8567d91 Expand permissions of the job 2024-09-30 02:43:17 -07:00
Daniel Lovell 90cc1d29f8 Force a docs rebuild 2024-09-30 02:35:51 -07:00
Daniel Lovell 2174379fb1 Force commit docs/package-lock.json 2024-09-30 02:27:55 -07:00
Daniel Lovell 611dcb8365 Clean up config.js formatting 2024-09-30 02:26:25 -07:00
Daniel Lovell 4fb40a2bc6 Attempt to fix vuepress site 2024-09-30 02:26:02 -07:00
Daniel Lovell 4882caea45 Update config.js 2024-09-30 02:05:35 -07:00
Daniel Lovell e9d3f2b388 Attempt to decouple docs and test jobs 2024-09-30 02:05:26 -07:00
Daniel Lovell 765f3983f3 Add docs.yaml, update test.yaml, vuepress 2024-09-30 01:39:12 -07:00
Daniel Lovell 474c322b35 Add coverage upload to pages in test.yaml 2024-09-30 00:56:31 -07:00
Daniel Lovell 9a569e6415 Merge branch 'release' 2024-09-30 00:40:34 -07:00
Daniel Lovell 1dcaf2d245 Add CI tests in test.yaml, rename assemblies 2024-09-30 00:19:46 -07:00
Daniel Lovell 156960bae5 Reference latest tag in release.yaml, don't create new one 2024-09-30 00:06:56 -07:00
Daniel Lovell f701e34863 Fix missing assemblies, add ifdef for UNITY_EDITOR
Editor scripts require ifdef UNITY_EDITOR so as to not be compiled
2024-09-29 23:45:59 -07:00
Daniel Lovell 32f3b0aa7a Cannot have tags with same name as branches 2024-09-29 23:30:36 -07:00
Daniel Lovell e15b70fafc Assignment system overhaul to fix many bugs 2024-09-29 23:18:40 -07:00
74 changed files with 4365 additions and 1777 deletions

View File

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

47
.github/workflows/docs.yaml vendored Normal file
View File

@ -0,0 +1,47 @@
# .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

View File

@ -1,27 +0,0 @@
# 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,14 +18,19 @@ jobs:
ref: ${{ github.event.workflow_run.head_branch }} ref: ${{ github.event.workflow_run.head_branch }}
fetch-depth: 0 fetch-depth: 0
lfs: false 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 - name: Create release
id: create_release id: create_release
uses: actions/create-release@v1 uses: actions/create-release@v1
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
tag_name: ${{ github.event.workflow_run.head_branch }} tag_name: ${{ steps.get_latest_tag.outputs.LATEST_TAG }}
release_name: ${{ github.event.workflow_run.head_branch }} release_name: ${{ steps.get_latest_tag.outputs.LATEST_TAG }}
body_path: RELEASE.md body_path: RELEASE.md
draft: false draft: false
prerelease: false prerelease: false

69
.github/workflows/test.yaml vendored Normal file
View File

@ -0,0 +1,69 @@
# .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

4
.gitignore vendored
View File

@ -80,4 +80,6 @@ crashlytics-build.properties
.vsconfig .vsconfig
# Telemetry Logs # Telemetry Logs
Telemetry/Logs/ Telemetry/Logs/
node_modules/

View File

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

View File

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

View File

@ -1,361 +0,0 @@
%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,361 +0,0 @@
%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,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 0d798867791c1444b985d8807b144572 guid: b00bb4a2208bd164392fce8408d145e7
folderAsset: yes folderAsset: yes
DefaultImporter: DefaultImporter:
externalObjects: {} externalObjects: {}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections;
using UnityEngine; using UnityEngine;
using System.Linq;
using System.Diagnostics.Contracts;
// The assignment class is an interface for assigning a threat to each interceptor. // The assignment class is an interface for assigning a threat to each interceptor.
public interface IAssignment { public interface IAssignment {
@ -8,39 +11,30 @@ public interface IAssignment {
// The first element corresponds to the interceptor index, and the second element // The first element corresponds to the interceptor index, and the second element
// corresponds to the threat index. // corresponds to the threat index.
public struct AssignmentItem { public struct AssignmentItem {
public int InterceptorIndex; public Interceptor Interceptor;
public int ThreatIndex; public Threat Threat;
public AssignmentItem(int missileIndex, int threatIndex) { public AssignmentItem(Interceptor interceptor, Threat threat) {
InterceptorIndex = missileIndex; Interceptor = interceptor;
ThreatIndex = threatIndex; Threat = threat;
} }
} }
// A list containing the interceptor-target assignments. // A list containing the interceptor-target assignments.
// Assign a target to each interceptor that has not been assigned a target yet. // Assign a target to each interceptor that has not been assigned a target yet.
public abstract IEnumerable<AssignmentItem> Assign(List<Agent> missiles, List<Agent> targets); [Pure]
public abstract IEnumerable<AssignmentItem> Assign(in IReadOnlyList<Interceptor> interceptors, in IReadOnlyList<ThreatData> threatTable);
// Get the list of assignable interceptor indices. // Get the list of assignable interceptor indices.
protected static List<int> GetAssignableInterceptorIndices(List<Agent> missiles) { [Pure]
List<int> assignableInterceptorIndices = new List<int>(); protected static List<Interceptor> GetAssignableInterceptors(in IReadOnlyList<Interceptor> interceptors) {
for (int missileIndex = 0; missileIndex < missiles.Count; missileIndex++) { return interceptors.Where(interceptor => interceptor.IsAssignable()).ToList();
if (missiles[missileIndex].IsAssignable()) {
assignableInterceptorIndices.Add(missileIndex);
}
}
return assignableInterceptorIndices;
} }
// Get the list of active target indices. // Get the list of active threats.
protected static List<int> GetActiveThreatIndices(List<Agent> threats) { [Pure]
List<int> activeThreatIndices = new List<int>(); protected static List<ThreatData> GetActiveThreats(in IReadOnlyList<ThreatData> threats) {
for (int threatIndex = 0; threatIndex < threats.Count; threatIndex++) { return threats.Where(t => t.Status != ThreatStatus.DESTROYED).ToList();
if (!threats[threatIndex].IsHit()) {
activeThreatIndices.Add(threatIndex);
}
}
return activeThreatIndices;
} }
} }

View File

@ -1,39 +1,43 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using UnityEngine; using UnityEngine;
using System.Diagnostics.Contracts;
// The round-robin assignment class assigns missiles to the targets in a // The round-robin assignment class assigns interceptors to the targets in a
// round-robin order. // round-robin order using the new paradigm.
public class RoundRobinAssignment : IAssignment { public class RoundRobinAssignment : IAssignment {
// Previous target index that was assigned. // Previous target index that was assigned.
private int prevTargetIndex = -1; private int prevTargetIndex = -1;
// Assign a target to each interceptor that has not been assigned a target yet. // 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) { [Pure]
List<IAssignment.AssignmentItem> assignments = new List<IAssignment.AssignmentItem>(); public IEnumerable<IAssignment.AssignmentItem> Assign(in IReadOnlyList<Interceptor> interceptors, in IReadOnlyList<ThreatData> targets) {
List<int> assignableInterceptorIndices = IAssignment.GetAssignableInterceptorIndices(missiles); List<IAssignment.AssignmentItem> assignments = new List<IAssignment.AssignmentItem>();
if (assignableInterceptorIndices.Count == 0) {
return assignments; // 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;
} }
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,70 +3,64 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using Unity.VisualScripting; using Unity.VisualScripting;
using UnityEngine; using UnityEngine;
using System.Diagnostics.Contracts;
// The threat assignment class assigns missiles to the targets based // The threat assignment class assigns interceptors to the targets based
// on the threat level of the targets. // on the threat level of the targets.
public class ThreatAssignment : IAssignment { public class ThreatAssignment : IAssignment {
// Assign a target to each interceptor that has not been assigned a target yet. // 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) { [Pure]
public IEnumerable<IAssignment.AssignmentItem> Assign(in IReadOnlyList<Interceptor> interceptors, in IReadOnlyList<ThreatData> targets) {
List<IAssignment.AssignmentItem> assignments = new List<IAssignment.AssignmentItem>(); List<IAssignment.AssignmentItem> assignments = new List<IAssignment.AssignmentItem>();
List<int> assignableInterceptorIndices = IAssignment.GetAssignableInterceptorIndices(missiles); List<Interceptor> assignableInterceptors = IAssignment.GetAssignableInterceptors(interceptors);
if (assignableInterceptorIndices.Count == 0) { if (assignableInterceptors.Count == 0) {
Debug.LogWarning("No assignable interceptors found");
return assignments; return assignments;
} }
List<int> activeThreatIndices = IAssignment.GetActiveThreatIndices(targets); List<ThreatData> activeThreats = IAssignment.GetActiveThreats(targets);
if (activeThreatIndices.Count == 0) { if (activeThreats.Count == 0) {
Debug.LogWarning("No active threats found");
return assignments; return assignments;
} }
Vector3 positionToDefend = Vector3.zero; Vector3 positionToDefend = Vector3.zero;
List<ThreatInfo> threatInfos = List<ThreatInfo> threatInfos =
CalculateThreatLevels(targets, activeThreatIndices, positionToDefend); CalculateThreatLevels(activeThreats, positionToDefend);
foreach (int missileIndex in assignableInterceptorIndices) { // Sort ThreatInfo first by ThreatData.Status (UNASSIGNED first, then ASSIGNED)
if (missiles[missileIndex].HasAssignedTarget()) // Within each group, order by ThreatLevel descending
continue; threatInfos = threatInfos.OrderByDescending(t => t.ThreatData.Status == ThreatStatus.UNASSIGNED)
if (threatInfos.Count == 0) .ThenByDescending(t => t.ThreatLevel)
break; .ToList();
// Find the optimal target for this interceptor based on distance and threat var assignableInterceptorsEnumerator = assignableInterceptors.GetEnumerator();
ThreatInfo optimalTarget = null; if (assignableInterceptorsEnumerator.MoveNext()) // Move to the first element
float optimalScore = float.MinValue; {
foreach (ThreatInfo threatInfo in threatInfos) {
foreach (ThreatInfo threat in threatInfos) { assignments.Add(new IAssignment.AssignmentItem(assignableInterceptorsEnumerator.Current, threatInfo.ThreatData.Threat));
float distance = Vector3.Distance(missiles[missileIndex].transform.position, if (!assignableInterceptorsEnumerator.MoveNext()) {
targets[threat.TargetIndex].transform.position); break;
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; return assignments;
} }
private List<ThreatInfo> CalculateThreatLevels(List<Agent> targets, List<int> activeThreatIndices,
Vector3 missilesMeanPosition) { private List<ThreatInfo> CalculateThreatLevels(List<ThreatData> threatTable,
Vector3 defensePosition) {
List<ThreatInfo> threatInfos = new List<ThreatInfo>(); List<ThreatInfo> threatInfos = new List<ThreatInfo>();
foreach (int targetIndex in activeThreatIndices) { foreach (ThreatData threatData in threatTable) {
Agent target = targets[targetIndex]; Threat threat = threatData.Threat;
float distanceToMean = Vector3.Distance(target.transform.position, missilesMeanPosition); float distanceToMean = Vector3.Distance(threat.transform.position, defensePosition);
float velocityMagnitude = target.GetVelocity().magnitude; float velocityMagnitude = threat.GetVelocity().magnitude;
// Calculate threat level based on proximity and velocity // Calculate threat level based on proximity and velocity
float threatLevel = (1 / distanceToMean) * velocityMagnitude; float threatLevel = (1 / distanceToMean) * velocityMagnitude;
threatInfos.Add(new ThreatInfo(targetIndex, threatLevel)); threatInfos.Add(new ThreatInfo(threatData, threatLevel));
} }
// Sort threats in descending order // Sort threats in descending order
@ -74,11 +68,11 @@ public class ThreatAssignment : IAssignment {
} }
private class ThreatInfo { private class ThreatInfo {
public int TargetIndex { get; } public ThreatData ThreatData { get; }
public float ThreatLevel { get; } public float ThreatLevel { get; }
public ThreatInfo(int targetIndex, float threatLevel) { public ThreatInfo(ThreatData threatData, float threatLevel) {
TargetIndex = targetIndex; ThreatData = threatData;
ThreatLevel = threatLevel; ThreatLevel = threatLevel;
} }
} }

View File

@ -4,121 +4,115 @@ using UnityEngine;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Converters; using Newtonsoft.Json.Converters;
[Serializable] [Serializable]
public class SimulationConfig { public class SimulationConfig {
[Header("Simulation Settings")] [Header("Simulation Settings")]
public float timeScale = 0.05f; public float timeScale = 0.05f;
// [Header("Defense Points")] [Header("Interceptor Swarm Configurations")]
// public SwarmConfig defense_points_config; public List<SwarmConfig> interceptor_swarm_configs = new List<SwarmConfig>();
[Header("Interceptor Swarm Configurations")] [Header("Threat Swarm Configurations")]
public List<SwarmConfig> interceptor_swarm_configs = new List<SwarmConfig>(); public List<SwarmConfig> threat_swarm_configs = new List<SwarmConfig>();
[Header("Threat Swarm Configurations")]
public List<SwarmConfig> threat_swarm_configs = new List<SwarmConfig>();
} }
[Serializable] [Serializable]
public class DynamicConfig { public class DynamicConfig {
public LaunchConfig launch_config; public LaunchConfig launch_config;
public SensorConfig sensor_config; public SensorConfig sensor_config;
} }
[Serializable] [Serializable]
public class SwarmConfig { public class SwarmConfig {
public int num_agents; public int num_agents;
public AgentConfig agent_config; public AgentConfig agent_config;
} }
[Serializable] [Serializable]
public class AgentConfig { public class AgentConfig {
public InterceptorType interceptor_type; public InterceptorType interceptor_type;
public ThreatType threat_type; public ThreatType threat_type;
public InitialState initial_state; public InitialState initial_state;
public StandardDeviation standard_deviation; public StandardDeviation standard_deviation;
public DynamicConfig dynamic_config; public DynamicConfig dynamic_config;
public PlottingConfig plotting_config; public PlottingConfig plotting_config;
public SubmunitionsConfig submunitions_config; public SubmunitionsConfig submunitions_config;
public static AgentConfig FromSubmunitionAgentConfig(SubmunitionAgentConfig submunitionConfig) { public static AgentConfig FromSubmunitionAgentConfig(SubmunitionAgentConfig submunitionConfig) {
return new AgentConfig { return new AgentConfig {
interceptor_type = submunitionConfig.interceptor_type, interceptor_type = submunitionConfig.interceptor_type,
initial_state = submunitionConfig.initial_state, initial_state = submunitionConfig.initial_state,
standard_deviation = submunitionConfig.standard_deviation, standard_deviation = submunitionConfig.standard_deviation,
dynamic_config = submunitionConfig.dynamic_config, dynamic_config = submunitionConfig.dynamic_config,
plotting_config = submunitionConfig.plotting_config, plotting_config = submunitionConfig.plotting_config,
// Set other fields as needed, using default values if not present in SubmunitionAgentConfig // Set other fields as needed, using default values if not present in SubmunitionAgentConfig
threat_type = ThreatType.DRONE, // Or another default value threat_type = ThreatType.DRONE, // Or another default value
submunitions_config = null // Or a default value if needed submunitions_config = null // Or a default value if needed
}; };
} }
} }
[Serializable] [Serializable]
public class InitialState { public class InitialState {
public Vector3 position; public Vector3 position;
public Vector3 rotation; public Vector3 rotation;
public Vector3 velocity; public Vector3 velocity;
} }
[Serializable] [Serializable]
public class StandardDeviation { public class StandardDeviation {
public Vector3 position; public Vector3 position;
public Vector3 velocity; public Vector3 velocity;
} }
[Serializable] [Serializable]
public class LaunchConfig { public class LaunchConfig {
public float launch_time; public float launch_time;
} }
[Serializable] [Serializable]
public class PlottingConfig { public class PlottingConfig {
public ConfigColor color; public ConfigColor color;
public LineStyle linestyle; public LineStyle linestyle;
public Marker marker; public Marker marker;
} }
[Serializable] [Serializable]
public class SubmunitionsConfig { public class SubmunitionsConfig {
public int num_submunitions; public int num_submunitions;
public LaunchConfig launch_config; public LaunchConfig launch_config;
public SubmunitionAgentConfig agent_config; public SubmunitionAgentConfig agent_config;
} }
[Serializable] [Serializable]
public class SubmunitionAgentConfig { public class SubmunitionAgentConfig {
public InterceptorType interceptor_type; public InterceptorType interceptor_type;
public InitialState initial_state; public InitialState initial_state;
public StandardDeviation standard_deviation; public StandardDeviation standard_deviation;
public DynamicConfig dynamic_config; public DynamicConfig dynamic_config;
public PlottingConfig plotting_config; public PlottingConfig plotting_config;
} }
[Serializable] [Serializable]
public class SensorConfig { public class SensorConfig {
public SensorType type; public SensorType type;
public float frequency; public float frequency;
} }
[Serializable] [Serializable]
public class TargetConfig { public class TargetConfig {
public ThreatType threat_type; public ThreatType threat_type;
public InitialState initial_state; public InitialState initial_state;
public PlottingConfig plotting_config; public PlottingConfig plotting_config;
public string prefabName; public string prefabName;
} }
// Enums // Enums
[JsonConverter(typeof(StringEnumConverter))] [JsonConverter(typeof(StringEnumConverter))]
public enum InterceptorType { HYDRA_70, MICROMISSILE } public enum InterceptorType { HYDRA_70, MICROMISSILE }
[JsonConverter(typeof(StringEnumConverter))] [JsonConverter(typeof(StringEnumConverter))]
public enum ThreatType { public enum ThreatType { DRONE, ANTISHIP_MISSILE }
DRONE,
FIXED_WING_MISSILE,
ROLL_STABILIZED_MISSILE
} // Add ballistic later -michael
[JsonConverter(typeof(StringEnumConverter))] [JsonConverter(typeof(StringEnumConverter))]
public enum ConfigColor { BLUE, GREEN, RED } public enum ConfigColor { BLUE, GREEN, RED }
[JsonConverter(typeof(StringEnumConverter))] [JsonConverter(typeof(StringEnumConverter))]

View File

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

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

View File

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

View File

@ -1,30 +1,146 @@
using UnityEngine; using UnityEngine;
using System.Collections.Generic;
using System.Collections;
using System; using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
// Integrated Air Defense System // Integrated Air Defense System
public class IADS : MonoBehaviour { public class IADS : MonoBehaviour {
public enum TargetStatus { UNASSIGNED, ASSIGNED, HIT, DEGRADED, DESTROYED }
// Look up threat status by unique threat ID public enum ThreatAssignmentStyle {
public Dictionary<string, TargetStatus> _targetStatusDictionary; ONE_TIME,
CONTINUOUS
}
private List<Threat> _threats; public static IADS Instance { get; private set; }
private IAssignment _assignmentScheme;
[SerializeField]
private List<ThreatData> _threatTable = new List<ThreatData>();
private Dictionary<Threat, ThreatData> _threatDataMap = new Dictionary<Threat, ThreatData>();
private List<Interceptor> _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;
void Start() { private void Start() {
_threats = new List<Threat>(); SimManager.Instance.OnSimulationEnded += RegisterSimulationEnded;
SimManager.Instance.OnNewThreat += RegisterNewThreat;
SimManager.Instance.OnNewInterceptor += RegisterNewInterceptor;
_assignmentScheme = new ThreatAssignment();
}
public void LateUpdate() {
if (_assignmentQueue.Count > 0) {
AssignInterceptorsToThreats(_assignmentQueue);
_assignmentQueue.Clear();
}
}
public void RequestThreatAssignment(List<Interceptor> interceptors) {
_assignmentQueue.AddRange(interceptors);
}
public void RequestThreatAssignment(Interceptor interceptor) {
_assignmentQueue.Add(interceptor);
}
/// <summary>
/// Assigns the specified list of missiles to available targets based on the assignment scheme.
/// </summary>
/// <param name="missilesToAssign">The list of missiles to assign.</param>
public void AssignInterceptorsToThreats(List<Interceptor> missilesToAssign) {
// Perform the assignment
IEnumerable<IAssignment.AssignmentItem> assignments =
_assignmentScheme.Assign(missilesToAssign, _threatTable);
// Apply the assignments to the missiles
foreach (var assignment in assignments) {
assignment.Interceptor.AssignTarget(assignment.Threat);
_threatDataMap[assignment.Threat].AssignInterceptor(assignment.Interceptor);
Debug.Log($"Interceptor {assignment.Interceptor.name} assigned to threat {assignment.Threat.name}");
}
// Check if any interceptors were not assigned
List<Interceptor> unassignedInterceptors = missilesToAssign.Where(m => !m.HasAssignedTarget()).ToList();
if (unassignedInterceptors.Count > 0)
{
string unassignedIds = string.Join(", ", unassignedInterceptors.Select(m => m.name));
int totalInterceptors = missilesToAssign.Count;
int assignedInterceptors = totalInterceptors - unassignedInterceptors.Count;
Debug.LogWarning($"Warning: {unassignedInterceptors.Count} out of {totalInterceptors} interceptors were not assigned to any threat. " +
$"Unassigned interceptor IDs: {unassignedIds}. " +
$"Total interceptors: {totalInterceptors}, Assigned: {assignedInterceptors}, Unassigned: {unassignedInterceptors.Count}");
// Log information about the assignment scheme
Debug.Log($"Current Assignment Scheme: {_assignmentScheme.GetType().Name}");
}
} }
public void RegisterNewThreat(Threat threat) { public void RegisterNewThreat(Threat threat) {
_threats.Add(threat); ThreatData threatData = new ThreatData(threat, threat.gameObject.name);
OnRegisterNewThreat?.Invoke(threat); _threatTable.Add(threatData);
_threatDataMap.Add(threat, threatData);
// Subscribe to the threat's events
// TODO: If we do not want omniscient IADS, we
// need to model the IADS's sensors here.
threat.OnInterceptHit += RegisterThreatHit;
threat.OnInterceptMiss += RegisterThreatMiss;
} }
public void RegisterNewInterceptor(Interceptor interceptor) {
// Placeholder
interceptor.OnInterceptMiss += RegisterInterceptorMiss;
interceptor.OnInterceptHit += RegisterInterceptorHit;
}
private void RegisterInterceptorHit(Interceptor interceptor, Threat threat) {
ThreatData threatData = _threatDataMap[threat];
if (threatData != null) {
threatData.RemoveInterceptor(interceptor);
MarkThreatDestroyed(threatData);
}
}
private void RegisterInterceptorMiss(Interceptor interceptor, Threat threat) {
// Remove the interceptor from the threat's assigned interceptors
_threatDataMap[threat].RemoveInterceptor(interceptor);
}
private void RegisterThreatHit(Interceptor interceptor, Threat threat) {
ThreatData threatData = _threatDataMap[threat];
if (threatData != null) {
threatData.RemoveInterceptor(interceptor);
MarkThreatDestroyed(threatData);
}
}
private void MarkThreatDestroyed(ThreatData threatData) {
if (threatData != null) {
threatData.MarkDestroyed();
}
}
private void RegisterThreatMiss(Interceptor interceptor, Threat threat) {
ThreatData threatData = _threatDataMap[threat];
threatData.RemoveInterceptor(interceptor);
}
private void RegisterSimulationEnded() {
_threatTable.Clear();
_threatDataMap.Clear();
_assignmentQueue.Clear();
}
} }

View File

@ -0,0 +1,48 @@
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public enum ThreatStatus {
UNASSIGNED,
ASSIGNED,
DESTROYED
}
[System.Serializable]
public class ThreatData
{
public Threat Threat;
[SerializeField]
private ThreatStatus _status;
public ThreatStatus Status { get { return _status; } }
public string ThreatID;
[SerializeField]
private List<Interceptor> _assignedInterceptors; // Changed from property to field
public void AssignInterceptor(Interceptor interceptor) {
if(Status == ThreatStatus.DESTROYED) {
Debug.LogError($"AssignInterceptor: Threat {ThreatID} is destroyed, cannot assign interceptor");
return;
}
_status = ThreatStatus.ASSIGNED;
_assignedInterceptors.Add(interceptor);
}
public void RemoveInterceptor(Interceptor interceptor) {
_assignedInterceptors.Remove(interceptor);
if(_assignedInterceptors.Count == 0) {
_status = ThreatStatus.UNASSIGNED;
}
}
public void MarkDestroyed() {
_status = ThreatStatus.DESTROYED;
}
// Constructor remains the same
public ThreatData(Threat threat, string threatID)
{
Threat = threat;
_status = ThreatStatus.UNASSIGNED;
ThreatID = threatID;
_assignedInterceptors = new List<Interceptor>(); // Initialize the list
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -3,70 +3,261 @@ using System.Collections;
using System.IO; using System.IO;
using System; using System;
using System.Linq; using System.Linq;
using System.Collections.Generic;
using System.Threading;
public class SimMonitor : MonoBehaviour public class SimMonitor : MonoBehaviour
{ {
private const float UpdateRate = 0.01f; // 100 Hz private const float _updateRate = 0.1f; // 100 Hz
private StreamWriter writer; private string _telemetryBinPath;
private Coroutine monitorRoutine; private string _eventLogPath;
private Coroutine _monitorRoutine;
private string _sessionDirectory;
private FileStream _telemetryFileStream;
private BinaryWriter _telemetryBinaryWriter;
[SerializeField]
private List<EventRecord> _eventLogCache;
[System.Serializable]
private class EventRecord
{
public float Time;
public float PositionX;
public float PositionY;
public float PositionZ;
public string EventType;
public string Details;
}
private void Awake() {
InitializeSessionDirectory();
}
private void Start() private void Start()
{ {
SimManager.Instance.OnSimulationStarted += RegisterSimulationStarted;
SimManager.Instance.OnSimulationEnded += RegisterSimulationEnded; SimManager.Instance.OnSimulationEnded += RegisterSimulationEnded;
InitializeFile(); SimManager.Instance.OnNewThreat += RegisterNewThreat;
monitorRoutine = StartCoroutine(MonitorRoutine()); SimManager.Instance.OnNewInterceptor += RegisterNewInterceptor;
} }
private void InitializeFile() private void InitializeSessionDirectory() {
string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
_sessionDirectory = Application.persistentDataPath + $"\\Telemetry\\Logs\\{timestamp}";
Directory.CreateDirectory(_sessionDirectory);
Debug.Log($"Monitoring simulation logs to {_sessionDirectory}");
}
private void InitializeLogFiles()
{ {
string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss"); string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
string fileName = $"sim_telemetry_{timestamp}.csv";
string directory = Application.persistentDataPath + "/Telemetry/Logs/"; _eventLogPath = Path.Combine(_sessionDirectory, $"sim_events_{timestamp}.csv");
Directory.CreateDirectory(directory);
string path = Path.Combine(directory, fileName); // Initialize the event log cache
writer = new StreamWriter(path, false); _eventLogCache = new List<EventRecord>();
writer.WriteLine("Time,AgentID,AgentX,AgentY,AgentZ,AgentVX,AgentVY,AgentVZ,AgentState,AgentType");
Debug.Log($"Monitoring simulation data to {path}"); _telemetryBinPath = Path.Combine(_sessionDirectory, $"sim_telemetry_{timestamp}.bin");
// Open the file stream and binary writer for telemetry data
_telemetryFileStream = new FileStream(_telemetryBinPath, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
_telemetryBinaryWriter = new BinaryWriter(_telemetryFileStream);
Debug.Log("Log files initialized successfully.");
}
private void CloseLogFiles() {
if (_telemetryBinaryWriter != null)
{
_telemetryBinaryWriter.Flush();
_telemetryBinaryWriter.Close();
_telemetryBinaryWriter = null;
}
if (_telemetryFileStream != null)
{
_telemetryFileStream.Close();
_telemetryFileStream = null;
}
} }
private IEnumerator MonitorRoutine() private IEnumerator MonitorRoutine()
{ {
while (true) while (true)
{ {
ExportTelemetry(); RecordTelemetry();
yield return new WaitForSeconds(UpdateRate); yield return new WaitForSeconds(_updateRate);
} }
} }
private void ExportTelemetry() private void RecordTelemetry()
{ {
float time = (float)SimManager.Instance.GetElapsedSimulationTime(); float time = (float)SimManager.Instance.GetElapsedSimulationTime();
foreach (var agent in SimManager.Instance.GetActiveThreats().Cast<Agent>().Concat(SimManager.Instance.GetActiveInterceptors().Cast<Agent>())) var agents = SimManager.Instance.GetActiveAgents();
{ if(_telemetryBinaryWriter == null) {
Vector3 pos = agent.transform.position; Debug.LogWarning("Telemetry binary writer is null");
if(pos == Vector3.zero) { return;
continue;
}
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}");
} }
for (int i = 0; i < agents.Count; i++)
{
var agent = agents[i];
writer.Flush(); if (!agent.gameObject.activeInHierarchy)
continue;
Vector3 pos = agent.transform.position;
if (pos == Vector3.zero)
continue;
Vector3 vel = agent.GetVelocity(); // Ensure GetVelocity() doesn't allocate
int agentID = agent.GetInstanceID();
int flightPhase = (int)agent.GetFlightPhase();
byte agentType = (byte)(agent is Threat ? 0 : 1);
// Write telemetry data directly to the binary file
_telemetryBinaryWriter.Write(time);
_telemetryBinaryWriter.Write(agentID);
_telemetryBinaryWriter.Write(pos.x);
_telemetryBinaryWriter.Write(pos.y);
_telemetryBinaryWriter.Write(pos.z);
_telemetryBinaryWriter.Write(vel.x);
_telemetryBinaryWriter.Write(vel.y);
_telemetryBinaryWriter.Write(vel.z);
_telemetryBinaryWriter.Write(flightPhase);
_telemetryBinaryWriter.Write(agentType);
}
}
public void ConvertBinaryTelemetryToCsv(string binaryFilePath, string csvFilePath)
{
try
{
using FileStream fs = new FileStream(binaryFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using BinaryReader reader = new BinaryReader(fs);
using StreamWriter writer = new StreamWriter(csvFilePath, false);
{
// Write CSV header
writer.WriteLine("Time,AgentID,AgentX,AgentY,AgentZ,AgentVX,AgentVY,AgentVZ,AgentState,AgentType");
while (reader.BaseStream.Position != reader.BaseStream.Length)
{
float time = reader.ReadSingle();
int agentID = reader.ReadInt32();
float posX = reader.ReadSingle();
float posY = reader.ReadSingle();
float posZ = reader.ReadSingle();
float velX = reader.ReadSingle();
float velY = reader.ReadSingle();
float velZ = reader.ReadSingle();
int flightPhase = reader.ReadInt32();
byte agentTypeByte = reader.ReadByte();
string agentType = agentTypeByte == 0 ? "T" : "M";
// Write the data to CSV
writer.WriteLine($"{time:F2},{agentID},{posX:F2},{posY:F2},{posZ:F2},{velX:F2},{velY:F2},{velZ:F2},{flightPhase},{agentType}");
}
}
}
catch (IOException e)
{
Debug.LogWarning($"An IO error occurred while converting binary telemetry to CSV: {e.Message}");
}
}
private void WriteEventsToFile()
{
using (StreamWriter writer = new StreamWriter(_eventLogPath, false))
{
// Write CSV header
writer.WriteLine("Time,PositionX,PositionY,PositionZ,Event,Details");
foreach (var record in _eventLogCache)
{
writer.WriteLine($"{record.Time:F2},{record.PositionX:F2},{record.PositionY:F2},{record.PositionZ:F2},{record.EventType},{record.Details}");
}
}
}
private void RegisterSimulationStarted()
{
InitializeLogFiles();
_monitorRoutine = StartCoroutine(MonitorRoutine());
} }
private void RegisterSimulationEnded() private void RegisterSimulationEnded()
{ {
writer.Close(); StopCoroutine(_monitorRoutine);
StopCoroutine(monitorRoutine); CloseLogFiles();
WriteEventsToFile();
StartCoroutine(ConvertBinaryTelemetryToCsvCoroutine(_telemetryBinPath, Path.ChangeExtension(_telemetryBinPath, ".csv")));
} }
private IEnumerator ConvertBinaryTelemetryToCsvCoroutine(string binaryFilePath, string csvFilePath)
{
yield return null; // Wait for the next frame to ensure RecordTelemetry() has finished
ConvertBinaryTelemetryToCsv(binaryFilePath, csvFilePath);
}
public void RegisterNewThreat(Threat threat) {
RegisterNewAgent(threat, "NEW_THREAT");
}
public void RegisterNewInterceptor(Interceptor interceptor) {
RegisterNewAgent(interceptor, "NEW_INTERCEPTOR");
interceptor.OnInterceptMiss += RegisterInterceptorMiss;
interceptor.OnInterceptHit += RegisterInterceptorHit;
}
private void RegisterNewAgent(Agent agent, string eventType)
{
float time = (float)SimManager.Instance.GetElapsedSimulationTime();
Vector3 pos = agent.transform.position;
var record = new EventRecord
{
Time = time,
PositionX = pos.x,
PositionY = pos.y,
PositionZ = pos.z,
EventType = eventType,
Details = agent.name
};
_eventLogCache.Add(record);
}
public void RegisterInterceptorHit(Interceptor interceptor, Threat threat) {
RegisterInterceptEvent(interceptor, threat, true);
}
public void RegisterInterceptorMiss(Interceptor interceptor, Threat threat) {
RegisterInterceptEvent(interceptor, threat, false);
}
public void RegisterInterceptEvent(Interceptor interceptor, Threat threat, bool hit)
{
float time = (float)SimManager.Instance.GetElapsedSimulationTime();
Vector3 pos = interceptor.transform.position;
string eventType = hit ? "HIT" : "MISS";
var record = new EventRecord
{
Time = time,
PositionX = pos.x,
PositionY = pos.y,
PositionZ = pos.z,
EventType = eventType,
Details = $"{interceptor.name} and {threat.name}"
};
_eventLogCache.Add(record);
}
private void OnDestroy() private void OnDestroy()
{ {
if (writer != null) CloseLogFiles();
{
writer.Close();
}
} }
} }

View File

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

View File

@ -1,4 +1,3 @@
using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -20,21 +19,30 @@ public class SimManager : MonoBehaviour {
[SerializeField] [SerializeField]
public SimulationConfig simulationConfig; public SimulationConfig simulationConfig;
private List<Interceptor> _interceptors = new List<Interceptor>();
private List<Interceptor> _activeInterceptors = new List<Interceptor>(); private List<Interceptor> _activeInterceptors = new List<Interceptor>();
private List<Threat> _unassignedThreats = new List<Threat>();
private List<Threat> _threats = new List<Threat>();
private List<Threat> _activeThreats = new List<Threat>(); private List<Interceptor> _interceptorObjects = new List<Interceptor>();
private List<Threat> _threatObjects = new List<Threat>();
private float _elapsedSimulationTime = 0f; private float _elapsedSimulationTime = 0f;
private float endTime = 100f; // Set an appropriate end time private float endTime = 100f; // Set an appropriate end time
private bool simulationRunning = false; private bool simulationRunning = false;
private IAssignment _assignmentScheme;
public delegate void SimulationEventHandler(); public delegate void SimulationEventHandler();
public event SimulationEventHandler OnSimulationEnded; public event SimulationEventHandler OnSimulationEnded;
public event SimulationEventHandler OnSimulationStarted; 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> /// <summary>
/// Gets the elapsed simulation time. /// Gets the elapsed simulation time.
/// </summary> /// </summary>
@ -48,12 +56,12 @@ public class SimManager : MonoBehaviour {
} }
public List<Threat> GetActiveThreats() { public List<Threat> GetActiveThreats() {
return _activeThreats; return _threatObjects.Where(threat => !threat.IsHit()).ToList();
} }
public List<Agent> GetActiveAgents() { public List<Agent> GetActiveAgents() {
return _activeInterceptors.ConvertAll(interceptor => interceptor as Agent) return _activeInterceptors.ConvertAll(interceptor => interceptor as Agent)
.Concat(_activeThreats.ConvertAll(threat => threat as Agent)) .Concat(GetActiveThreats().ConvertAll(threat => threat as Agent))
.ToList(); .ToList();
} }
@ -75,6 +83,7 @@ public class SimManager : MonoBehaviour {
StartSimulation(); StartSimulation();
ResumeSimulation(); ResumeSimulation();
} }
} }
public void SetTimeScale(float timeScale) { public void SetTimeScale(float timeScale) {
@ -84,8 +93,9 @@ public class SimManager : MonoBehaviour {
} }
public void StartSimulation() { public void StartSimulation() {
InitializeSimulation();
OnSimulationStarted?.Invoke(); OnSimulationStarted?.Invoke();
InitializeSimulation();
} }
public void PauseSimulation() { public void PauseSimulation() {
@ -103,13 +113,14 @@ public class SimManager : MonoBehaviour {
} }
private void InitializeSimulation() { 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>(); List<Interceptor> missiles = new List<Interceptor>();
// Create missiles based on config // Create missiles based on config
foreach (var swarmConfig in simulationConfig.interceptor_swarm_configs) { foreach (var swarmConfig in simulationConfig.interceptor_swarm_configs) {
for (int i = 0; i < swarmConfig.num_agents; i++) { for (int i = 0; i < swarmConfig.num_agents; i++) {
var interceptor = CreateInterceptor(swarmConfig.agent_config); CreateInterceptor(swarmConfig.agent_config);
interceptor.OnAgentHit += RegisterInterceptorHit;
interceptor.OnAgentMiss += RegisterInterceptorMiss;
} }
} }
@ -117,78 +128,38 @@ public class SimManager : MonoBehaviour {
// Create targets based on config // Create targets based on config
foreach (var swarmConfig in simulationConfig.threat_swarm_configs) { foreach (var swarmConfig in simulationConfig.threat_swarm_configs) {
for (int i = 0; i < swarmConfig.num_agents; i++) { for (int i = 0; i < swarmConfig.num_agents; i++) {
var threat = CreateThreat(swarmConfig.agent_config); 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() { public void AssignInterceptorsToThreats() {
AssignInterceptorsToThreats(_interceptors); IADS.Instance.AssignInterceptorsToThreats(_interceptorObjects);
} }
public void RegisterInterceptorHit(Agent interceptor) { public void RegisterInterceptorHit(Interceptor interceptor, Threat threat) {
if (interceptor is Interceptor missileComponent) { if (interceptor is Interceptor missileComponent) {
_activeInterceptors.Remove(missileComponent); _activeInterceptors.Remove(missileComponent);
} }
} }
public void RegisterInterceptorMiss(Agent interceptor) { public void RegisterInterceptorMiss(Interceptor interceptor, Threat threat) {
if (interceptor is Interceptor missileComponent) { if (interceptor is Interceptor missileComponent) {
_activeInterceptors.Remove(missileComponent); _activeInterceptors.Remove(missileComponent);
} }
} }
public void RegisterThreatHit(Agent threat) { public void RegisterThreatHit(Interceptor interceptor, Threat threat) {
if (threat is Threat targetComponent) { // Placeholder
_activeThreats.Remove(targetComponent);
}
} }
public void RegisterThreatMiss(Agent threat) { public void RegisterThreatMiss(Interceptor interceptor, Threat threat) {
if (threat is Threat targetComponent) { Debug.Log($"RegisterThreatMiss: Interceptor {interceptor.name} missed threat {threat.name}");
_unassignedThreats.Add(targetComponent); // Placeholder
}
} }
/// <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> /// <summary>
/// Creates a interceptor based on the provided configuration. /// Creates a interceptor based on the provided configuration.
@ -196,32 +167,41 @@ public class SimManager : MonoBehaviour {
/// <param name="config">Configuration settings for the interceptor.</param> /// <param name="config">Configuration settings for the interceptor.</param>
/// <returns>The created Interceptor instance, or null if creation failed.</returns> /// <returns>The created Interceptor instance, or null if creation failed.</returns>
public Interceptor CreateInterceptor(AgentConfig config) { public Interceptor CreateInterceptor(AgentConfig config) {
string prefabName = string prefabName = config.interceptor_type switch { InterceptorType.HYDRA_70 => "Hydra70",
config.interceptor_type switch { InterceptorType.HYDRA_70 => "Hydra70", InterceptorType.MICROMISSILE => "Micromissile",
InterceptorType.MICROMISSILE => "Micromissile", _ => "Hydra70" };
_ => "Hydra70" };
GameObject missileObject = CreateAgent(config, prefabName); GameObject interceptorObject = CreateAgent(config, prefabName);
if (missileObject == null) if (interceptorObject == null)
return null; return null;
// Interceptor-specific logic // Interceptor-specific logic
switch (config.dynamic_config.sensor_config.type) { switch (config.dynamic_config.sensor_config.type) {
case SensorType.IDEAL: case SensorType.IDEAL:
missileObject.AddComponent<IdealSensor>(); interceptorObject.AddComponent<IdealSensor>();
break; break;
default: default:
Debug.LogError($"Sensor type '{config.dynamic_config.sensor_config.type}' not found."); Debug.LogError($"Sensor type '{config.dynamic_config.sensor_config.type}' not found.");
break; break;
} }
_interceptors.Add(missileObject.GetComponent<Interceptor>()); Interceptor interceptor = interceptorObject.GetComponent<Interceptor>();
_activeInterceptors.Add(missileObject.GetComponent<Interceptor>()); _interceptorObjects.Add(interceptor);
_activeInterceptors.Add(interceptor);
// Subscribe events
interceptor.OnInterceptHit += RegisterInterceptorHit;
interceptor.OnInterceptMiss += RegisterInterceptorMiss;
// Assign a unique and simple ID // Assign a unique and simple ID
int missileId = _interceptors.Count; int interceptorId = _interceptorObjects.Count;
missileObject.name = $"{config.interceptor_type}_Interceptor_{missileId}"; interceptorObject.name = $"{config.interceptor_type}_Interceptor_{interceptorId}";
return missileObject.GetComponent<Interceptor>();
// Let listeners know a new interceptor has been created
OnNewInterceptor?.Invoke(interceptor);
return interceptorObject.GetComponent<Interceptor>();
} }
/// <summary> /// <summary>
@ -231,23 +211,28 @@ public class SimManager : MonoBehaviour {
/// <returns>The created Threat instance, or null if creation failed.</returns> /// <returns>The created Threat instance, or null if creation failed.</returns>
private Threat CreateThreat(AgentConfig config) { private Threat CreateThreat(AgentConfig config) {
string prefabName = config.threat_type switch { string prefabName = config.threat_type switch {
ThreatType.DRONE => "Drone", ThreatType.FIXED_WING_MISSILE => "FixedWingMissileThreat", ThreatType.DRONE => "Drone", ThreatType.ANTISHIP_MISSILE => "AntishipMissile",
ThreatType.ROLL_STABILIZED_MISSILE => "RollStabilizedMissileThreat",
_ => throw new System.ArgumentException($"Unsupported threat type: {config.threat_type}") _ => throw new System.ArgumentException($"Unsupported threat type: {config.threat_type}")
}; };
GameObject threatObject = CreateAgent(config, prefabName); GameObject threatObject = CreateAgent(config, prefabName);
if (threatObject == null) { if (threatObject == null)
Debug.LogError($"Failed to create threat for {prefabName}.");
return null; 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 // Assign a unique and simple ID
int targetId = _threats.Count; int targetId = _threatObjects.Count;
threatObject.name = $"{config.threat_type}_Target_{targetId}"; 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>(); return threatObject.GetComponent<Threat>();
} }
@ -297,22 +282,22 @@ public class SimManager : MonoBehaviour {
_elapsedSimulationTime = 0f; _elapsedSimulationTime = 0f;
simulationRunning = IsSimulationRunning(); simulationRunning = IsSimulationRunning();
// Clear existing missiles and targets // Clear existing interceptors and threats
foreach (var interceptor in _interceptors) { foreach (var interceptor in _interceptorObjects) {
if (interceptor != null) { if (interceptor != null) {
Destroy(interceptor.gameObject); Destroy(interceptor.gameObject);
} }
} }
foreach (var threat in _threats) { foreach (var threat in _threatObjects) {
if (threat != null) { if (threat != null) {
Destroy(threat.gameObject); Destroy(threat.gameObject);
} }
} }
_interceptors.Clear(); _interceptorObjects.Clear();
_threats.Clear(); _activeInterceptors.Clear();
_unassignedThreats.Clear(); _threatObjects.Clear();
StartSimulation(); StartSimulation();
} }
@ -320,8 +305,8 @@ public class SimManager : MonoBehaviour {
void Update() { void Update() {
// Check if all missiles have terminated // Check if all missiles have terminated
bool allInterceptorsTerminated = true; bool allInterceptorsTerminated = true;
foreach (var interceptor in _interceptors) { foreach (var interceptor in _interceptorObjects) {
if (interceptor != null && !interceptor.IsHit() && !interceptor.IsMiss()) { if (interceptor != null && interceptor.GetFlightPhase() != Agent.FlightPhase.TERMINATED) {
allInterceptorsTerminated = false; allInterceptorsTerminated = false;
break; break;
} }

View File

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

View File

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

View File

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

View File

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

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

View File

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

@ -3,29 +3,6 @@ using System.Collections.Generic;
using UnityEngine; using UnityEngine;
public abstract class Threat : Agent { 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() { public override bool IsAssignable() {
return false; return false;
} }
@ -36,18 +13,5 @@ public abstract class Threat : Agent {
protected override void FixedUpdate() { protected override void FixedUpdate() {
base.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

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

View File

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

View File

@ -1,146 +0,0 @@
{
"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,25 +0,0 @@
{
"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
}
}

8
Assets/Tests.meta Normal file
View File

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

View File

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

View File

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

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

View File

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

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

View File

@ -0,0 +1,24 @@
{
"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,6 +1,6 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 9c1ccc1cc7ad8cb4aa0b59a0afd50568 guid: d15c92e585e721749b63d85007276dbe
PrefabImporter: AssemblyDefinitionImporter:
externalObjects: {} externalObjects: {}
userData: userData:
assetBundleName: assetBundleName:

View File

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

View File

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

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

View File

@ -0,0 +1,22 @@
{
"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

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

View File

@ -55,7 +55,18 @@ MonoBehaviour:
- rid: 162646946419048467 - rid: 162646946419048467
- rid: 162646946419048468 - rid: 162646946419048468
m_RuntimeSettings: m_RuntimeSettings:
m_List: [] 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_AssetVersion: 8 m_AssetVersion: 8
m_ObsoleteDefaultVolumeProfile: {fileID: 0} m_ObsoleteDefaultVolumeProfile: {fileID: 0}
m_RenderingLayerNames: m_RenderingLayerNames:

View File

@ -1,5 +1,26 @@
{ {
"m_Dictionary": { "m_Dictionary": {
"m_DictionaryValues": [] "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}"
}
]
} }
} }

View File

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

101
README.md
View File

@ -2,6 +2,15 @@
![Sim Salvo Animation](docs/images/sim_salvo_animation.gif) ![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 # 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. 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.
@ -24,91 +33,9 @@ You can find the latest release [here](https://github.com/PisterLab/micromissile
* Navigate to `Privacy & Security`. * Navigate to `Privacy & Security`.
* Click on `Open Anyway` to bypass Apple's developer check. * Click on `Open Anyway` to bypass Apple's developer check.
# Development # Next Steps
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. - 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.
## Table of Contents - 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).
- [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

@ -1,82 +0,0 @@
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)

198
Tools/visualize_log.py Normal file
View File

@ -0,0 +1,198 @@
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 Normal file
View File

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

View File

@ -0,0 +1,40 @@
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.'
}
}
})

84
docs/Development_Guide.md Normal file
View File

@ -0,0 +1,84 @@
# 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)

85
docs/Markdown_Examples.md Normal file
View File

@ -0,0 +1,85 @@
# 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**: - **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. - **`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. - **`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`](Assets/Scripts/Config/SimulationConfig.cs) - **C# Script**: [`SimulationConfig.cs`](https://github.com/PisterLab/micromissiles-unity/blob/master/Assets/Scripts/Config/StaticConfig.cs)
- **Model Configurations** (found in `Assets/StreamingAssets/Configs/Models/`): - **Model Configurations** (found in `Assets/StreamingAssets/Configs/Models/`):
- **`micromissile.json`** - **`micromissile.json`**
- **`hydra70.json`** - **`hydra70.json`**
- **`drone.json`** - **`drone.json`**
- **C# Script**: [`StaticConfig.cs`](Assets/Scripts/Config/StaticConfig.cs) - **C# Script**: [`StaticConfig.cs`](https://github.com/PisterLab/micromissiles-unity/blob/master/Assets/Scripts/Config/StaticConfig.cs)
### File Locations ### 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. This is a basic configuration featuring a single salvo with one interceptor type (`HYDRA_70`) and seven threat drones.
```json:Assets/StreamingAssets/Configs/1_salvo_1_hydra_7_drones.json ```json
{ {
"timeScale": 1, "timeScale": 1,
"interceptor_swarm_configs": [ "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. 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:Assets/StreamingAssets/Configs/3_salvo_10_hydra_200_drones.json ```json
{ {
"timeScale": 1, "timeScale": 1,
"interceptor_swarm_configs": [ "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. This script defines the data structures used to interpret the JSON simulation configuration files.
[Assets/Scripts/Config/SimulationConfig.cs](../../Assets/Scripts/Config/SimulationConfig.cs) [Assets/Scripts/Config/SimulationConfig.cs](https://github.com/PisterLab/micromissiles-unity/blob/master/Assets/Scripts/Config/SimulationConfig.cs)
**Classes**: **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. This script defines the classes corresponding to the model configuration JSON structure.
[Assets/Scripts/Config/StaticConfig.cs](../../Assets/Scripts/Config/StaticConfig.cs) [Assets/Scripts/Config/StaticConfig.cs](https://github.com/PisterLab/micromissiles-unity/blob/master/Assets/Scripts/Config/StaticConfig.cs)
For example: For example:
```csharp:Assets/Scripts/Config/StaticConfig.cs ```csharp
[Serializable] [Serializable]
public class StaticConfig { public class StaticConfig {
[Serializable] [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: For further assistance, refer to the comments and documentation within the code files:
- [`SimManager.cs`](Assets/Scripts/SimManager.cs): Manages simulation state and agent creation. - [`SimManager.cs`](https://github.com/PisterLab/micromissiles-unity/blob/master/Assets/Scripts/SimManager.cs): Manages simulation state and agent creation.
- [`InputManager.cs`](Assets/Scripts/Managers/InputManager.cs): Handles user input and interactions. - [`InputManager.cs`](https://github.com/PisterLab/micromissiles-unity/blob/master/Assets/Scripts/Managers/InputManager.cs): Handles user input and interactions.
--- ---

View File

@ -1,12 +1,259 @@
# Simulation Logging # 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). 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 will be exported to For example, on Windows, the logs are exported to:
`C:\Users\<user>\AppData\LocalLow\BAMLAB\micromissiles\Telemetry`.
On MacOS, the logs will be exported to ```
`~/Library/Application Support/BAMLAB/micromissiles/Telemetry`. C:\Users\<user>\AppData\LocalLow\BAMLAB\micromissiles\Telemetry\Logs
```
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.

After

Width:  |  Height:  |  Size: 254 KiB

41
docs/index.md Normal file
View File

@ -0,0 +1,41 @@
---
# 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 Normal file

File diff suppressed because it is too large Load Diff

11
package.json Normal file
View File

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