Compare commits
51 Commits
more-targe
...
master
Author | SHA1 | Date |
---|---|---|
Daniel Lovell | 54c7e5fbc7 | |
Daniel Lovell | 256f845c18 | |
Daniel Lovell | ae5798081a | |
Daniel Lovell | eeb82a9539 | |
Daniel Lovell | 1cec2fab7d | |
Daniel Lovell | ecfdb490ae | |
Daniel Lovell | 45cdf740b1 | |
Daniel Lovell | 090c3168ec | |
Daniel Lovell | 4768cffe8a | |
Daniel Lovell | 9c5dfe9b07 | |
Daniel Lovell | f13103a9e5 | |
Daniel Lovell | 4b72ed9c27 | |
Daniel Lovell | 6c88a881f2 | |
Daniel Lovell | 3e59ede2ed | |
Daniel Lovell | aba01e9a4e | |
Daniel Lovell | 05ef40aa94 | |
Daniel Lovell | 4a79b77b41 | |
Daniel Lovell | 494cb8591f | |
Daniel Lovell | dc811e0185 | |
Daniel Lovell | 78b349610f | |
Daniel Lovell | 6ada38fb73 | |
Daniel Lovell | 269fc479cc | |
Daniel Lovell | 99d617f142 | |
Daniel Lovell | 05400f318a | |
Daniel Lovell | 17987c2c5f | |
Daniel Lovell | 0c7d69d632 | |
Daniel Lovell | 2d97112e51 | |
Daniel Lovell | 64b542a866 | |
Daniel Lovell | 9bd3e513ca | |
Daniel Lovell | fab0c769d1 | |
Daniel Lovell | f4faafcc24 | |
Daniel Lovell | 46fe48afdb | |
Daniel Lovell | 91587afce5 | |
Daniel Lovell | 3293d65ccb | |
Daniel Lovell | f6d1935694 | |
Daniel Lovell | 07838e79b4 | |
Daniel Lovell | 4cf8567d91 | |
Daniel Lovell | 90cc1d29f8 | |
Daniel Lovell | 2174379fb1 | |
Daniel Lovell | 611dcb8365 | |
Daniel Lovell | 4fb40a2bc6 | |
Daniel Lovell | 4882caea45 | |
Daniel Lovell | e9d3f2b388 | |
Daniel Lovell | 765f3983f3 | |
Daniel Lovell | 474c322b35 | |
Daniel Lovell | 9a569e6415 | |
Daniel Lovell | 1dcaf2d245 | |
Daniel Lovell | 156960bae5 | |
Daniel Lovell | f701e34863 | |
Daniel Lovell | 32f3b0aa7a | |
Daniel Lovell | e15b70fafc |
|
@ -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'
|
||||||
|
|
|
@ -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
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -81,3 +81,5 @@ crashlytics-build.properties
|
||||||
|
|
||||||
# Telemetry Logs
|
# Telemetry Logs
|
||||||
Telemetry/Logs/
|
Telemetry/Logs/
|
||||||
|
|
||||||
|
node_modules/
|
|
@ -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: []
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: f034d27b4aab67a47865af3d624c4375
|
guid: b00bb4a2208bd164392fce8408d145e7
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: bd2143c1edb61ab4ab876add0f4ab9f2
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0f46139bb9c99e9499af0597ad648f3e
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8accc326a00806044a0099b7d98eb9cc
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -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}
|
||||||
|
|
|
@ -33,12 +33,12 @@ 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(
|
||||||
|
@ -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();
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
@ -140,3 +141,4 @@ public class GenerateCone : EditorWindow {
|
||||||
return sum / vertices.Count;
|
return sum / vertices.Count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
|
@ -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;
|
||||||
|
|
||||||
private List<Interceptor> _interceptors;
|
[SerializeField]
|
||||||
|
private List<ThreatData> _threatTable = new List<ThreatData>();
|
||||||
|
private Dictionary<Threat, ThreatData> _threatDataMap = new Dictionary<Threat, ThreatData>();
|
||||||
|
|
||||||
private List<Vessel> _vessels;
|
private List<Interceptor> _assignmentQueue = new List<Interceptor>();
|
||||||
|
|
||||||
public delegate void RegisterNewThreatDelegate(Threat threat);
|
private void Awake() {
|
||||||
public event RegisterNewThreatDelegate OnRegisterNewThreat;
|
if (Instance == null) {
|
||||||
|
Instance = this;
|
||||||
|
} else {
|
||||||
|
Destroy(gameObject);
|
||||||
|
}
|
||||||
|
|
||||||
void Start() {
|
}
|
||||||
_threats = new List<Threat>();
|
|
||||||
|
private void Start() {
|
||||||
|
SimManager.Instance.OnSimulationEnded += RegisterSimulationEnded;
|
||||||
|
SimManager.Instance.OnNewThreat += RegisterNewThreat;
|
||||||
|
SimManager.Instance.OnNewInterceptor += RegisterNewInterceptor;
|
||||||
|
_assignmentScheme = new ThreatAssignment();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LateUpdate() {
|
||||||
|
if (_assignmentQueue.Count > 0) {
|
||||||
|
AssignInterceptorsToThreats(_assignmentQueue);
|
||||||
|
_assignmentQueue.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RequestThreatAssignment(List<Interceptor> interceptors) {
|
||||||
|
_assignmentQueue.AddRange(interceptors);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RequestThreatAssignment(Interceptor interceptor) {
|
||||||
|
_assignmentQueue.Add(interceptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Assigns the specified list of missiles to available targets based on the assignment scheme.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="missilesToAssign">The list of missiles to assign.</param>
|
||||||
|
public void AssignInterceptorsToThreats(List<Interceptor> missilesToAssign) {
|
||||||
|
// Perform the assignment
|
||||||
|
IEnumerable<IAssignment.AssignmentItem> assignments =
|
||||||
|
_assignmentScheme.Assign(missilesToAssign, _threatTable);
|
||||||
|
|
||||||
|
// Apply the assignments to the missiles
|
||||||
|
foreach (var assignment in assignments) {
|
||||||
|
assignment.Interceptor.AssignTarget(assignment.Threat);
|
||||||
|
_threatDataMap[assignment.Threat].AssignInterceptor(assignment.Interceptor);
|
||||||
|
Debug.Log($"Interceptor {assignment.Interceptor.name} assigned to threat {assignment.Threat.name}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if any interceptors were not assigned
|
||||||
|
List<Interceptor> unassignedInterceptors = missilesToAssign.Where(m => !m.HasAssignedTarget()).ToList();
|
||||||
|
|
||||||
|
if (unassignedInterceptors.Count > 0)
|
||||||
|
{
|
||||||
|
string unassignedIds = string.Join(", ", unassignedInterceptors.Select(m => m.name));
|
||||||
|
int totalInterceptors = missilesToAssign.Count;
|
||||||
|
int assignedInterceptors = totalInterceptors - unassignedInterceptors.Count;
|
||||||
|
|
||||||
|
Debug.LogWarning($"Warning: {unassignedInterceptors.Count} out of {totalInterceptors} interceptors were not assigned to any threat. " +
|
||||||
|
$"Unassigned interceptor IDs: {unassignedIds}. " +
|
||||||
|
$"Total interceptors: {totalInterceptors}, Assigned: {assignedInterceptors}, Unassigned: {unassignedInterceptors.Count}");
|
||||||
|
|
||||||
|
// Log information about the assignment scheme
|
||||||
|
Debug.Log($"Current Assignment Scheme: {_assignmentScheme.GetType().Name}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RegisterNewThreat(Threat threat) {
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: adc0c5dbdb9dc7d498b50cf9a15c2db5
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,6 @@ public class Hydra70 : Interceptor {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
SimManager.Instance.AssignInterceptorsToThreats(submunitions);
|
IADS.Instance.RequestThreatAssignment(submunitions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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:
|
||||||
|
|
|
@ -19,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>
|
||||||
|
@ -47,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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,74 +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);
|
||||||
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,27 +171,37 @@ public class SimManager : MonoBehaviour {
|
||||||
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>
|
||||||
|
@ -233,13 +218,21 @@ public class SimManager : MonoBehaviour {
|
||||||
if (threatObject == null)
|
if (threatObject == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
_threats.Add(threatObject.GetComponent<Threat>());
|
Threat threat = threatObject.GetComponent<Threat>();
|
||||||
_activeThreats.Add(threatObject.GetComponent<Threat>());
|
|
||||||
_unassignedThreats.Add(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>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,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();
|
||||||
}
|
}
|
||||||
|
@ -312,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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"name": "bamlab.micromissiles",
|
||||||
|
"rootNamespace": "",
|
||||||
|
"references": [
|
||||||
|
"GUID:6055be8ebefd69e48b49212b09b47b2f",
|
||||||
|
"GUID:c668b7a00ffe56a498ddb1fc9f96f4e6"
|
||||||
|
],
|
||||||
|
"includePlatforms": [],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": false
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: af8bba08a36038347823e2f46bdc9857
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5ab47dc725c65cb4cb1fa6948fb0c9a7
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c80e4daffe8e47e4b8e151adf0b21a86
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -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.");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b48cf6e434723a449a49186eeda32d3f
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7ddf271569a78ee4e995192d4df0ef3f
|
|
@ -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
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d15c92e585e721749b63d85007276dbe
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: fb45bee6d70202941a0bab2fe23f0ffb
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2e04183212e40bf43a2625427494a839
|
|
@ -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
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e06647a307e3eb742b52b0482094df98
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -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:
|
||||||
|
|
|
@ -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}"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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
101
README.md
|
@ -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)
|
|
|
@ -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)
|
|
|
@ -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)
|
|
@ -0,0 +1,5 @@
|
||||||
|
node_modules/
|
||||||
|
package-lock.json
|
||||||
|
.vuepress/dist/
|
||||||
|
.vitepress/cache/
|
||||||
|
.vitepress/dist/
|
|
@ -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.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
|
@ -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)
|
|
@ -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).
|
|
@ -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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
@ -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 |
|
@ -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).
|
File diff suppressed because it is too large
Load Diff
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue