commit fcdcb14e57d09ea4072344b8f55eded17cab8ea5 Author: Paul Huliganga Date: Fri May 15 10:41:41 2026 -0400 feat: initial commit — modified sdsandbox scripts for donkeycar RL training Key changes vs upstream tawnkramer/sdsandbox: - Car.cs: per-wheel OverlapSphere barrier detection, CCD mode - TcpCarHandler.cs: regen_road with RoadBuilder+PathManager fallback, set_ai_text overlay, BrakeOnUpdate support - PathManager.cs: self-intersection fix with XZ segment retry loop - RoadBuilder.cs: BoxCollider per segment, showBarrierMeshes flag - MapOverlay.cs: minimap refresh on road node position change Unity version: 6000.4.4f1 Co-Authored-By: Claude Sonnet 4.6 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..bf953dd --- /dev/null +++ b/.gitattributes @@ -0,0 +1,20 @@ +# Normalize line endings: store as LF, checkout as native +* text=auto + +# Force text for C# and Unity text files +*.cs text eol=lf +*.unity text eol=lf +*.prefab text eol=lf +*.asset text eol=lf +*.asmdef text eol=lf +*.json text eol=lf +*.yaml text eol=lf +*.md text eol=lf + +# Binary files — never diff or mangle +*.tga binary +*.png binary +*.jpg binary +*.fbx binary +*.wav binary +*.mp3 binary diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..80a4b22 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +# Unity generated +*.csproj +*.sln +*.user +*.suo +*.tmp + +# OS +.DS_Store +Thumbs.db + +# Meta files are intentionally tracked (Unity needs them for GUIDs) diff --git a/Packages/manifest.json b/Packages/manifest.json new file mode 100755 index 0000000..7b3e0a0 --- /dev/null +++ b/Packages/manifest.json @@ -0,0 +1,48 @@ +{ + "dependencies": { + "com.unity.ai.navigation": "2.0.12", + "com.unity.collab-proxy": "2.12.4", + "com.unity.ide.rider": "3.0.39", + "com.unity.ide.visualstudio": "2.0.27", + "com.unity.ide.vscode": "1.2.5", + "com.unity.multiplayer.center": "1.0.1", + "com.unity.probuilder": "6.0.9", + "com.unity.test-framework": "1.6.0", + "com.unity.timeline": "1.8.12", + "com.unity.ugui": "2.0.0", + "com.unity.modules.accessibility": "1.0.0", + "com.unity.modules.adaptiveperformance": "1.0.0", + "com.unity.modules.ai": "1.0.0", + "com.unity.modules.androidjni": "1.0.0", + "com.unity.modules.animation": "1.0.0", + "com.unity.modules.assetbundle": "1.0.0", + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.cloth": "1.0.0", + "com.unity.modules.director": "1.0.0", + "com.unity.modules.imageconversion": "1.0.0", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.particlesystem": "1.0.0", + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.physics2d": "1.0.0", + "com.unity.modules.screencapture": "1.0.0", + "com.unity.modules.terrain": "1.0.0", + "com.unity.modules.terrainphysics": "1.0.0", + "com.unity.modules.tilemap": "1.0.0", + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.uielements": "1.0.0", + "com.unity.modules.umbra": "1.0.0", + "com.unity.modules.unityanalytics": "1.0.0", + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.unitywebrequestassetbundle": "1.0.0", + "com.unity.modules.unitywebrequestaudio": "1.0.0", + "com.unity.modules.unitywebrequesttexture": "1.0.0", + "com.unity.modules.unitywebrequestwww": "1.0.0", + "com.unity.modules.vectorgraphics": "1.0.0", + "com.unity.modules.vehicles": "1.0.0", + "com.unity.modules.video": "1.0.0", + "com.unity.modules.vr": "1.0.0", + "com.unity.modules.wind": "1.0.0", + "com.unity.modules.xr": "1.0.0" + } +} diff --git a/Packages/packages-lock.json b/Packages/packages-lock.json new file mode 100755 index 0000000..de80625 --- /dev/null +++ b/Packages/packages-lock.json @@ -0,0 +1,459 @@ +{ + "dependencies": { + "com.unity.ai.navigation": { + "version": "2.0.12", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.modules.ai": "1.0.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.burst": { + "version": "1.8.29", + "depth": 3, + "source": "registry", + "dependencies": { + "com.unity.mathematics": "1.2.1", + "com.unity.modules.jsonserialize": "1.0.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.collab-proxy": { + "version": "2.12.4", + "depth": 0, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.collections": { + "version": "6.4.0", + "depth": 3, + "source": "builtin", + "dependencies": { + "com.unity.burst": "1.8.23", + "com.unity.mathematics": "1.3.2", + "com.unity.nuget.mono-cecil": "1.11.5", + "com.unity.test-framework": "1.4.6", + "com.unity.test-framework.performance": "3.0.3" + } + }, + "com.unity.ext.nunit": { + "version": "2.0.5", + "depth": 1, + "source": "builtin", + "dependencies": {} + }, + "com.unity.ide.rider": { + "version": "3.0.39", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.ext.nunit": "1.0.6" + }, + "url": "https://packages.unity.com" + }, + "com.unity.ide.visualstudio": { + "version": "2.0.27", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.test-framework": "1.1.33" + }, + "url": "https://packages.unity.com" + }, + "com.unity.ide.vscode": { + "version": "1.2.5", + "depth": 0, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.mathematics": { + "version": "1.3.3", + "depth": 3, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.multiplayer.center": { + "version": "1.0.1", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.uielements": "1.0.0" + } + }, + "com.unity.nuget.mono-cecil": { + "version": "1.11.6", + "depth": 4, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.probuilder": { + "version": "6.0.9", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.shadergraph": "17.0.3", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.physics": "1.0.0", + "com.unity.settings-manager": "1.0.3" + }, + "url": "https://packages.unity.com" + }, + "com.unity.render-pipelines.core": { + "version": "17.4.0", + "depth": 2, + "source": "builtin", + "dependencies": { + "com.unity.burst": "1.8.14", + "com.unity.mathematics": "1.3.2", + "com.unity.ugui": "2.0.0", + "com.unity.collections": "2.4.3", + "com.unity.modules.terrain": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0" + } + }, + "com.unity.searcher": { + "version": "4.9.4", + "depth": 2, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.settings-manager": { + "version": "2.1.1", + "depth": 1, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.shadergraph": { + "version": "17.4.0", + "depth": 1, + "source": "builtin", + "dependencies": { + "com.unity.render-pipelines.core": "17.4.0", + "com.unity.searcher": "4.9.3" + } + }, + "com.unity.test-framework": { + "version": "1.6.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.ext.nunit": "2.0.3", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0" + } + }, + "com.unity.test-framework.performance": { + "version": "3.4.0", + "depth": 4, + "source": "registry", + "dependencies": { + "com.unity.test-framework": "1.1.33", + "com.unity.modules.jsonserialize": "1.0.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.timeline": { + "version": "1.8.12", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.director": "1.0.0", + "com.unity.modules.animation": "1.0.0", + "com.unity.modules.particlesystem": "1.0.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.ugui": { + "version": "2.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.imgui": "1.0.0" + } + }, + "com.unity.modules.accessibility": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.adaptiveperformance": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.subsystems": "1.0.0" + } + }, + "com.unity.modules.ai": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.androidjni": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.animation": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.assetbundle": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.audio": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.cloth": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics": "1.0.0" + } + }, + "com.unity.modules.director": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.animation": "1.0.0" + } + }, + "com.unity.modules.hierarchycore": { + "version": "1.0.0", + "depth": 1, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.imageconversion": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.imgui": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.jsonserialize": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.particlesystem": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.physics": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.physics2d": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.screencapture": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.imageconversion": "1.0.0" + } + }, + "com.unity.modules.subsystems": { + "version": "1.0.0", + "depth": 1, + "source": "builtin", + "dependencies": { + "com.unity.modules.jsonserialize": "1.0.0" + } + }, + "com.unity.modules.terrain": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.terrainphysics": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.terrain": "1.0.0" + } + }, + "com.unity.modules.tilemap": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics2d": "1.0.0" + } + }, + "com.unity.modules.ui": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.uielements": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.hierarchycore": "1.0.0", + "com.unity.modules.physics": "1.0.0" + } + }, + "com.unity.modules.umbra": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.unityanalytics": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0" + } + }, + "com.unity.modules.unitywebrequest": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.unitywebrequestassetbundle": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.assetbundle": "1.0.0", + "com.unity.modules.unitywebrequest": "1.0.0" + } + }, + "com.unity.modules.unitywebrequestaudio": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.audio": "1.0.0" + } + }, + "com.unity.modules.unitywebrequesttexture": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.imageconversion": "1.0.0" + } + }, + "com.unity.modules.unitywebrequestwww": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.unitywebrequestassetbundle": "1.0.0", + "com.unity.modules.unitywebrequestaudio": "1.0.0", + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.assetbundle": "1.0.0", + "com.unity.modules.imageconversion": "1.0.0" + } + }, + "com.unity.modules.vectorgraphics": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.uielements": "1.0.0", + "com.unity.modules.imageconversion": "1.0.0", + "com.unity.modules.imgui": "1.0.0" + } + }, + "com.unity.modules.vehicles": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics": "1.0.0" + } + }, + "com.unity.modules.video": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.unitywebrequest": "1.0.0" + } + }, + "com.unity.modules.vr": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.xr": "1.0.0" + } + }, + "com.unity.modules.wind": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.xr": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.subsystems": "1.0.0" + } + } + } +} diff --git a/ProjectSettings/AudioManager.asset b/ProjectSettings/AudioManager.asset new file mode 100755 index 0000000..27287fe --- /dev/null +++ b/ProjectSettings/AudioManager.asset @@ -0,0 +1,19 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!11 &1 +AudioManager: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Volume: 1 + Rolloff Scale: 1 + Doppler Factor: 1 + Default Speaker Mode: 2 + m_SampleRate: 0 + m_DSPBufferSize: 1024 + m_VirtualVoiceCount: 512 + m_RealVoiceCount: 32 + m_SpatializerPlugin: + m_AmbisonicDecoderPlugin: + m_DisableAudio: 0 + m_VirtualizeEffects: 1 + m_RequestedDSPBufferSize: 0 diff --git a/ProjectSettings/ClusterInputManager.asset b/ProjectSettings/ClusterInputManager.asset new file mode 100755 index 0000000..e7886b2 --- /dev/null +++ b/ProjectSettings/ClusterInputManager.asset @@ -0,0 +1,6 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!236 &1 +ClusterInputManager: + m_ObjectHideFlags: 0 + m_Inputs: [] diff --git a/ProjectSettings/DynamicsManager.asset b/ProjectSettings/DynamicsManager.asset new file mode 100755 index 0000000..6612c40 --- /dev/null +++ b/ProjectSettings/DynamicsManager.asset @@ -0,0 +1,37 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!55 &1 +PhysicsManager: + m_ObjectHideFlags: 0 + serializedVersion: 13 + m_Gravity: {x: 0, y: -78.48, z: 0} + m_DefaultMaterial: {fileID: 0} + m_BounceThreshold: 1 + m_DefaultMaxDepenetrationVelocity: 10 + m_SleepThreshold: 0.005 + m_DefaultContactOffset: 0.01 + m_DefaultSolverIterations: 10 + m_DefaultSolverVelocityIterations: 1 + m_QueriesHitBackfaces: 0 + m_QueriesHitTriggers: 1 + m_EnableAdaptiveForce: 0 + m_ClothInterCollisionDistance: 0.1 + m_ClothInterCollisionStiffness: 0.2 + m_ContactsGeneration: 0 + m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + m_AutoSimulation: 1 + m_AutoSyncTransforms: 0 + m_ReuseCollisionCallbacks: 0 + m_ClothInterCollisionSettingsToggle: 0 + m_ClothGravity: {x: 0, y: -9.81, z: 0} + m_ContactPairsMode: 0 + m_BroadphaseType: 0 + m_WorldBounds: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 250, y: 250, z: 250} + m_WorldSubdivisions: 8 + m_FrictionType: 0 + m_EnableEnhancedDeterminism: 0 + m_EnableUnifiedHeightmaps: 1 + m_SolverType: 0 + m_DefaultMaxAngularSpeed: 50 diff --git a/ProjectSettings/EditorBuildSettings.asset b/ProjectSettings/EditorBuildSettings.asset new file mode 100755 index 0000000..e0eb87e --- /dev/null +++ b/ProjectSettings/EditorBuildSettings.asset @@ -0,0 +1,41 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1045 &1 +EditorBuildSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Scenes: + - enabled: 1 + path: Assets/Scenes/menu.unity + guid: 1ac0872dcad57c44496bb684aa1998f8 + - enabled: 1 + path: Assets/Scenes/generated_road.unity + guid: 80ac5eef166784810a0ab8e6742f8a30 + - enabled: 1 + path: Assets/Scenes/warehouse.unity + guid: 4483f94080df5d84f9e98fdae085f167 + - enabled: 1 + path: Assets/Scenes/sparkfun_avc.unity + guid: b362fdcb28a444fa5b719f21d5115c5a + - enabled: 1 + path: Assets/Scenes/generated_track.unity + guid: b003bdd73179fe046b15d779a91c20e8 + - enabled: 1 + path: Assets/Scenes/roboracingleague_1.unity + guid: b37ac42fbc7419143a38115aec8b91ba + - enabled: 1 + path: Assets/Scenes/waveshare.unity + guid: c5d126de1fd7ec9468c8765592f5f783 + - enabled: 1 + path: Assets/Scenes/mini_monaco.unity + guid: 66f33ad92e768c045a184401f8a0fd4c + - enabled: 1 + path: Assets/Scenes/warren.unity + guid: a65aa29f80d4f97458c095f4838a8d36 + - enabled: 1 + path: Assets/Scenes/circuit_launch.unity + guid: 574a0b2d2b3bf6641bf9a81ed0cb3e1d + - enabled: 1 + path: Assets/Scenes/mountain_track.unity + guid: fa958af1d4fc8ef449b0c7b710692aad + m_configObjects: {} diff --git a/ProjectSettings/EditorSettings.asset b/ProjectSettings/EditorSettings.asset new file mode 100755 index 0000000..995f735 --- /dev/null +++ b/ProjectSettings/EditorSettings.asset @@ -0,0 +1,35 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!159 &1 +EditorSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_ExternalVersionControlSupport: Hidden Meta Files + m_SerializationMode: 2 + m_LineEndingsForNewScripts: 1 + m_DefaultBehaviorMode: 0 + m_PrefabRegularEnvironment: {fileID: 0} + m_PrefabUIEnvironment: {fileID: 0} + m_SpritePackerMode: 0 + m_SpritePackerPaddingPower: 1 + m_EtcTextureCompressorBehavior: 1 + m_EtcTextureFastCompressor: 1 + m_EtcTextureNormalCompressor: 2 + m_EtcTextureBestCompressor: 4 + m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd;asmdef;asmref;rsp + m_ProjectGenerationRootNamespace: + m_CollabEditorSettings: + inProgressEnabled: 1 + m_EnableTextureStreamingInEditMode: 1 + m_EnableTextureStreamingInPlayMode: 1 + m_AsyncShaderCompilation: 1 + m_EnterPlayModeOptionsEnabled: 0 + m_EnterPlayModeOptions: 3 + m_ShowLightmapResolutionOverlay: 1 + m_UseLegacyProbeSampleCount: 1 + m_AssetPipelineMode: 1 + m_CacheServerMode: 0 + m_CacheServerEndpoint: + m_CacheServerNamespacePrefix: default + m_CacheServerEnableDownload: 1 + m_CacheServerEnableUpload: 1 diff --git a/ProjectSettings/GraphicsSettings.asset b/ProjectSettings/GraphicsSettings.asset new file mode 100755 index 0000000..ba0e96c --- /dev/null +++ b/ProjectSettings/GraphicsSettings.asset @@ -0,0 +1,69 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!30 &1 +GraphicsSettings: + m_ObjectHideFlags: 0 + serializedVersion: 13 + m_Deferred: + m_Mode: 1 + m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0} + m_DeferredReflections: + m_Mode: 1 + m_Shader: {fileID: 74, guid: 0000000000000000f000000000000000, type: 0} + m_ScreenSpaceShadows: + m_Mode: 1 + m_Shader: {fileID: 64, guid: 0000000000000000f000000000000000, type: 0} + m_LegacyDeferred: + m_Mode: 1 + m_Shader: {fileID: 63, guid: 0000000000000000f000000000000000, type: 0} + m_DepthNormals: + m_Mode: 1 + m_Shader: {fileID: 62, guid: 0000000000000000f000000000000000, type: 0} + m_MotionVectors: + m_Mode: 1 + m_Shader: {fileID: 75, guid: 0000000000000000f000000000000000, type: 0} + m_LightHalo: + m_Mode: 1 + m_Shader: {fileID: 105, guid: 0000000000000000f000000000000000, type: 0} + m_LensFlare: + m_Mode: 1 + m_Shader: {fileID: 102, guid: 0000000000000000f000000000000000, type: 0} + m_VideoShadersIncludeMode: 2 + m_AlwaysIncludedShaders: + - {fileID: 7, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 15104, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 15105, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 15106, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 10783, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 16000, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 16001, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 17000, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 16002, guid: 0000000000000000f000000000000000, type: 0} + m_PreloadedShaders: [] + m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000, + type: 0} + m_CustomRenderPipeline: {fileID: 0} + m_TransparencySortMode: 0 + m_TransparencySortAxis: {x: 0, y: 0, z: 1} + m_DefaultRenderingPath: 1 + m_DefaultMobileRenderingPath: 1 + m_TierSettings: [] + m_LightmapStripping: 0 + m_FogStripping: 0 + m_InstancingStripping: 0 + m_LightmapKeepPlain: 1 + m_LightmapKeepDirCombined: 1 + m_LightmapKeepDynamicPlain: 1 + m_LightmapKeepDynamicDirCombined: 1 + m_LightmapKeepShadowMask: 1 + m_LightmapKeepSubtractive: 1 + m_FogKeepLinear: 1 + m_FogKeepExp: 1 + m_FogKeepExp2: 1 + m_AlbedoSwatchInfos: [] + m_LightsUseLinearIntensity: 0 + m_LightsUseColorTemperature: 0 + m_DefaultRenderingLayerMask: 1 + m_LogWhenShaderIsCompiled: 0 diff --git a/ProjectSettings/InputManager.asset b/ProjectSettings/InputManager.asset new file mode 100755 index 0000000..b16147e --- /dev/null +++ b/ProjectSettings/InputManager.asset @@ -0,0 +1,487 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!13 &1 +InputManager: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Axes: + - serializedVersion: 3 + m_Name: Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: left + positiveButton: right + altNegativeButton: a + altPositiveButton: d + gravity: 3 + dead: 0.001 + sensitivity: 3 + snap: 1 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: down + positiveButton: up + altNegativeButton: s + altPositiveButton: w + gravity: 3 + dead: 0.001 + sensitivity: 3 + snap: 1 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire1 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left ctrl + altNegativeButton: + altPositiveButton: mouse 0 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire2 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left alt + altNegativeButton: + altPositiveButton: mouse 1 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire3 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left shift + altNegativeButton: + altPositiveButton: mouse 2 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Jump + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: space + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse X + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse Y + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 1 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse ScrollWheel + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 2 + joyNum: 0 + - serializedVersion: 3 + m_Name: Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0.19 + sensitivity: 1 + snap: 0 + invert: 0 + type: 2 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0.19 + sensitivity: 1 + snap: 0 + invert: 1 + type: 2 + axis: 1 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire1 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 0 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire2 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 1 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire3 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 2 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Jump + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 3 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Submit + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: return + altNegativeButton: + altPositiveButton: joystick button 0 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Submit + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: enter + altNegativeButton: + altPositiveButton: space + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Cancel + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: escape + altNegativeButton: + altPositiveButton: joystick button 1 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Enable Debug Button 1 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left ctrl + altNegativeButton: + altPositiveButton: joystick button 8 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Enable Debug Button 2 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: backspace + altNegativeButton: + altPositiveButton: joystick button 9 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Reset + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left alt + altNegativeButton: + altPositiveButton: joystick button 1 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Next + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: page down + altNegativeButton: + altPositiveButton: joystick button 5 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Previous + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: page up + altNegativeButton: + altPositiveButton: joystick button 4 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Validate + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: return + altNegativeButton: + altPositiveButton: joystick button 0 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Persistent + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: right shift + altNegativeButton: + altPositiveButton: joystick button 2 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Multiplier + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left shift + altNegativeButton: + altPositiveButton: joystick button 3 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: left + positiveButton: right + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: down + positiveButton: up + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: down + positiveButton: up + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 2 + axis: 6 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: left + positiveButton: right + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 2 + axis: 5 + joyNum: 0 diff --git a/ProjectSettings/MemorySettings.asset b/ProjectSettings/MemorySettings.asset new file mode 100755 index 0000000..517e60e --- /dev/null +++ b/ProjectSettings/MemorySettings.asset @@ -0,0 +1,35 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!387306366 &1 +MemorySettings: + m_ObjectHideFlags: 0 + m_EditorMemorySettings: + m_MainAllocatorBlockSize: -1 + m_ThreadAllocatorBlockSize: -1 + m_MainGfxBlockSize: -1 + m_ThreadGfxBlockSize: -1 + m_CacheBlockSize: -1 + m_TypetreeBlockSize: -1 + m_ProfilerBlockSize: -1 + m_ProfilerEditorBlockSize: -1 + m_BucketAllocatorGranularity: -1 + m_BucketAllocatorBucketsCount: -1 + m_BucketAllocatorBlockSize: -1 + m_BucketAllocatorBlockCount: -1 + m_ProfilerBucketAllocatorGranularity: -1 + m_ProfilerBucketAllocatorBucketsCount: -1 + m_ProfilerBucketAllocatorBlockSize: -1 + m_ProfilerBucketAllocatorBlockCount: -1 + m_TempAllocatorSizeMain: -1 + m_JobTempAllocatorBlockSize: -1 + m_BackgroundJobTempAllocatorBlockSize: -1 + m_JobTempAllocatorReducedBlockSize: -1 + m_TempAllocatorSizeGIBakingWorker: -1 + m_TempAllocatorSizeNavMeshWorker: -1 + m_TempAllocatorSizeAudioWorker: -1 + m_TempAllocatorSizeCloudWorker: -1 + m_TempAllocatorSizeGfx: -1 + m_TempAllocatorSizeJobWorker: -1 + m_TempAllocatorSizeBackgroundWorker: -1 + m_TempAllocatorSizePreloadManager: -1 + m_PlatformMemorySettings: [] diff --git a/ProjectSettings/MultiplayerManager.asset b/ProjectSettings/MultiplayerManager.asset new file mode 100755 index 0000000..c19bcd7 --- /dev/null +++ b/ProjectSettings/MultiplayerManager.asset @@ -0,0 +1,9 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!655991488 &1 +MultiplayerManager: + m_ObjectHideFlags: 0 + m_EnableMultiplayerRoles: 0 + m_EnablePlayModeLocalDeployment: 0 + m_EnablePlayModeRemoteDeployment: 0 + m_StrippingTypes: {} diff --git a/ProjectSettings/NavMeshAreas.asset b/ProjectSettings/NavMeshAreas.asset new file mode 100755 index 0000000..9c07ef5 --- /dev/null +++ b/ProjectSettings/NavMeshAreas.asset @@ -0,0 +1,91 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!126 &1 +NavMeshProjectSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + areas: + - name: Walkable + cost: 1 + - name: Not Walkable + cost: 1 + - name: Jump + cost: 2 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + m_LastAgentTypeID: -887442657 + m_Settings: + - serializedVersion: 2 + agentTypeID: 0 + agentRadius: 2 + agentHeight: 1 + agentSlope: 60 + agentClimb: 0.2 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + debug: + m_Flags: 0 + m_SettingNames: + - Car diff --git a/ProjectSettings/PackageManagerSettings.asset b/ProjectSettings/PackageManagerSettings.asset new file mode 100755 index 0000000..be4a797 --- /dev/null +++ b/ProjectSettings/PackageManagerSettings.asset @@ -0,0 +1,43 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &1 +MonoBehaviour: + m_ObjectHideFlags: 61 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 13964, guid: 0000000000000000e000000000000000, type: 0} + m_Name: + m_EditorClassIdentifier: + m_EnablePreviewPackages: 0 + m_EnablePackageDependencies: 0 + m_AdvancedSettingsExpanded: 1 + m_ScopedRegistriesSettingsExpanded: 1 + oneTimeWarningShown: 0 + m_Registries: + - m_Id: main + m_Name: + m_Url: https://packages.unity.com + m_Scopes: [] + m_IsDefault: 1 + m_Capabilities: 7 + m_UserSelectedRegistryName: + m_UserAddingNewScopedRegistry: 0 + m_RegistryInfoDraft: + m_ErrorMessage: + m_Original: + m_Id: + m_Name: + m_Url: + m_Scopes: [] + m_IsDefault: 0 + m_Capabilities: 0 + m_Modified: 0 + m_Name: + m_Url: + m_Scopes: + - + m_SelectedScopeIndex: 0 diff --git a/ProjectSettings/Packages/com.unity.probuilder/Settings.json b/ProjectSettings/Packages/com.unity.probuilder/Settings.json new file mode 100755 index 0000000..5fa09d7 --- /dev/null +++ b/ProjectSettings/Packages/com.unity.probuilder/Settings.json @@ -0,0 +1,306 @@ +{ + "m_Dictionary": { + "m_DictionaryValues": [ + { + "type": "UnityEngine.ProBuilder.LogLevel, Unity.ProBuilder, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", + "key": "log.level", + "value": "{\"m_Value\":3}" + }, + { + "type": "UnityEngine.ProBuilder.LogOutput, Unity.ProBuilder, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", + "key": "log.output", + "value": "{\"m_Value\":1}" + }, + { + "type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "log.path", + "value": "{\"m_Value\":\"ProBuilderLog.txt\"}" + }, + { + "type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "editor.materialPalettePath", + "value": "{\"m_Value\":\"Assets/ProBuilder Data/Default Material Palette.asset\"}" + }, + { + "type": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "VertexColorPalette.previousColorPalette", + "value": "{\"m_Value\":\"Assets/ProBuilder Data/Default Color Palette.asset\"}" + }, + { + "type": "UnityEngine.ProBuilder.SemVer, Unity.ProBuilder, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", + "key": "about.identifier", + "value": "{\"m_Value\":{\"m_Major\":6,\"m_Minor\":0,\"m_Patch\":9,\"m_Build\":-1,\"m_Type\":\"\",\"m_Metadata\":\"\",\"m_Date\":\"\"}}" + }, + { + "type": "UnityEngine.ProBuilder.SemVer, Unity.ProBuilder, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", + "key": "preferences.version", + "value": "{\"m_Value\":{\"m_Major\":6,\"m_Minor\":0,\"m_Patch\":9,\"m_Build\":-1,\"m_Type\":\"\",\"m_Metadata\":\"\",\"m_Date\":\"\"}}" + }, + { + "type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "UnityEngine.ProBuilder.ProBuilderEditor-isUtilityWindow", + "value": "{\"m_Value\":false}" + }, + { + "type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "editor.backFaceSelectEnabled", + "value": "{\"m_Value\":false}" + }, + { + "type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "editor.toolbarIconGUI", + "value": "{\"m_Value\":false}" + }, + { + "type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "editor.showSceneInfo", + "value": "{\"m_Value\":false}" + }, + { + "type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "editor.showEditorNotifications", + "value": "{\"m_Value\":false}" + }, + { + "type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "meshImporter.quads", + "value": "{\"m_Value\":true}" + }, + { + "type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "meshImporter.smoothing", + "value": "{\"m_Value\":true}" + }, + { + "type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "UVEditor.showPreviewMaterial", + "value": "{\"m_Value\":true}" + }, + { + "type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "lightmapping.autoUnwrapLightmapUV", + "value": "{\"m_Value\":true}" + }, + { + "type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "mesh.newShapesSnapToGrid", + "value": "{\"m_Value\":true}" + }, + { + "type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "editor.autoRecalculateCollisions", + "value": "{\"m_Value\":false}" + }, + { + "type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "mesh.meshColliderIsConvex", + "value": "{\"m_Value\":false}" + }, + { + "type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "export.exportRecursive", + "value": "{\"m_Value\":false}" + }, + { + "type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "export.exportAsGroup", + "value": "{\"m_Value\":false}" + }, + { + "type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "export.objApplyTransform", + "value": "{\"m_Value\":false}" + }, + { + "type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "export.objExportRightHanded", + "value": "{\"m_Value\":true}" + }, + { + "type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "export.objExportCopyTextures", + "value": "{\"m_Value\":true}" + }, + { + "type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "export.objExportVertexColors", + "value": "{\"m_Value\":false}" + }, + { + "type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "export.objTextureOffsetScale", + "value": "{\"m_Value\":false}" + }, + { + "type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "export.objQuads", + "value": "{\"m_Value\":true}" + }, + { + "type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "editor.closeWindowAfterShapeCreation", + "value": "{\"m_Value\":false}" + }, + { + "type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "UnityEditor.ProBuilder.UVEditor-isUtilityWindow", + "value": "{\"m_Value\":false}" + }, + { + "type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "smoothing.showSettings", + "value": "{\"m_Value\":false}" + }, + { + "type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "smoothing.showPreview", + "value": "{\"m_Value\":false}" + }, + { + "type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "smoothing.showNormals", + "value": "{\"m_Value\":false}" + }, + { + "type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "smoothing.showHelp", + "value": "{\"m_Value\":false}" + }, + { + "type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "editor.stripProBuilderScriptsOnBuild", + "value": "{\"m_Value\":true}" + }, + { + "type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "FillHole.selectEntirePath", + "value": "{\"m_Value\":true}" + }, + { + "type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "GrowSelection.useAngle", + "value": "{\"m_Value\":true}" + }, + { + "type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "GrowSelection.iterativeGrow", + "value": "{\"m_Value\":false}" + }, + { + "type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "SelectEdgeLoop.selectIterative", + "value": "{\"m_Value\":false}" + }, + { + "type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "SelectEdgeRing.selectIterative", + "value": "{\"m_Value\":false}" + }, + { + "type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "editor.extrudeEdgesAsGroup", + "value": "{\"m_Value\":true}" + }, + { + "type": "System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "experimental.enabled", + "value": "{\"m_Value\":false}" + }, + { + "type": "UnityEngine.ProBuilder.SelectionModifierBehavior, Unity.ProBuilder, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", + "key": "editor.rectSelectModifier", + "value": "{\"m_Value\":0}" + }, + { + "type": "UnityEngine.ProBuilder.RectSelectMode, Unity.ProBuilder, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", + "key": "editor.dragSelectRectMode", + "value": "{\"m_Value\":0}" + }, + { + "type": "UnityEngine.ProBuilder.SelectMode, Unity.ProBuilder, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", + "key": "editor.selectMode", + "value": "{\"m_Value\":1}" + }, + { + "type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "meshImporter.smoothingAngle", + "value": "{\"m_Value\":4.199999809265137}" + }, + { + "type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "uv.uvEditorGridSnapIncrement", + "value": "{\"m_Value\":0.125}" + }, + { + "type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "GrowSelection.angleValue", + "value": "{\"m_Value\":15.0}" + }, + { + "type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "ExtrudeFaces.distance", + "value": "{\"m_Value\":0.5}" + }, + { + "type": "System.Single, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", + "key": "ExtrudeEdges.distance", + "value": "{\"m_Value\":0.5}" + }, + { + "type": "UnityEngine.ProBuilder.PivotLocation, Unity.ProBuilder, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", + "key": "mesh.newShapePivotLocation", + "value": "{\"m_Value\":1}" + }, + { + "type": "UnityEngine.Rendering.ShadowCastingMode, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", + "key": "mesh.shadowCastingMode", + "value": "{\"m_Value\":1}" + }, + { + "type": "UnityEngine.Material, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", + "key": "mesh.userMaterial", + "value": "{\"m_Value\":{\"instanceID\":0}}" + }, + { + "type": "UnityEditor.StaticEditorFlags, UnityEditor.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", + "key": "mesh.defaultStaticEditorFlags", + "value": "{\"m_Value\":0}" + }, + { + "type": "UnityEngine.ProBuilder.ColliderType, Unity.ProBuilder, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", + "key": "mesh.newShapeColliderType", + "value": "{\"m_Value\":2}" + }, + { + "type": "UnityEngine.ProBuilder.UnwrapParameters, Unity.ProBuilder, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", + "key": "lightmapping.defaultLightmapUnwrapParameters", + "value": "{\"m_Value\":{\"m_HardAngle\":88.0,\"m_PackMargin\":20.0,\"m_AngleError\":8.0,\"m_AreaError\":15.0}}" + }, + { + "type": "UnityEditor.ProBuilder.Actions.Export+ExportFormat, Unity.ProBuilder.Editor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", + "key": "export.format", + "value": "{\"m_Value\":0}" + }, + { + "type": "UnityEditor.ProBuilder.Actions.ExportAssetOptions, Unity.ProBuilder.Editor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", + "key": "export.assetOptions", + "value": "{\"m_Value\":{\"makePrefab\":false,\"replaceOriginal\":false}}" + }, + { + "type": "UnityEngine.Vector3, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", + "key": "MoveElements.s_Translation", + "value": "{\"m_Value\":{\"x\":0.0,\"y\":1.0,\"z\":0.0}}" + }, + { + "type": "UnityEditor.ProBuilder.Actions.OffsetElements+CoordinateSpace, Unity.ProBuilder.Editor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", + "key": "MoveElements.s_CoordinateSpace", + "value": "{\"m_Value\":0}" + }, + { + "type": "UnityEngine.ProBuilder.ExtrudeMethod, Unity.ProBuilder, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", + "key": "editor.extrudeMethod", + "value": "{\"m_Value\":2}" + } + ] + } +} \ No newline at end of file diff --git a/ProjectSettings/Packages/com.unity.settings-manager/Settings.json b/ProjectSettings/Packages/com.unity.settings-manager/Settings.json new file mode 100755 index 0000000..872dce0 --- /dev/null +++ b/ProjectSettings/Packages/com.unity.settings-manager/Settings.json @@ -0,0 +1,7 @@ +{ + "m_Name": "Settings", + "m_Path": "ProjectSettings/Packages/com.unity.settings-manager/Settings.json", + "m_Dictionary": { + "m_DictionaryValues": [] + } +} \ No newline at end of file diff --git a/ProjectSettings/Physics2DSettings.asset b/ProjectSettings/Physics2DSettings.asset new file mode 100755 index 0000000..5b7cd3d --- /dev/null +++ b/ProjectSettings/Physics2DSettings.asset @@ -0,0 +1,56 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!19 &1 +Physics2DSettings: + m_ObjectHideFlags: 0 + serializedVersion: 5 + m_Gravity: {x: 0, y: -9.81} + m_DefaultMaterial: {fileID: 0} + m_VelocityIterations: 8 + m_PositionIterations: 3 + m_VelocityThreshold: 1 + m_MaxLinearCorrection: 0.2 + m_MaxAngularCorrection: 8 + m_MaxTranslationSpeed: 100 + m_MaxRotationSpeed: 360 + m_BaumgarteScale: 0.2 + m_BaumgarteTimeOfImpactScale: 0.75 + m_TimeToSleep: 0.5 + m_LinearSleepTolerance: 0.01 + m_AngularSleepTolerance: 2 + m_DefaultContactOffset: 0.01 + m_JobOptions: + serializedVersion: 2 + useMultithreading: 1 + useConsistencySorting: 0 + m_InterpolationPosesPerJob: 100 + m_NewContactsPerJob: 30 + m_CollideContactsPerJob: 100 + m_ClearFlagsPerJob: 200 + m_ClearBodyForcesPerJob: 200 + m_SyncDiscreteFixturesPerJob: 50 + m_SyncContinuousFixturesPerJob: 50 + m_FindNearestContactsPerJob: 100 + m_UpdateTriggerContactsPerJob: 100 + m_IslandSolverCostThreshold: 100 + m_IslandSolverBodyCostScale: 1 + m_IslandSolverContactCostScale: 10 + m_IslandSolverJointCostScale: 10 + m_IslandSolverBodiesPerJob: 50 + m_IslandSolverContactsPerJob: 50 + m_SimulationMode: 0 + m_QueriesHitTriggers: 1 + m_QueriesStartInColliders: 1 + m_CallbacksOnDisable: 1 + m_ReuseCollisionCallbacks: 0 + m_AutoSyncTransforms: 0 + m_AlwaysShowColliders: 0 + m_ShowColliderSleep: 1 + m_ShowColliderContacts: 0 + m_ShowColliderAABB: 0 + m_ContactArrowScale: 0.2 + m_ColliderAwakeColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.7529412} + m_ColliderAsleepColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.36078432} + m_ColliderContactColor: {r: 1, g: 0, b: 1, a: 0.6862745} + m_ColliderAABBColor: {r: 1, g: 1, b: 0, a: 0.2509804} + m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/ProjectSettings/PresetManager.asset b/ProjectSettings/PresetManager.asset new file mode 100755 index 0000000..67a94da --- /dev/null +++ b/ProjectSettings/PresetManager.asset @@ -0,0 +1,7 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1386491679 &1 +PresetManager: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_DefaultPresets: {} diff --git a/ProjectSettings/ProjectSettings.asset b/ProjectSettings/ProjectSettings.asset new file mode 100755 index 0000000..c465706 --- /dev/null +++ b/ProjectSettings/ProjectSettings.asset @@ -0,0 +1,766 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!129 &1 +PlayerSettings: + m_ObjectHideFlags: 0 + serializedVersion: 28 + productGUID: 14389bd05b800ddd78609991ec8e5684 + AndroidProfiler: 0 + AndroidFilterTouchesWhenObscured: 0 + AndroidEnableSustainedPerformanceMode: 0 + defaultScreenOrientation: 4 + targetDevice: 2 + useOnDemandResources: 0 + accelerometerFrequency: 60 + companyName: DonkeyCar + productName: donkey_sim + defaultCursor: {fileID: 0} + cursorHotspot: {x: 0, y: 0} + m_SplashScreenBackgroundColor: {r: 0.13725491, g: 0.12156863, b: 0.1254902, a: 1} + m_ShowUnitySplashScreen: 1 + m_ShowUnitySplashLogo: 1 + m_SplashScreenOverlayOpacity: 1 + m_SplashScreenAnimation: 1 + m_SplashScreenLogoStyle: 1 + m_SplashScreenDrawMode: 0 + m_SplashScreenBackgroundAnimationZoom: 1 + m_SplashScreenLogoAnimationZoom: 1 + m_SplashScreenBackgroundLandscapeAspect: 1 + m_SplashScreenBackgroundPortraitAspect: 1 + m_SplashScreenBackgroundLandscapeUvs: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + m_SplashScreenBackgroundPortraitUvs: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + m_SplashScreenLogos: [] + m_VirtualRealitySplashScreen: {fileID: 0} + defaultScreenWidth: 1920 + defaultScreenHeight: 1127 + defaultScreenWidthWeb: 960 + defaultScreenHeightWeb: 600 + m_StereoRenderingPath: 0 + m_ActiveColorSpace: 1 + unsupportedMSAAFallback: 0 + m_SpriteBatchMaxVertexCount: 65535 + m_SpriteBatchVertexThreshold: 300 + m_MTRendering: 1 + mipStripping: 0 + numberOfMipsStripped: 0 + numberOfMipsStrippedPerMipmapLimitGroup: {} + m_StackTraceTypes: 010000000100000001000000010000000100000001000000 + iosShowActivityIndicatorOnLoading: -1 + androidShowActivityIndicatorOnLoading: -1 + iosUseCustomAppBackgroundBehavior: 0 + allowedAutorotateToPortrait: 1 + allowedAutorotateToPortraitUpsideDown: 1 + allowedAutorotateToLandscapeRight: 1 + allowedAutorotateToLandscapeLeft: 1 + useOSAutorotation: 1 + use32BitDisplayBuffer: 1 + preserveFramebufferAlpha: 0 + disableDepthAndStencilBuffers: 0 + androidStartInFullscreen: 1 + androidRenderOutsideSafeArea: 1 + androidUseSwappy: 0 + androidDisplayOptions: 1 + androidBlitType: 0 + androidResizeableActivity: 1 + androidDefaultWindowWidth: 1920 + androidDefaultWindowHeight: 1080 + androidMinimumWindowWidth: 400 + androidMinimumWindowHeight: 300 + androidFullscreenMode: 1 + androidAutoRotationBehavior: 1 + androidPredictiveBackSupport: 1 + androidApplicationEntry: 1 + defaultIsNativeResolution: 1 + macRetinaSupport: 1 + runInBackground: 1 + muteOtherAudioSources: 0 + Prepare IOS For Recording: 0 + Force IOS Speakers When Recording: 0 + audioSpatialExperience: 0 + deferSystemGesturesMode: 0 + hideHomeButton: 0 + submitAnalytics: 1 + usePlayerLog: 1 + dedicatedServerOptimizations: 1 + bakeCollisionMeshes: 0 + forceSingleInstance: 0 + useFlipModelSwapchain: 1 + resizableWindow: 1 + useMacAppStoreValidation: 0 + macAppStoreCategory: public.app-category.games + gpuSkinning: 0 + meshDeformation: 0 + xboxPIXTextureCapture: 0 + xboxEnableAvatar: 0 + xboxEnableKinect: 0 + xboxEnableKinectAutoTracking: 0 + xboxEnableFitness: 0 + visibleInBackground: 1 + allowFullscreenSwitch: 1 + fullscreenMode: 3 + xboxSpeechDB: 0 + xboxEnableHeadOrientation: 0 + xboxEnableGuest: 0 + xboxEnablePIXSampling: 0 + metalFramebufferOnly: 0 + metalUseMetalDisplayLink: 0 + xboxOneResolution: 0 + xboxOneSResolution: 0 + xboxOneXResolution: 3 + xboxOneMonoLoggingLevel: 0 + xboxOneLoggingLevel: 1 + xboxOneDisableEsram: 0 + xboxOneEnableTypeOptimization: 0 + xboxOnePresentImmediateThreshold: 0 + switchQueueCommandMemory: 1048576 + switchQueueControlMemory: 16384 + switchQueueComputeMemory: 262144 + switchNVNShaderPoolsGranularity: 33554432 + switchNVNDefaultPoolsGranularity: 16777216 + switchNVNOtherPoolsGranularity: 16777216 + switchGpuScratchPoolGranularity: 2097152 + switchAllowGpuScratchShrinking: 0 + switchNVNMaxPublicTextureIDCount: 0 + switchNVNMaxPublicSamplerIDCount: 0 + switchMaxWorkerMultiple: 8 + switchNVNGraphicsFirmwareMemory: 32 + switchGraphicsJobsSyncAfterKick: 1 + vulkanNumSwapchainBuffers: 3 + vulkanEnableSetSRGBWrite: 0 + vulkanEnablePreTransform: 0 + vulkanEnableLateAcquireNextImage: 0 + vulkanEnableCommandBufferRecycling: 1 + loadStoreDebugModeEnabled: 0 + visionOSBundleVersion: 1.0 + tvOSBundleVersion: 1.0 + bundleVersion: 1.0 + preloadedAssets: [] + metroInputSource: 0 + wsaTransparentSwapchain: 0 + xboxOneDisableKinectGpuReservation: 1 + xboxOneEnable7thCore: 1 + vrSettings: + enable360StereoCapture: 0 + enableFrameTimingStats: 0 + enableOpenGLProfilerGPURecorders: 1 + allowHDRDisplaySupport: 0 + useHDRDisplay: 0 + hdrBitDepth: 0 + m_ColorGamuts: 00000000 + targetPixelDensity: 30 + resolutionScalingMode: 0 + resetResolutionOnWindowResize: 0 + androidSupportedAspectRatio: 1 + androidMaxAspectRatio: 2.1 + androidMinAspectRatio: 1 + applicationIdentifier: + Standalone: com.DonkeyCar.donkeysim + buildNumber: + Standalone: 0 + VisionOS: 0 + iPhone: 0 + tvOS: 0 + overrideDefaultApplicationIdentifier: 0 + AndroidBundleVersionCode: 1 + AndroidMinSdkVersion: 25 + AndroidTargetSdkVersion: 0 + AndroidPreferredInstallLocation: 1 + AndroidPreferredDataLocation: 1 + aotOptions: + stripEngineCode: 1 + iPhoneStrippingLevel: 0 + iPhoneScriptCallOptimization: 0 + ForceInternetPermission: 0 + ForceSDCardPermission: 0 + CreateWallpaper: 0 + androidSplitApplicationBinary: 0 + keepLoadedShadersAlive: 0 + StripUnusedMeshComponents: 0 + strictShaderVariantMatching: 0 + VertexChannelCompressionMask: 4054 + iPhoneSdkVersion: 988 + iOSSimulatorArchitecture: 0 + iOSTargetOSVersionString: 15.0 + tvOSSdkVersion: 0 + tvOSSimulatorArchitecture: 0 + tvOSRequireExtendedGameController: 0 + tvOSTargetOSVersionString: 15.0 + VisionOSSdkVersion: 0 + VisionOSTargetOSVersionString: 1.0 + uIPrerenderedIcon: 0 + uIRequiresPersistentWiFi: 0 + uIRequiresFullScreen: 1 + uIStatusBarHidden: 1 + uIExitOnSuspend: 0 + uIStatusBarStyle: 0 + appleTVSplashScreen: {fileID: 0} + appleTVSplashScreen2x: {fileID: 0} + tvOSSmallIconLayers: [] + tvOSSmallIconLayers2x: [] + tvOSLargeIconLayers: [] + tvOSLargeIconLayers2x: [] + tvOSTopShelfImageLayers: [] + tvOSTopShelfImageLayers2x: [] + tvOSTopShelfImageWideLayers: [] + tvOSTopShelfImageWideLayers2x: [] + iOSLaunchScreenType: 0 + iOSLaunchScreenPortrait: {fileID: 0} + iOSLaunchScreenLandscape: {fileID: 0} + iOSLaunchScreenBackgroundColor: + serializedVersion: 2 + rgba: 0 + iOSLaunchScreenFillPct: 100 + iOSLaunchScreenSize: 100 + iOSLaunchScreeniPadType: 0 + iOSLaunchScreeniPadImage: {fileID: 0} + iOSLaunchScreeniPadBackgroundColor: + serializedVersion: 2 + rgba: 0 + iOSLaunchScreeniPadFillPct: 100 + iOSLaunchScreeniPadSize: 100 + iOSLaunchScreenCustomStoryboardPath: + iOSLaunchScreeniPadCustomStoryboardPath: + iOSDeviceRequirements: [] + iOSURLSchemes: [] + macOSURLSchemes: [] + iOSBackgroundModes: 0 + iOSMetalForceHardShadows: 0 + metalEditorSupport: 1 + metalAPIValidation: 1 + metalCompileShaderBinary: 0 + iOSRenderExtraFrameOnPause: 0 + iosCopyPluginsCodeInsteadOfSymlink: 0 + appleDeveloperTeamID: + iOSManualSigningProvisioningProfileID: + tvOSManualSigningProvisioningProfileID: + VisionOSManualSigningProvisioningProfileID: + iOSManualSigningProvisioningProfileType: 0 + tvOSManualSigningProvisioningProfileType: 0 + VisionOSManualSigningProvisioningProfileType: 0 + appleEnableAutomaticSigning: 0 + iOSRequireARKit: 0 + iOSAutomaticallyDetectAndAddCapabilities: 1 + appleEnableProMotion: 0 + shaderPrecisionModel: 0 + clonedFromGUID: 00000000000000000000000000000000 + templatePackageId: + templateDefaultScene: + useCustomMainManifest: 0 + useCustomLauncherManifest: 0 + useCustomMainGradleTemplate: 0 + useCustomLauncherGradleManifest: 0 + useCustomBaseGradleTemplate: 0 + useCustomGradlePropertiesTemplate: 0 + useCustomGradleSettingsTemplate: 0 + useCustomProguardFile: 0 + AndroidTargetArchitectures: 1 + AndroidAllowedArchitectures: -1 + AndroidSplashScreenScale: 0 + androidSplashScreen: {fileID: 0} + AndroidKeystoreName: + AndroidKeyaliasName: + AndroidEnableArmv9SecurityFeatures: 0 + AndroidEnableArm64MTE: 0 + AndroidBuildApkPerCpuArchitecture: 0 + AndroidTVCompatibility: 0 + AndroidIsGame: 1 + androidAppCategory: 3 + useAndroidAppCategory: 1 + androidAppCategoryOther: + AndroidEnableTango: 0 + androidEnableBanner: 1 + androidUseLowAccuracyLocation: 0 + androidUseCustomKeystore: 0 + m_AndroidBanners: + - width: 320 + height: 180 + banner: {fileID: 0} + androidGamepadSupportLevel: 0 + AndroidMinifyRelease: 0 + AndroidMinifyDebug: 0 + AndroidValidateAppBundleSize: 1 + AndroidAppBundleSizeToValidate: 150 + AndroidReportGooglePlayAppDependencies: 1 + androidSymbolsSizeThreshold: 800 + m_BuildTargetIcons: + - m_BuildTarget: + m_Icons: + - serializedVersion: 2 + m_Icon: {fileID: 2800000, guid: 5a980be9821c24f4ba4fd728e5fa23e3, type: 3} + m_Width: 128 + m_Height: 128 + m_Kind: 0 + m_BuildTargetPlatformIcons: [] + m_BuildTargetBatching: [] + m_BuildTargetShaderSettings: [] + m_BuildTargetGraphicsJobs: + - m_BuildTarget: MacStandaloneSupport + m_GraphicsJobs: 1 + - m_BuildTarget: Switch + m_GraphicsJobs: 0 + - m_BuildTarget: MetroSupport + m_GraphicsJobs: 0 + - m_BuildTarget: AppleTVSupport + m_GraphicsJobs: 0 + - m_BuildTarget: BJMSupport + m_GraphicsJobs: 0 + - m_BuildTarget: LinuxStandaloneSupport + m_GraphicsJobs: 1 + - m_BuildTarget: PS4Player + m_GraphicsJobs: 0 + - m_BuildTarget: iOSSupport + m_GraphicsJobs: 0 + - m_BuildTarget: WindowsStandaloneSupport + m_GraphicsJobs: 1 + - m_BuildTarget: XboxOnePlayer + m_GraphicsJobs: 0 + - m_BuildTarget: LuminSupport + m_GraphicsJobs: 0 + - m_BuildTarget: AndroidPlayer + m_GraphicsJobs: 0 + - m_BuildTarget: WebGLSupport + m_GraphicsJobs: 0 + m_BuildTargetGraphicsJobMode: + - m_BuildTarget: PS4Player + m_GraphicsJobMode: 0 + - m_BuildTarget: XboxOnePlayer + m_GraphicsJobMode: 0 + m_BuildTargetGraphicsAPIs: + - m_BuildTarget: iOSSupport + m_APIs: 10000000 + m_Automatic: 1 + - m_BuildTarget: AndroidPlayer + m_APIs: 0b000000 + m_Automatic: 0 + - m_BuildTarget: WindowsStandaloneSupport + m_APIs: 0200000012000000 + m_Automatic: 0 + m_BuildTargetVRSettings: [] + m_DefaultShaderChunkSizeInMB: 16 + m_DefaultShaderChunkCount: 0 + openGLRequireES31: 0 + openGLRequireES31AEP: 0 + openGLRequireES32: 0 + m_TemplateCustomTags: {} + mobileMTRendering: + Android: 1 + iPhone: 1 + tvOS: 1 + m_BuildTargetGroupLightmapEncodingQuality: [] + m_BuildTargetGroupHDRCubemapEncodingQuality: [] + m_BuildTargetGroupLightmapSettings: [] + m_BuildTargetGroupLoadStoreDebugModeSettings: [] + m_BuildTargetNormalMapEncoding: [] + m_BuildTargetDefaultTextureCompressionFormat: [] + playModeTestRunnerEnabled: 0 + runPlayModeTestAsEditModeTest: 0 + actionOnDotNetUnhandledException: 1 + editorGfxJobOverride: 1 + enableInternalProfiler: 0 + logObjCUncaughtExceptions: 1 + enableCrashReportAPI: 0 + cameraUsageDescription: + locationUsageDescription: + microphoneUsageDescription: + bluetoothUsageDescription: + macOSTargetOSVersion: 12.0 + switchNMETAOverride: + switchNetLibKey: + switchSocketMemoryPoolSize: 6144 + switchSocketAllocatorPoolSize: 128 + switchSocketConcurrencyLimit: 14 + switchScreenResolutionBehavior: 2 + switchUseCPUProfiler: 0 + switchEnableFileSystemTrace: 0 + switchLTOSetting: 0 + switchApplicationID: 0x01004b9000490000 + switchNSODependencies: + switchCompilerFlags: + switchTitleNames_0: + switchTitleNames_1: + switchTitleNames_2: + switchTitleNames_3: + switchTitleNames_4: + switchTitleNames_5: + switchTitleNames_6: + switchTitleNames_7: + switchTitleNames_8: + switchTitleNames_9: + switchTitleNames_10: + switchTitleNames_11: + switchTitleNames_12: + switchTitleNames_13: + switchTitleNames_14: + switchTitleNames_15: + switchPublisherNames_0: + switchPublisherNames_1: + switchPublisherNames_2: + switchPublisherNames_3: + switchPublisherNames_4: + switchPublisherNames_5: + switchPublisherNames_6: + switchPublisherNames_7: + switchPublisherNames_8: + switchPublisherNames_9: + switchPublisherNames_10: + switchPublisherNames_11: + switchPublisherNames_12: + switchPublisherNames_13: + switchPublisherNames_14: + switchPublisherNames_15: + switchIcons_0: {fileID: 0} + switchIcons_1: {fileID: 0} + switchIcons_2: {fileID: 0} + switchIcons_3: {fileID: 0} + switchIcons_4: {fileID: 0} + switchIcons_5: {fileID: 0} + switchIcons_6: {fileID: 0} + switchIcons_7: {fileID: 0} + switchIcons_8: {fileID: 0} + switchIcons_9: {fileID: 0} + switchIcons_10: {fileID: 0} + switchIcons_11: {fileID: 0} + switchIcons_12: {fileID: 0} + switchIcons_13: {fileID: 0} + switchIcons_14: {fileID: 0} + switchIcons_15: {fileID: 0} + switchSmallIcons_0: {fileID: 0} + switchSmallIcons_1: {fileID: 0} + switchSmallIcons_2: {fileID: 0} + switchSmallIcons_3: {fileID: 0} + switchSmallIcons_4: {fileID: 0} + switchSmallIcons_5: {fileID: 0} + switchSmallIcons_6: {fileID: 0} + switchSmallIcons_7: {fileID: 0} + switchSmallIcons_8: {fileID: 0} + switchSmallIcons_9: {fileID: 0} + switchSmallIcons_10: {fileID: 0} + switchSmallIcons_11: {fileID: 0} + switchSmallIcons_12: {fileID: 0} + switchSmallIcons_13: {fileID: 0} + switchSmallIcons_14: {fileID: 0} + switchSmallIcons_15: {fileID: 0} + switchManualHTML: + switchAccessibleURLs: + switchLegalInformation: + switchMainThreadStackSize: 1048576 + switchPresenceGroupId: + switchLogoHandling: 0 + switchReleaseVersion: 0 + switchDisplayVersion: 1.0.0 + switchStartupUserAccount: 0 + switchSupportedLanguagesMask: 0 + switchLogoType: 0 + switchApplicationErrorCodeCategory: + switchUserAccountSaveDataSize: 0 + switchUserAccountSaveDataJournalSize: 0 + switchApplicationAttribute: 0 + switchCardSpecSize: -1 + switchCardSpecClock: -1 + switchRatingsMask: 0 + switchRatingsInt_0: 0 + switchRatingsInt_1: 0 + switchRatingsInt_2: 0 + switchRatingsInt_3: 0 + switchRatingsInt_4: 0 + switchRatingsInt_5: 0 + switchRatingsInt_6: 0 + switchRatingsInt_7: 0 + switchRatingsInt_8: 0 + switchRatingsInt_9: 0 + switchRatingsInt_10: 0 + switchRatingsInt_11: 0 + switchRatingsInt_12: 0 + switchLocalCommunicationIds_0: + switchLocalCommunicationIds_1: + switchLocalCommunicationIds_2: + switchLocalCommunicationIds_3: + switchLocalCommunicationIds_4: + switchLocalCommunicationIds_5: + switchLocalCommunicationIds_6: + switchLocalCommunicationIds_7: + switchParentalControl: 0 + switchAllowsScreenshot: 1 + switchAllowsVideoCapturing: 1 + switchAllowsRuntimeAddOnContentInstall: 0 + switchDataLossConfirmation: 0 + switchUserAccountLockEnabled: 0 + switchSystemResourceMemory: 16777216 + switchSupportedNpadStyles: 22 + switchNativeFsCacheSize: 32 + switchIsHoldTypeHorizontal: 0 + switchSupportedNpadCount: 8 + switchEnableTouchScreen: 1 + switchSocketConfigEnabled: 0 + switchTcpInitialSendBufferSize: 32 + switchTcpInitialReceiveBufferSize: 64 + switchTcpAutoSendBufferSizeMax: 256 + switchTcpAutoReceiveBufferSizeMax: 256 + switchUdpSendBufferSize: 9 + switchUdpReceiveBufferSize: 42 + switchSocketBufferEfficiency: 4 + switchSocketInitializeEnabled: 1 + switchNetworkInterfaceManagerInitializeEnabled: 1 + switchDisableHTCSPlayerConnection: 0 + switchUseNewStyleFilepaths: 0 + switchUseLegacyFmodPriorities: 0 + switchUseMicroSleepForYield: 1 + switchEnableRamDiskSupport: 0 + switchMicroSleepForYieldTime: 25 + switchRamDiskSpaceSize: 12 + switchUpgradedPlayerSettingsToNMETA: 0 + ps4NPAgeRating: 12 + ps4NPTitleSecret: + ps4NPTrophyPackPath: + ps4ParentalLevel: 11 + ps4ContentID: ED1633-NPXX51362_00-0000000000000000 + ps4Category: 0 + ps4MasterVersion: 01.00 + ps4AppVersion: 01.00 + ps4AppType: 0 + ps4ParamSfxPath: + ps4VideoOutPixelFormat: 0 + ps4VideoOutInitialWidth: 1920 + ps4VideoOutBaseModeInitialWidth: 1920 + ps4VideoOutReprojectionRate: 60 + ps4PronunciationXMLPath: + ps4PronunciationSIGPath: + ps4BackgroundImagePath: + ps4StartupImagePath: + ps4StartupImagesFolder: + ps4IconImagesFolder: + ps4SaveDataImagePath: + ps4SdkOverride: + ps4BGMPath: + ps4ShareFilePath: + ps4ShareOverlayImagePath: + ps4PrivacyGuardImagePath: + ps4ExtraSceSysFile: + ps4NPtitleDatPath: + ps4RemotePlayKeyAssignment: -1 + ps4RemotePlayKeyMappingDir: + ps4PlayTogetherPlayerCount: 0 + ps4EnterButtonAssignment: 2 + ps4ApplicationParam1: 0 + ps4ApplicationParam2: 0 + ps4ApplicationParam3: 0 + ps4ApplicationParam4: 0 + ps4DownloadDataSize: 0 + ps4GarlicHeapSize: 2048 + ps4ProGarlicHeapSize: 2560 + playerPrefsMaxSize: 32768 + ps4Passcode: fa37JncCHryDsbzayy4cBWDxS22JjzhM + ps4pnSessions: 1 + ps4pnPresence: 1 + ps4pnFriends: 1 + ps4pnGameCustomData: 1 + playerPrefsSupport: 0 + enableApplicationExit: 0 + resetTempFolder: 1 + restrictedAudioUsageRights: 0 + ps4UseResolutionFallback: 0 + ps4ReprojectionSupport: 0 + ps4UseAudio3dBackend: 0 + ps4UseLowGarlicFragmentationMode: 1 + ps4SocialScreenEnabled: 0 + ps4ScriptOptimizationLevel: 2 + ps4Audio3dVirtualSpeakerCount: 14 + ps4attribCpuUsage: 0 + ps4PatchPkgPath: + ps4PatchLatestPkgPath: + ps4PatchChangeinfoPath: + ps4PatchDayOne: 0 + ps4attribUserManagement: 0 + ps4attribMoveSupport: 0 + ps4attrib3DSupport: 0 + ps4attribShareSupport: 0 + ps4attribExclusiveVR: 0 + ps4disableAutoHideSplash: 0 + ps4videoRecordingFeaturesUsed: 0 + ps4contentSearchFeaturesUsed: 0 + ps4CompatibilityPS5: 0 + ps4AllowPS5Detection: 0 + ps4GPU800MHz: 1 + ps4attribEyeToEyeDistanceSettingVR: 0 + ps4IncludedModules: [] + ps4attribVROutputEnabled: 0 + monoEnv: + splashScreenBackgroundSourceLandscape: {fileID: 0} + splashScreenBackgroundSourcePortrait: {fileID: 0} + blurSplashScreenBackground: 1 + spritePackerPolicy: + webGLMemorySize: 32 + webGLExceptionSupport: 1 + webGLNameFilesAsHashes: 0 + webGLShowDiagnostics: 0 + webGLDataCaching: 1 + webGLDebugSymbols: 0 + webGLEmscriptenArgs: + webGLModulesDirectory: + webGLTemplate: APPLICATION:Default + webGLAnalyzeBuildSize: 0 + webGLUseEmbeddedResources: 0 + webGLCompressionFormat: 0 + webGLWasmArithmeticExceptions: 0 + webGLLinkerTarget: 1 + webGLThreadsSupport: 0 + webGLDecompressionFallback: 0 + webGLInitialMemorySize: 32 + webGLMaximumMemorySize: 2048 + webGLMemoryGrowthMode: 2 + webGLMemoryLinearGrowthStep: 16 + webGLMemoryGeometricGrowthStep: 0.2 + webGLMemoryGeometricGrowthCap: 96 + webGLPowerPreference: 2 + webGLWebAssemblyTable: 0 + webGLWebAssemblyBigInt: 0 + webGLCloseOnQuit: 0 + webWasm2023: 0 + webEnableSubmoduleStrippingCompatibility: 0 + scriptingDefineSymbols: + Android: CROSS_PLATFORM_INPUT;MOBILE_INPUT + Standalone: CROSS_PLATFORM_INPUT + Windows Store Apps: MOBILE_INPUT + iPhone: CROSS_PLATFORM_INPUT;MOBILE_INPUT + additionalCompilerArguments: {} + platformArchitecture: {} + scriptingBackend: + Android: 0 + il2cppCompilerConfiguration: {} + il2cppCodeGeneration: {} + il2cppStacktraceInformation: {} + managedStrippingLevel: + Android: 1 + EmbeddedLinux: 1 + GameCoreScarlett: 1 + GameCoreXboxOne: 1 + Kepler: 1 + Nintendo Switch: 1 + Nintendo Switch 2: 1 + PS4: 1 + PS5: 1 + QNX: 1 + VisionOS: 1 + WebGL: 1 + Windows Store Apps: 1 + XboxOne: 1 + iPhone: 1 + tvOS: 1 + incrementalIl2cppBuild: {} + suppressCommonWarnings: 1 + allowUnsafeCode: 0 + useDeterministicCompilation: 1 + additionalIl2CppArgs: + scriptingRuntimeVersion: 1 + gcIncremental: 0 + gcWBarrierValidation: 0 + apiCompatibilityLevelPerPlatform: {} + editorAssembliesCompatibilityLevel: 1 + m_RenderingPath: 1 + m_MobileRenderingPath: 1 + metroPackageName: sdsim + metroPackageVersion: + metroCertificatePath: + metroCertificatePassword: + metroCertificateSubject: + metroCertificateIssuer: + metroCertificateNotAfter: 0000000000000000 + metroApplicationDescription: sdsim + wsaImages: {} + metroTileShortName: + metroTileShowName: 0 + metroMediumTileShowName: 0 + metroLargeTileShowName: 0 + metroWideTileShowName: 0 + metroSupportStreamingInstall: 0 + metroLastRequiredScene: 0 + metroDefaultTileSize: 1 + metroTileForegroundText: 2 + metroTileBackgroundColor: {r: 0.13333334, g: 0.17254902, b: 0.21568628, a: 0} + metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902, b: 0.21568628, + a: 1} + metroSplashScreenUseBackgroundColor: 0 + syncCapabilities: 0 + platformCapabilities: {} + metroTargetDeviceFamilies: {} + metroFTAName: + metroFTAFileTypes: [] + metroProtocolName: + vcxProjDefaultLanguage: + XboxOneProductId: + XboxOneUpdateKey: + XboxOneSandboxId: + XboxOneContentId: + XboxOneTitleId: + XboxOneSCId: + XboxOneGameOsOverridePath: + XboxOnePackagingOverridePath: + XboxOneAppManifestOverridePath: + XboxOneVersion: 1.0.0.0 + XboxOnePackageEncryption: 0 + XboxOnePackageUpdateGranularity: 2 + XboxOneDescription: + XboxOneLanguage: + - enus + XboxOneCapability: [] + XboxOneGameRating: {} + XboxOneIsContentPackage: 0 + XboxOneEnhancedXboxCompatibilityMode: 0 + XboxOneEnableGPUVariability: 1 + XboxOneSockets: {} + XboxOneSplashScreen: {fileID: 0} + XboxOneAllowedProductIds: [] + XboxOnePersistentLocalStorageSize: 0 + XboxOneXTitleMemory: 8 + XboxOneOverrideIdentityName: + XboxOneOverrideIdentityPublisher: + vrEditorSettings: {} + cloudServicesEnabled: {} + luminIcon: + m_Name: + m_ModelFolderPath: + m_PortalFolderPath: + luminCert: + m_CertPath: + m_SignPackage: 1 + luminIsChannelApp: 0 + luminVersion: + m_VersionCode: 1 + m_VersionName: + hmiPlayerDataPath: + hmiForceSRGBBlit: 0 + embeddedLinuxEnableGamepadInput: 0 + hmiCpuConfiguration: + hmiLogStartupTiming: 0 + qnxGraphicConfPath: + apiCompatibilityLevel: 6 + captureStartupLogs: {} + activeInputHandler: 0 + windowsGamepadBackendHint: 0 + enableDirectStorage: 0 + cloudProjectId: + framebufferDepthMemorylessMode: 0 + qualitySettingsNames: [] + projectName: + organizationId: + cloudEnabled: 0 + legacyClampBlendShapeWeights: 0 + hmiLoadingImage: {fileID: 0} + platformRequiresReadableAssets: 0 + virtualTexturingSupportEnabled: 0 + insecureHttpOption: 0 + androidVulkanDenyFilterList: [] + androidVulkanAllowFilterList: [] + androidVulkanDeviceFilterListAsset: {fileID: 0} + d3d12DeviceFilterListAsset: {fileID: 0} + allowedHttpConnections: 3 diff --git a/ProjectSettings/ProjectVersion.txt b/ProjectSettings/ProjectVersion.txt new file mode 100755 index 0000000..cfa73b6 --- /dev/null +++ b/ProjectSettings/ProjectVersion.txt @@ -0,0 +1,2 @@ +m_EditorVersion: 6000.4.4f1 +m_EditorVersionWithRevision: 6000.4.4f1 (360f97ecca93) diff --git a/ProjectSettings/QualitySettings.asset b/ProjectSettings/QualitySettings.asset new file mode 100755 index 0000000..41b7028 --- /dev/null +++ b/ProjectSettings/QualitySettings.asset @@ -0,0 +1,236 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!47 &1 +QualitySettings: + m_ObjectHideFlags: 0 + serializedVersion: 5 + m_CurrentQuality: 5 + m_QualitySettings: + - serializedVersion: 2 + name: Very Low + pixelLightCount: 0 + shadows: 0 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 15 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + skinWeights: 1 + textureQuality: 1 + anisotropicTextures: 0 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 0 + lodBias: 0.3 + maximumLODLevel: 0 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 4 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Low + pixelLightCount: 0 + shadows: 0 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 20 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + skinWeights: 2 + textureQuality: 0 + anisotropicTextures: 0 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 0 + lodBias: 0.4 + maximumLODLevel: 0 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 16 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Medium + pixelLightCount: 1 + shadows: 1 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 20 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + skinWeights: 2 + textureQuality: 0 + anisotropicTextures: 1 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 1 + lodBias: 0.7 + maximumLODLevel: 0 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 64 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: High + pixelLightCount: 2 + shadows: 2 + shadowResolution: 1 + shadowProjection: 1 + shadowCascades: 2 + shadowDistance: 40 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + skinWeights: 2 + textureQuality: 0 + anisotropicTextures: 1 + antiAliasing: 0 + softParticles: 0 + softVegetation: 1 + realtimeReflectionProbes: 1 + billboardsFaceCameraPosition: 1 + vSyncCount: 1 + lodBias: 1 + maximumLODLevel: 0 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 256 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Very High + pixelLightCount: 3 + shadows: 2 + shadowResolution: 2 + shadowProjection: 1 + shadowCascades: 2 + shadowDistance: 70 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + skinWeights: 4 + textureQuality: 0 + anisotropicTextures: 2 + antiAliasing: 2 + softParticles: 1 + softVegetation: 1 + realtimeReflectionProbes: 1 + billboardsFaceCameraPosition: 1 + vSyncCount: 1 + lodBias: 1.5 + maximumLODLevel: 0 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 1024 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Ultra + pixelLightCount: 4 + shadows: 2 + shadowResolution: 2 + shadowProjection: 1 + shadowCascades: 4 + shadowDistance: 150 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + skinWeights: 255 + textureQuality: 0 + anisotropicTextures: 2 + antiAliasing: 2 + softParticles: 1 + softVegetation: 1 + realtimeReflectionProbes: 1 + billboardsFaceCameraPosition: 1 + vSyncCount: 0 + lodBias: 2 + maximumLODLevel: 0 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 4096 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 0} + excludedTargetPlatforms: [] + m_PerPlatformDefaultQuality: + Android: 2 + BJM: 5 + Lumin: 5 + Nintendo Switch: 5 + PS4: 5 + Standalone: 5 + WebGL: 3 + Windows Store Apps: 5 + XboxOne: 5 + iPhone: 2 + tvOS: 2 diff --git a/ProjectSettings/SceneTemplateSettings.json b/ProjectSettings/SceneTemplateSettings.json new file mode 100755 index 0000000..6f3e60f --- /dev/null +++ b/ProjectSettings/SceneTemplateSettings.json @@ -0,0 +1,167 @@ +{ + "templatePinStates": [], + "dependencyTypeInfos": [ + { + "userAdded": false, + "type": "UnityEngine.AnimationClip", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEditor.Animations.AnimatorController", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.AnimatorOverrideController", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEditor.Audio.AudioMixerController", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.ComputeShader", + "ignore": true, + "defaultInstantiationMode": 1, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.Cubemap", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.GameObject", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEditor.LightingDataAsset", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": false + }, + { + "userAdded": false, + "type": "UnityEngine.LightingSettings", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.Material", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEditor.MonoScript", + "ignore": true, + "defaultInstantiationMode": 1, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.PhysicMaterial", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.PhysicsMaterial2D", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.Rendering.PostProcessing.PostProcessProfile", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.Rendering.PostProcessing.PostProcessResources", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.Rendering.VolumeProfile", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEditor.SceneAsset", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": false + }, + { + "userAdded": false, + "type": "UnityEngine.Shader", + "ignore": true, + "defaultInstantiationMode": 1, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.ShaderVariantCollection", + "ignore": true, + "defaultInstantiationMode": 1, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.Texture", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.Texture2D", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + }, + { + "userAdded": false, + "type": "UnityEngine.Timeline.TimelineAsset", + "ignore": false, + "defaultInstantiationMode": 0, + "supportsModification": true + } + ], + "defaultDependencyTypeInfo": { + "userAdded": false, + "type": "", + "ignore": false, + "defaultInstantiationMode": 1, + "supportsModification": true + }, + "newSceneOverride": 0 +} \ No newline at end of file diff --git a/ProjectSettings/ShaderGraphSettings.asset b/ProjectSettings/ShaderGraphSettings.asset new file mode 100755 index 0000000..e33ca24 --- /dev/null +++ b/ProjectSettings/ShaderGraphSettings.asset @@ -0,0 +1,19 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &1 +MonoBehaviour: + m_ObjectHideFlags: 53 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: de02f9e1d18f588468e474319d09a723, type: 3} + m_Name: + m_EditorClassIdentifier: Unity.ShaderGraph.Editor::UnityEditor.ShaderGraph.ShaderGraphProjectSettings + shaderVariantLimit: 2048 + overrideShaderVariantLimit: 0 + customInterpolatorErrorThreshold: 32 + customInterpolatorWarningThreshold: 16 + customHeatmapValues: {fileID: 0} diff --git a/ProjectSettings/TagManager.asset b/ProjectSettings/TagManager.asset new file mode 100755 index 0000000..bc47e88 --- /dev/null +++ b/ProjectSettings/TagManager.asset @@ -0,0 +1,46 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!78 &1 +TagManager: + serializedVersion: 2 + tags: + - ground + - road_mesh + - pathNode + layers: + - Default + - TransparentFX + - Ignore Raycast + - + - Water + - UI + - + - + - CarName + - Ignore Lidar + - OverHead + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + m_SortingLayers: + - name: Default + uniqueID: 0 + locked: 0 diff --git a/ProjectSettings/TimeManager.asset b/ProjectSettings/TimeManager.asset new file mode 100755 index 0000000..c3ea6be --- /dev/null +++ b/ProjectSettings/TimeManager.asset @@ -0,0 +1,9 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!5 &1 +TimeManager: + m_ObjectHideFlags: 0 + Fixed Timestep: 0.01 + Maximum Allowed Timestep: 0.1 + m_TimeScale: 1 + Maximum Particle Timestep: 0.03 diff --git a/ProjectSettings/TimelineSettings.asset b/ProjectSettings/TimelineSettings.asset new file mode 100755 index 0000000..b4fbdb0 --- /dev/null +++ b/ProjectSettings/TimelineSettings.asset @@ -0,0 +1,15 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &1 +MonoBehaviour: + m_ObjectHideFlags: 61 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a287be6c49135cd4f9b2b8666c39d999, type: 3} + m_Name: + m_EditorClassIdentifier: + assetDefaultFramerate: 60 diff --git a/ProjectSettings/URPProjectSettings.asset b/ProjectSettings/URPProjectSettings.asset new file mode 100755 index 0000000..3077404 --- /dev/null +++ b/ProjectSettings/URPProjectSettings.asset @@ -0,0 +1,15 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &1 +MonoBehaviour: + m_ObjectHideFlags: 61 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 247994e1f5a72c2419c26a37e9334c01, type: 3} + m_Name: + m_EditorClassIdentifier: + m_LastMaterialVersion: 4 diff --git a/ProjectSettings/UnityConnectSettings.asset b/ProjectSettings/UnityConnectSettings.asset new file mode 100755 index 0000000..a88bee0 --- /dev/null +++ b/ProjectSettings/UnityConnectSettings.asset @@ -0,0 +1,36 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!310 &1 +UnityConnectSettings: + m_ObjectHideFlags: 0 + serializedVersion: 1 + m_Enabled: 0 + m_TestMode: 0 + m_EventOldUrl: https://api.uca.cloud.unity3d.com/v1/events + m_EventUrl: https://cdp.cloud.unity3d.com/v1/events + m_ConfigUrl: https://config.uca.cloud.unity3d.com + m_DashboardUrl: https://dashboard.unity3d.com + m_TestInitMode: 0 + CrashReportingSettings: + m_EventUrl: https://perf-events.cloud.unity3d.com + m_Enabled: 0 + m_LogBufferSize: 10 + m_CaptureEditorExceptions: 1 + UnityPurchasingSettings: + m_Enabled: 0 + m_TestMode: 0 + UnityAnalyticsSettings: + m_Enabled: 0 + m_TestMode: 0 + m_InitializeOnStartup: 1 + m_PackageRequiringCoreStatsPresent: 0 + UnityAdsSettings: + m_Enabled: 0 + m_InitializeOnStartup: 1 + m_TestMode: 0 + m_IosGameId: + m_AndroidGameId: + m_GameIds: {} + m_GameId: + PerformanceReportingSettings: + m_Enabled: 0 diff --git a/ProjectSettings/VFXManager.asset b/ProjectSettings/VFXManager.asset new file mode 100755 index 0000000..46f38e1 --- /dev/null +++ b/ProjectSettings/VFXManager.asset @@ -0,0 +1,14 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!937362698 &1 +VFXManager: + m_ObjectHideFlags: 0 + m_IndirectShader: {fileID: 0} + m_CopyBufferShader: {fileID: 0} + m_SortShader: {fileID: 0} + m_StripUpdateShader: {fileID: 0} + m_RenderPipeSettingsPath: + m_FixedTimeStep: 0.016666668 + m_MaxDeltaTime: 0.05 + m_CompiledVersion: 0 + m_RuntimeVersion: 0 diff --git a/ProjectSettings/VersionControlSettings.asset b/ProjectSettings/VersionControlSettings.asset new file mode 100755 index 0000000..dca2881 --- /dev/null +++ b/ProjectSettings/VersionControlSettings.asset @@ -0,0 +1,8 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!890905787 &1 +VersionControlSettings: + m_ObjectHideFlags: 0 + m_Mode: Visible Meta Files + m_CollabEditorSettings: + inProgressEnabled: 1 diff --git a/ProjectSettings/XRSettings.asset b/ProjectSettings/XRSettings.asset new file mode 100755 index 0000000..482590c --- /dev/null +++ b/ProjectSettings/XRSettings.asset @@ -0,0 +1,10 @@ +{ + "m_SettingKeys": [ + "VR Device Disabled", + "VR Device User Alert" + ], + "m_SettingValues": [ + "False", + "False" + ] +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..46b5a76 --- /dev/null +++ b/README.md @@ -0,0 +1,45 @@ +# sdsandbox-rl-scripts + +Modified C# scripts for the sdsandbox Unity simulator, used with the +[donkeycar-rl-autoresearch](https://paje.ca/git/paulh/donkeycar-rl-autoresearch) project. + +## What's here + +- `Scripts/` — all C# scripts from `sdsim/Assets/Scripts/`, including our RL training modifications +- `ProjectSettings/` — Unity project settings +- `Packages/` — Unity package manifest + +## Key modifications (vs upstream tawnkramer/sdsandbox) + +- **`Car.cs`** — per-wheel OverlapSphere barrier collision detection; CCD mode +- **`TcpCarHandler.cs`** — `regen_road` message support with direct RoadBuilder+PathManager fallback when no TrainingManager present; `set_ai_text` overlay message +- **`PathManager.cs`** — self-intersection fix: retry loop with XZ segment-segment math (up to 20 retries) +- **`RoadBuilder.cs`** — BoxCollider per segment with overlap; `showBarrierMeshes` flag +- **`MapOverlay.cs`** — minimap refresh on road node position change (not just count) + +## Setup on a new machine + +1. Clone the upstream sdsandbox (provides all binary assets Unity needs): + ```bash + git clone https://github.com/tawnkramer/sdsandbox + ``` + +2. Clone this repo: + ```bash + git clone git@paje.ca:paulh/sdsandbox-rl-scripts.git + ``` + +3. Overlay our scripts into the sdsandbox project: + ```bash + cp -r sdsandbox-rl-scripts/Scripts/* sdsandbox/sdsim/Assets/Scripts/ + cp -r sdsandbox-rl-scripts/ProjectSettings/* sdsandbox/sdsim/ProjectSettings/ + cp -r sdsandbox-rl-scripts/Packages/* sdsandbox/sdsim/Packages/ + ``` + +4. Open `sdsandbox/sdsim` in Unity 6000.4.4f1. Unity will reimport assets on first open. + +5. Build: **Edit → Project Settings → Player Builder → WinBuild** (or use the batch build command from the donkeycar-rl-autoresearch SESSION_HANDOFF.md). + +## Unity version + +**Unity 6000.4.4f1** — other versions are not tested and may have incompatible APIs. diff --git a/Scripts/Car.cs b/Scripts/Car.cs new file mode 100755 index 0000000..68743c7 --- /dev/null +++ b/Scripts/Car.cs @@ -0,0 +1,379 @@ +using UnityEngine; +using System.Collections; + + +public class Car : MonoBehaviour, ICar{ + + public CarSpawner carSpawner; + public WheelCollider[] wheelColliders; + public Transform[] wheelMeshes; + + public float maxSpeed = 30f; + public float maxTorque = 50f; + public float maxBreakTorque = 50f; + public AnimationCurve torqueCurve; + + public Transform centrOfMass; + + public float requestTorque = 0f; + public float requestBrake = 0f; + public float requestSteering = 0f; + + public Vector3 acceleration = Vector3.zero; + public Vector3 velocity = Vector3.zero; + public Vector3 prevVel = Vector3.zero; + + public Vector3 startPos; + public Quaternion startRot; + private Quaternion rotation = Quaternion.identity; + private Vector3 gyro = Vector3.zero; + + public Rigidbody rb; + + //for logging + public float lastSteer = 0.0f; + public float lastAccel = 0.0f; + + //when the car is doing multiple things, we sometimes want to sort out parts of the training + //use this label to pull partial training samples from a run + public string activity = "keep_lane"; + + public float maxSteer = 16.0f; + + //name of the last object we hit. + public string last_collision = "none"; + private float last_collision_time = -999f; + private const float collision_hold_seconds = 0.75f; + + // Pre-allocated buffer for per-wheel barrier overlap checks (avoids GC per frame). + private Collider[] _wheelOverlapBuf = new Collider[8]; + + + // Use this for initialization + void Awake () + { + rb = GetComponent(); + + if(rb && centrOfMass) + { + rb.centerOfMass = centrOfMass.localPosition; + } + + // Continuous collision detection prevents the car from tunnelling through + // thin or fast-moving colliders (including barrier walls) between frames. + if (rb) + rb.collisionDetectionMode = CollisionDetectionMode.Continuous; + + requestTorque = 0f; + requestSteering = 0f; + + SavePosRot(); + + // had to disable this because PID max steering was affecting the global max_steering + // maxSteer = PlayerPrefs.GetFloat("max_steer", 16.0f); + } + + public void SavePosRot() + { + startPos = transform.position; + startRot = transform.rotation; + } + + public void RestorePosRot() + { + Set(startPos, startRot); + } + + public void RequestThrottle(float val) + { + requestTorque = val; + requestBrake = 0f; + //Debug.Log("request throttle: " + val); + } + + public void SetMaxSteering(float val) + { + maxSteer = val; + // had to disable this because PID max steering was affecting the global max_steering + // PlayerPrefs.SetFloat("max_steer", maxSteer); + // PlayerPrefs.Save(); + } + + public float GetMaxSteering() + { + return maxSteer; + } + + public void RequestSteering(float val) + { + requestSteering = Mathf.Clamp(val, -maxSteer, maxSteer); + //Debug.Log("request steering: " + val); + } + + public void Set(Vector3 pos, Quaternion rot) + { + rb.position = pos; + rb.rotation = rot; + + //just setting it once doesn't seem to work. Try setting it multiple times.. + StartCoroutine(KeepSetting(pos, rot, 1)); + } + + IEnumerator KeepSetting(Vector3 pos, Quaternion rot, int numIter) + { + while(numIter > 0) + { + rb.isKinematic = true; + + yield return new WaitForFixedUpdate(); + + rb.position = pos; + rb.rotation = rot; + transform.position = pos; + transform.rotation = rot; + + numIter--; + + rb.isKinematic = false; + } + } + + public float GetSteering() + { + return requestSteering; + } + + public float GetThrottle() + { + return requestTorque; + } + + public float GetFootBrake() + { + return requestBrake; + } + + public float GetHandBrake() + { + return 0.0f; + } + + public Vector3 GetVelocity() + { + return velocity; + } + + public Vector3 GetAccel() + { + return acceleration; + } + public Vector3 GetGyro() + { + return gyro; + } + public float GetOrient () + { + Vector3 dir = transform.forward; + return Mathf.Atan2( dir.z, dir.x); + } + + public Transform GetTransform() + { + return this.transform; + } + + public bool IsStill() + { + return rb.IsSleeping(); + } + + public void RequestFootBrake(float val) + { + requestBrake = val; + } + + public void RequestHandBrake(float val) + { + //todo + } + + // Update is called once per frame + void Update () { + + UpdateWheelPositions(); + } + + public string GetActivity() + { + return activity; + } + + public void SetActivity(string act) + { + activity = act; + } + + void FixedUpdate() + { + lastSteer = requestSteering; + lastAccel = requestTorque; + prevVel = velocity; + velocity = transform.InverseTransformDirection(rb.velocity); + acceleration = (velocity - prevVel)/Time.deltaTime; + gyro = rb.angularVelocity; + rotation = rb.rotation; + + // use the torque curve + float throttle = torqueCurve.Evaluate(velocity.magnitude / maxSpeed) * requestTorque * maxTorque; + float steerAngle = requestSteering; + float brake = requestBrake * maxBreakTorque; + + //front two tires. + wheelColliders[2].steerAngle = steerAngle; + wheelColliders[3].steerAngle = steerAngle; + + //four wheel drive at the moment + foreach(WheelCollider wc in wheelColliders) + { + wc.motorTorque = throttle; + wc.brakeTorque = brake; + } + + // WheelColliders don't fire OnCollisionStay on this MonoBehaviour. We use + // two complementary checks to catch barrier contact from any angle: + // + // 1) Forward raycast — probes ahead of the car even before contact occurs, + // so it fires quickly when driving into a barrier nose-first. + if (requestTorque > 0.05f) + { + Vector3 rayOrigin = transform.position + transform.up * 0.3f; + RaycastHit rhit; + if (Physics.Raycast(rayOrigin, transform.forward, out rhit, 0.8f)) + { + if (!IsStartingLine(rhit.collider.gameObject.name)) + RegisterCollision(rhit.collider.gameObject.name); + } + } + + // 2) Per-wheel OverlapSphere — catches actual contact from any direction + // (side, rear, diagonal) regardless of throttle or car orientation. + // Uses barrier name filter to avoid false positives from the road surface. + foreach (WheelCollider wc in wheelColliders) + { + Vector3 wheelCenter; + Quaternion wheelRot; + wc.GetWorldPose(out wheelCenter, out wheelRot); + int n = Physics.OverlapSphereNonAlloc(wheelCenter, wc.radius + 0.05f, _wheelOverlapBuf); + for (int i = 0; i < n; i++) + { + if (_wheelOverlapBuf[i] == null) continue; + string hitName = _wheelOverlapBuf[i].gameObject.name; + if (hitName.Contains("barrier")) + { + RegisterCollision(hitName); + break; + } + } + } + + } + + void FlipUpright() + { + Quaternion rot = Quaternion.Euler(180f, 0f, 0f); + this.transform.rotation = transform.rotation * rot; + transform.position = transform.position + Vector3.up * 2; + } + + void UpdateWheelPositions() + { + Quaternion rot; + Vector3 pos; + + for(int i = 0; i < wheelColliders.Length; i++) + { + WheelCollider wc = wheelColliders[i]; + Transform tm = wheelMeshes[i]; + + wc.GetWorldPose(out pos, out rot); + + tm.position = pos; + tm.rotation = rot; + } + } + + //get the name of the last object we collided with + public string GetLastCollision() + { + return last_collision; + } + + public void ClearLastCollision() + { + if (Time.time - last_collision_time > collision_hold_seconds) + { + last_collision = "none"; + } + } + + bool ShouldPersistCollision(string collisionName, float speedMagnitude) + { + if (speedMagnitude < 1.5f) + { + return true; + } + + string lowered = collisionName.ToLowerInvariant(); + return lowered.Contains("barrier") || lowered.Contains("tree") || lowered.Contains("wall"); + } + + // The starting/finish line is a physical collider used for lap counting. + // It must not be treated as a barrier hit — lap counting is handled separately + // via the collision_with_starting_line TCP message in TcpCarHandler. + bool IsStartingLine(string objectName) + { + string lower = objectName.ToLowerInvariant(); + return lower.Contains("starting_line") || lower.Contains("startingline") || lower.Contains("finishline"); + } + + void RegisterCollision(string collisionName) + { + last_collision = collisionName; + last_collision_time = Time.time; + } + + void OnCollisionEnter(Collision col) + { + if (IsStartingLine(col.gameObject.name)) return; + RegisterCollision(col.gameObject.name); + } + + void OnCollisionStay(Collision col) + { + if (IsStartingLine(col.gameObject.name)) return; + float speedMagnitude = (rb != null) ? rb.velocity.magnitude : 0.0f; + if (ShouldPersistCollision(col.gameObject.name, speedMagnitude)) + { + RegisterCollision(col.gameObject.name); + } + } + + // Trigger-based barriers (used by built-in tracks like mini-monaco) fire + // OnTriggerEnter rather than OnCollisionEnter. Handle them here so that + // out-of-bounds detection works on all tracks, not just our custom ones. + void OnTriggerEnter(Collider other) + { + if (IsStartingLine(other.gameObject.name)) return; + RegisterCollision(other.gameObject.name); + } + + void OnTriggerStay(Collider other) + { + if (IsStartingLine(other.gameObject.name)) return; + float speedMagnitude = (rb != null) ? rb.velocity.magnitude : 0.0f; + if (ShouldPersistCollision(other.gameObject.name, speedMagnitude)) + { + RegisterCollision(other.gameObject.name); + } + } +} diff --git a/Scripts/Car.cs.meta b/Scripts/Car.cs.meta new file mode 100755 index 0000000..552966c --- /dev/null +++ b/Scripts/Car.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: ab3484103f28341d0a2099af385e1610 +timeCreated: 1448832312 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/CarBodyStyle.cs b/Scripts/CarBodyStyle.cs new file mode 100755 index 0000000..00da762 --- /dev/null +++ b/Scripts/CarBodyStyle.cs @@ -0,0 +1,48 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class CarBodyStyle : MonoBehaviour +{ + public GameObject[] bodyParts; + + // sometimes, object can have multiple material, here we are specifying witch one we want to modify + public string[] material_name; + private int mat_idx = 0; + + public void ApplyColor(Color col) + { + for (int i = 0; i < bodyParts.Length; i++) + { + GameObject part = bodyParts[i]; + Renderer rend = part.GetComponent(); + Material[] materials = rend.materials; + + if (i < material_name.Length && i < materials.Length) + { + mat_idx = SearchMaterial(materials, material_name[i]); + } + else + { + mat_idx = 0; + } + // Debug.Log("mat name: " + materials[mat_idx].name); + materials[mat_idx].SetColor("_Color", col); + } + } + + public int SearchMaterial(Material[] materials, string name) + { + if (name == "default") + return 0; + + // search for the material requested name + for (int idx = 0; idx < materials.Length; idx++) + { + // Debug.Log(materials[idx].name + " " + name); + if (materials[idx].name == name + " (Instance)") + return idx; + } + return 0; + } +} diff --git a/Scripts/CarBodyStyle.cs.meta b/Scripts/CarBodyStyle.cs.meta new file mode 100755 index 0000000..84601f6 --- /dev/null +++ b/Scripts/CarBodyStyle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0fb12741290ca1d4998921501601a696 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/CarConfig.cs b/Scripts/CarConfig.cs new file mode 100755 index 0000000..df65f1e --- /dev/null +++ b/Scripts/CarConfig.cs @@ -0,0 +1,78 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; + +public class CarConfig : MonoBehaviour +{ + public GameObject bodyStyles; + public GameObject car_name_base; + public GameObject OverheadViewSphere; + public TextMesh car_name_text; + public Timer timer; + + public void SetStyle(string body_style, int r, int g, int b, string car_name, int font_size) + { + Debug.Log("Setting car config."); + + if (car_name.Length > 1) + { + car_name_base.SetActive(true); + car_name_text.text = car_name; + car_name_text.fontSize = font_size; + transform.name = car_name; // rename the gameobject name + } + + if (timer != null) + { + timer.racerName = car_name; + } + else + { + Debug.LogError("timer not found while mapping racer name"); + } + + Color col = new Color(); + col.r = r / 255.0f; + col.g = g / 255.0f; + col.b = b / 255.0f; + + Renderer rend = OverheadViewSphere.GetComponent(); + Material[] materials = rend.materials; + materials[0].SetColor("_Color", col); + + + GameObject bodyStyle; + for (int i = 0; i < bodyStyles.transform.childCount; i++) // go through each bodyStyles to find the desired body style + { + bodyStyle = bodyStyles.transform.GetChild(i).gameObject; + if (bodyStyle.name == body_style) // check if it's the requested body style + { + bodyStyle.SetActive(true); + var carBodyStyle = bodyStyle.GetComponent(); + if (carBodyStyle != null) + { + carBodyStyle.ApplyColor(col); + } + else + { + Debug.LogWarning(bodyStyle.name + " doesn't have a CarBodyStyle component"); + } + return; + } + else // else just make sure it's disabled + { + bodyStyle.SetActive(false); + } + } + Debug.LogWarning("body_style not in list of available bodyStyles, using default one instead"); + + GameObject defaultBodyStyle = bodyStyles.transform.GetChild(0).gameObject; + var defaultCarBodyStyle = defaultBodyStyle.GetComponent(); + if (defaultCarBodyStyle != null) + { + defaultBodyStyle.SetActive(true); + defaultCarBodyStyle.ApplyColor(col); + } + } +} diff --git a/Scripts/CarConfig.cs.meta b/Scripts/CarConfig.cs.meta new file mode 100755 index 0000000..2cd5a04 --- /dev/null +++ b/Scripts/CarConfig.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 18637ebcb2c9a94dc9974c66102fb75c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/CarModel.cs b/Scripts/CarModel.cs new file mode 100755 index 0000000..6ce556c --- /dev/null +++ b/Scripts/CarModel.cs @@ -0,0 +1,47 @@ +using UnityEngine; +using System.Collections; + +public class CarModel +{ + public Vector3 pos = Vector3.zero; + public float length = 1f; + public float orientation = 0f; //radians rot about Y + + public void SetLength(float val) + { + length = val; + } + + public void Set(Vector3 p, float o) + { + pos = p; + orientation = o; + } + + public CarModel move(float distMove, float angleTurn) + { + CarModel newMod = new CarModel(); + newMod.pos = pos; + newMod.length = length; + newMod.orientation = orientation; + float theta = orientation; + + if(angleTurn < 0.001f) + { + newMod.pos.x += distMove * Mathf.Cos(theta); + newMod.pos.z += distMove * Mathf.Sin(theta); + } + else + { + float Beta = distMove / length * Mathf.Tan( angleTurn ); + float R = distMove / Beta; + float cx = pos.x - Mathf.Sin(theta) * R; + float cz = pos.z + Mathf.Cos(theta) * R; + newMod.pos.x = cx + Mathf.Sin(theta + Beta) * R; + newMod.pos.z = cz - Mathf.Cos(theta + Beta) * R; + newMod.orientation = theta + Beta; + } + + return newMod; + } +} diff --git a/Scripts/CarModel.cs.meta b/Scripts/CarModel.cs.meta new file mode 100755 index 0000000..5923dfd --- /dev/null +++ b/Scripts/CarModel.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 2ad110fb87d4845daac10710224235c8 +timeCreated: 1449523174 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/CarPath.cs b/Scripts/CarPath.cs new file mode 100755 index 0000000..6528deb --- /dev/null +++ b/Scripts/CarPath.cs @@ -0,0 +1,163 @@ +using UnityEngine; +using System.Collections; +using System.Collections.Generic; +using UnityEngine.AI; + +public class PathNode +{ + public Vector3 pos; + public Quaternion rotation; + public string activity; +} + +public class CarPath +{ + public List nodes; + public List centerNodes; + public NavMeshPath navMeshPath; + public int iActiveSpan = 0; + + + public CarPath() + { + nodes = new List(); + centerNodes = new List(); + navMeshPath = new NavMeshPath(); + } + + public int GetClosestSpanIndex(Vector3 carPos) + { + float minDistance = float.MaxValue; + int minDistanceIndex = -1; + for (int i = 0; i < nodes.Count; i++) + { + float dist = Vector3.Distance(nodes[i].pos, carPos); + if (dist < minDistance) + { + minDistance = dist; + minDistanceIndex = i; + } + } + return minDistanceIndex; + } + + public PathNode GetNode(int index) + { + if (index < nodes.Count) + return nodes[index]; + + return null; + } + + public void SmoothPath(float factor = 0.5f) + { + LineSeg3d.SegResult segRes = new LineSeg3d.SegResult(); + + for (int iN = 1; iN < nodes.Count - 2; iN++) + { + PathNode p = nodes[iN - 1]; + PathNode c = nodes[iN]; + PathNode n = nodes[iN + 1]; + + LineSeg3d seg = new LineSeg3d(ref p.pos, ref n.pos); + Vector3 closestP = seg.ClosestPointOnSegmentTo(ref c.pos, ref segRes); + Vector3 dIntersect = closestP - c.pos; + c.pos += dIntersect.normalized * factor; + } + } + public double getDistance(Vector3 currentPosition, Vector3 target) + { + double distance = 0.0; + if (NavMesh.CalculatePath(currentPosition, target, NavMesh.AllAreas, this.navMeshPath)) + { + if (this.navMeshPath.corners.Length > 5) + { + //ignore the first corners -> more stable + distance += (this.navMeshPath.corners[2] - currentPosition).magnitude; + for (int i = 3; i < this.navMeshPath.corners.Length; i++) + { + Vector3 start = this.navMeshPath.corners[i - 1]; + Vector3 end = this.navMeshPath.corners[i]; + distance += (end - start).magnitude; + } + } + } + return distance; + } + + public bool GetCrossTrackErr(Vector3 pos, ref int iActiveSpan, ref float err, int lookAhead = 1) + { + int nextIActiveSpan = (iActiveSpan + 1) % (nodes.Count); + int aheadIActiveSpan = (iActiveSpan + lookAhead) % (nodes.Count); + + PathNode a = nodes[iActiveSpan]; + PathNode b = nodes[nextIActiveSpan]; + PathNode c = nodes[aheadIActiveSpan]; + + //2d path. + pos.y = a.pos.y; + + LineSeg3d pathSeg = new LineSeg3d(ref a.pos, ref c.pos); + LineSeg3d.SegResult segRes = new LineSeg3d.SegResult(); + Vector3 closePt = pathSeg.ClosestPointOnSegmentTo(ref pos, ref segRes); + Vector3 errVec = pathSeg.ClosestVectorTo(ref pos); + + pathSeg.Draw(Color.green); + Debug.DrawLine(a.pos, closePt, Color.blue); + Debug.DrawRay(closePt, errVec, Color.white); + + float sign = 1.0f; + + Vector3 cp = Vector3.Cross(pathSeg.m_dir.normalized, errVec.normalized); + + if (cp.y > 0.0f) + sign = -1f; + + err = errVec.magnitude * sign; + + int oldActiveSpan = iActiveSpan ; + + float dista = Vector3.Distance(a.pos, pos); + float distb = Vector3.Distance(b.pos, pos); + if (dista > distb) + { + iActiveSpan = (iActiveSpan + 1) % (nodes.Count); + } + + // if (iActiveSpan - oldActiveSpan <= 0) { return true; } // we lapped + return false; // we are on the same lap + } + + public (float xmin, float xmax, float ymin, float ymax, float zmin, float zmax) GetPathBounds() + { + (float xmin, float xmax, float ymin, float ymax, float zmin, float zmax) bounds; + bounds.xmin = bounds.ymin = bounds.zmin = float.MaxValue; + bounds.xmax = bounds.ymax = bounds.zmax = float.MinValue; + + foreach (PathNode node in centerNodes) + { + + Vector3 pos = node.pos; + float x = pos.x; + float y = pos.y; + float z = pos.z; + + if (x < bounds.xmin) + bounds.xmin = x; + if (x > bounds.xmax) + bounds.xmax = x; + + if (y < bounds.ymin) + bounds.ymin = y; + if (y > bounds.ymax) + bounds.ymax = y; + + if (z < bounds.zmin) + bounds.zmin = z; + if (z > bounds.zmax) + bounds.zmax = z; + + } + return bounds; + } +} \ No newline at end of file diff --git a/Scripts/CarPath.cs.meta b/Scripts/CarPath.cs.meta new file mode 100755 index 0000000..9e78ef4 --- /dev/null +++ b/Scripts/CarPath.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 71b3d88cc247f4490bbdc990e9941183 +timeCreated: 1449527982 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/CarPusher.cs b/Scripts/CarPusher.cs new file mode 100755 index 0000000..703ee99 --- /dev/null +++ b/Scripts/CarPusher.cs @@ -0,0 +1,51 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class CarPusher : MonoBehaviour { + + public Car car; + public float amountToPushSideways = 1.0f; + public float amountToRotate = 10.0f; + public float durationTillNextPush = 3.0f; + public bool doPush = true; + public bool doRotate = true; + + float timer = 0.0f; + + // Use this for initialization + void Start () { + timer = durationTillNextPush - 1.0f; + } + + void PushCar() + { + Vector3 sideVec = car.gameObject.transform.right.normalized; + float randScale = Random.Range( -1f * amountToPushSideways, amountToPushSideways); + car.transform.position = car.transform.position + (sideVec * randScale); + } + + void RotateCar() + { + float randRot = Random.Range( -1f * amountToRotate, amountToRotate); + Quaternion rotY = Quaternion.Euler(0.0f, randRot, 0.0f); + car.transform.rotation = car.transform.rotation * rotY; + } + + // Update is called once per frame + void Update () + { + timer += Time.deltaTime; + + if(timer > durationTillNextPush) + { + timer = 0.0f; + + if(doPush) + PushCar(); + + if(doRotate) + RotateCar(); + } + } +} diff --git a/Scripts/CarPusher.cs.meta b/Scripts/CarPusher.cs.meta new file mode 100755 index 0000000..0aae9e2 --- /dev/null +++ b/Scripts/CarPusher.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 4a4f2387b0f594d808f4ade6f32da6ed +timeCreated: 1482953591 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/CarSpawner.cs b/Scripts/CarSpawner.cs new file mode 100755 index 0000000..3a06dc1 --- /dev/null +++ b/Scripts/CarSpawner.cs @@ -0,0 +1,528 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using tk; +using System; + +public class CarSpawner : MonoBehaviour +{ + + public PathManager pathManager; + public GameObject carPrefab; + public Transform[] startsTm; // list containing multiple starting points + public bool EnableTrainingManager = false; + + public delegate void OnNewCar(GameObject carObj); + public OnNewCar OnNewCarCB; + + public int numCarRows = 2; + public float distCarCols = 4.5f; + public float distCarRows = 5f; + + public GameObject mainCamera; + public GameObject splitScreenCamPrefab; + public GameObject splitScreenOHCamPrefab; + public int SplitScreenWidth = 2; + public RaceCameras raceCameras; + + public GameObject racerStatusPrefab; + public RectTransform raceStatusPanel; + int raceStatusWidth = 380; + int raceStatusHeight = 100; + int n_columns = 2; // number of columns in the RaceStatus panel + + public List cars = new List(); + public List cameras = new List(); + + static public GameObject getChildGameObject(GameObject fromGameObject, string withName) + { + //Author: Isaac Dart, June-13. + Transform[] ts = fromGameObject.transform.GetComponentsInChildren(true); + foreach (Transform t in ts) if (t.gameObject.name == withName) return t.gameObject; + + Debug.LogError("couldn't find: " + withName); + return null; + } + + // Find the car for the given JsonTcpClient and remove it from the scene. + public bool RemoveCar(tk.JsonTcpClient client) + { + GameObject toRemove = null; + + foreach (GameObject go in cars) + { + GameObject TcpClientObj = getChildGameObject(go, "TCPClient"); + + if (TcpClientObj != null) + { + tk.TcpCarHandler handler = TcpClientObj.GetComponent(); + + if (handler != null && handler.GetClient() == client) + { + toRemove = go; + } + } + } + + if (toRemove != null) + { + int iSplitScreenCam = cars.IndexOf(toRemove); + if (GlobalState.overheadCamera) { iSplitScreenCam += 1; } + + if (raceCameras != null) + { + int carID = toRemove.GetInstanceID() - 4; + if (raceCameras.carProgress.ContainsKey(carID)) + { + raceCameras.carProgress.Remove(carID); + } + } + + RemoveTimer(toRemove); + cars.Remove(toRemove); + + if (cameras.Count > iSplitScreenCam) + { + GameObject SplitScreenCamGo = cameras[iSplitScreenCam]; + RemoveSplitScreenCam(SplitScreenCamGo); + } + GameObject.Destroy(toRemove); + + Debug.Log("Removed car"); + return true; + } + else + { + Debug.LogError("failed to remove car"); + return false; + + } + } + + public void RemoveGhostCars() + { + foreach (GameObject car in cars) + { + tk.TcpCarHandler tcpCarHandler = car.GetComponentInChildren(); + if (tcpCarHandler != null && tcpCarHandler.IsGhostCar()) + { + tcpCarHandler.Boot(); + } + } + } + + public void RemoveAllCars() + { + // Remove each car one by one + foreach (GameObject car in cars) + { + int i = cars.IndexOf(car); + + if (raceCameras != null) + { + int carID = car.GetInstanceID() - 4; + if (raceCameras.carProgress.ContainsKey(carID)) + { + raceCameras.carProgress.Remove(carID); + } + } + + RemoveTimer(car); + cars.Remove(car); + + if (cameras.Count > i) + { + GameObject SplitScreenCamGo = cameras[i + 1]; + RemoveSplitScreenCam(SplitScreenCamGo); + } + GameObject.Destroy(car); + } + RemoveUiReferences(); + } + + void UpdateRaceStatusPannel() + { + int n_children = raceStatusPanel.transform.childCount; + int row = n_children; + if (row > n_columns) + row = n_columns; + + int col = (n_children / n_columns) + (n_children % n_columns); + float width = row * raceStatusWidth; + float height = col * raceStatusHeight; + raceStatusPanel.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, width); + raceStatusPanel.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, height); + raceStatusPanel.anchoredPosition = new Vector3(8.0f, -1 * height, 0.0f); + } + + public void AddTimer(Timer t, tk.JsonTcpClient client) + { + if (racerStatusPrefab == null) + return; + + GameObject go = Instantiate(racerStatusPrefab) as GameObject; + RaceStatus rs = go.GetComponent(); + rs.Init(t, client); + go.transform.SetParent(raceStatusPanel.transform); + go.transform.GetComponent().localScale = raceStatusPanel.transform.localScale; + + UpdateRaceStatusPannel(); // update the UI with the new child count + Debug.Log("Added timer"); + + } + + public void RemoveTimer(GameObject go) + { + Timer timer = getChildGameObject(go, "Timer").GetComponent(); + + if (timer != null && raceStatusPanel != null) + { + int count = raceStatusPanel.transform.childCount; + for (int i = 0; i < count; i++) + { + Transform child = raceStatusPanel.transform.GetChild(i); + RaceStatus rs = child.GetComponent(); + if (rs.timer == timer) + { + child.transform.SetParent(null); // detach from parent + Destroy(child.gameObject); // destroy child + UpdateRaceStatusPannel(); // update the UI with the new child count + Debug.Log("removed timer"); + return; + } + } + Debug.LogError("failed to find timer while removing it"); + return; + } + Debug.LogError("failed to remove timer"); + } + + public void AddSplitScreenCam(int index) + { + if (cameras.Count < GlobalState.maxSplitScreen && !GlobalState.raceCameras) + { + if (index == 0 && GlobalState.overheadCamera) + { + GameObject splitScreenOHCamGo = Instantiate(splitScreenOHCamPrefab); + OverHeadCamera OHCam = splitScreenOHCamGo.GetComponent(); + OHCam.pathManager = pathManager; + OHCam.Init(); + + cameras.Add(splitScreenOHCamGo); + } + else + { + GameObject splitScreenCamGo = Instantiate(splitScreenCamPrefab); + cameras.Add(splitScreenCamGo); + } + } + } + + public void RemoveSplitScreenCam(GameObject splitScreenCamGo) + { + GameObject.Destroy(splitScreenCamGo); + cameras.Remove(splitScreenCamGo); + UpdateSplitScreenCams(); + Debug.Log("removed split screen camera"); + } + + public void UpdateSplitScreenCams() + { + + if (GlobalState.raceCameras) + { + if (mainCamera != null) { mainCamera.SetActive(false); } + return; + } + + int num_cameras = cars.Count; + if (GlobalState.overheadCamera) { num_cameras += 1; } + if (num_cameras > GlobalState.maxSplitScreen) { num_cameras = GlobalState.maxSplitScreen; } + + // check if the number of cameras match the number of cars + if ((cameras.Count != num_cameras)) + { + // remove all cameras in there + foreach (GameObject splitScreenCamGo in cameras) + { + GameObject.Destroy(splitScreenCamGo); + } + cameras.Clear(); + + // and recreate some new ones + for (int i = 0; i < num_cameras; i++) + { + AddSplitScreenCam(i); + } + } + + // for each camera, update the rect + for (int i = 0; i < num_cameras; i++) + { + + if (i > 0 || !GlobalState.overheadCamera) // if the camera isn't overhead, assign a car to it + { + GameObject splitScreenCamGo = cameras[i]; + GameObject car; + if (GlobalState.overheadCamera) { car = cars[i - 1]; } + else { car = cars[i]; } + + // set target to the corresponding car + Camera splitScreenCam = splitScreenCamGo.GetComponent(); + + DrawLidar dLidar = splitScreenCamGo.GetComponent(); + dLidar.car = car; + + CameraFollow cameraFollow = splitScreenCam.GetComponent(); + cameraFollow.target = getChildGameObject(car, "CameraFollowTm").transform; + } + + int x_index = i % SplitScreenWidth; + int y_index = i / SplitScreenWidth; + int number_in_row = Math.Min((cameras.Count - y_index * SplitScreenWidth), SplitScreenWidth); + int number_of_row = 1 + ((cameras.Count - 1) / SplitScreenWidth); + + float w = 1 / (float)(number_in_row); + float h = 1 / (float)(number_of_row); + + float x = (x_index) / (float)number_in_row; + float y = (y_index) / (float)number_of_row; + + GameObject go = cameras[i]; + Camera camera = go.GetComponent(); + camera.rect = new Rect(x, y, w, h); + + if (GlobalState.overheadCamera && i == 0) { OverHeadCamera ohcam = go.GetComponent(); ohcam.Init(); } + } + + if (cameras.Count == 0 && mainCamera != null && !GlobalState.raceCameras) + { + mainCamera.SetActive(true); + } + else if (mainCamera != null) + { + mainCamera.SetActive(false); // make sure we are disabling main camera to avoid background rendering + } + } + + public void CarTextFacecamera(GameObject car, Transform target) + { + GameObject carNameObj = getChildGameObject(car, "CarName"); + + if (!carNameObj) + return; + + FaceTarget ft = carNameObj.GetComponent(); + + if (!ft) + return; + + ft.target = target; + + } + + public bool IsOccupied(Vector3 pos) + { + int carCount = cars.Count - 1; + + for (int iCar = 0; iCar < carCount; iCar++) + { + GameObject go = cars[iCar]; + Car car = go.GetComponent(); + if (Vector3.Distance(car.startPos, pos) < 1.0f) + return true; + } + + return false; + } + + public (Vector3, Quaternion) GetStartPosRot(int iCar) + { + + int iSpawn = iCar % startsTm.Length; + Transform spawn = startsTm[iSpawn]; + Vector3 pos = spawn.position; + Quaternion rot = spawn.rotation; + + int iCol = (iCar / startsTm.Length) % numCarRows; + int iRow = (iCar / startsTm.Length) / numCarRows; + + Vector3 offset = Vector3.zero; + offset.z = -distCarRows * iRow; + offset.x = -distCarCols * iCol; + + return (spawn.position + rot * offset, rot); + } + + public (Vector3, Quaternion) GetCarStartPosRot() + { + + Vector3 startPos = startsTm[0].position; // default position + Quaternion startRot = startsTm[0].rotation; // default rotation + + if (IsOccupied(startPos)) + { + int iCar = 0; + while (IsOccupied(startPos)) + { + (startPos, startRot) = GetStartPosRot(iCar); + iCar++; + } + } + + return (startPos, startRot); + } + + + public GameObject Spawn(tk.JsonTcpClient client, bool paceCar) + { + if (carPrefab == null) + { + Debug.LogError("No carPrefab set in CarSpawner!"); + return null; + } + + // Create a car object, and also hook up all the connections + // to various places in game that need to hook into the car. + GameObject go = GameObject.Instantiate(carPrefab) as GameObject; + + if (go == null) + { + Debug.LogError("CarSpawner failed to instantiate prefab!"); + return null; + } + + cars.Add(go); + + (Vector3 startPos, Quaternion startRot) = GetCarStartPosRot(); + go.transform.SetPositionAndRotation(startPos, startRot); + go.GetComponent().SavePosRot(); + UpdateSplitScreenCams(); + + GameObject TcpClientObj = getChildGameObject(go, "TCPClient"); + + + // CarTextFacecamera(go, cam.transform); + + if (TcpClientObj != null) + { + // without this it will not connect. + TcpClientObj.SetActive(true); + + // now set the connection settings. + TcpCarHandler carHandler = TcpClientObj.GetComponent(); + + if (carHandler != null) + carHandler.Init(client); + } + + if (OnNewCarCB != null) + OnNewCarCB.Invoke(go); + + /////////////////////////////////////////////// + // Search scene to find these. + MenuHandler menuHandler = GameObject.FindObjectOfType(); + Canvas canvas = GameObject.FindObjectOfType(); + GameObject panelMenu = getChildGameObject(canvas.gameObject, "Panel Menu"); + GameObject pidPanel = getChildGameObject(canvas.gameObject, "PIDPanel"); + /////////////////////////////////////////////// + + // set camera target follow tm + + // Set menu handler hooks + if (menuHandler != null) + { + menuHandler.PIDContoller = getChildGameObject(go, "PIDController"); + menuHandler.Logger = getChildGameObject(go, "Logger"); + menuHandler.NetworkSteering = getChildGameObject(go, "TCPClient"); + menuHandler.carJSControl = getChildGameObject(go, "JoyStickCarContoller"); + menuHandler.trainingManager = getChildGameObject(go, "TrainingManager").GetComponent(); + menuHandler.trainingManager.carObj = go; + + if (EnableTrainingManager) + { + menuHandler.trainingManager.gameObject.SetActive(true); + + getChildGameObject(go, "OverheadViewSphere").SetActive(true); + } + + if (GlobalState.bAutoHideSceneMenu && panelMenu != null) + { + panelMenu.SetActive(false); + } + } + + if (paceCar && !GlobalState.manualDriving) + { + GameObject pidController_go = getChildGameObject(go, "PIDController"); + pidController_go.SetActive(true); + } + else if (paceCar && GlobalState.manualDriving) + { + GameObject jsController = getChildGameObject(go, "JoyStickCarContoller"); + jsController.SetActive(true); + } + + // Add race status, if possible. + GameObject to = getChildGameObject(go, "Timer"); + + if (to != null) + { + AddTimer(to.GetComponent(), client); + } + else + { + Debug.LogError("failed to find Timer"); + } + + return go; + } + + internal void EnsureOneCar() + { + // pace car doesn't always mean cars.Count = 0, so will need to refactor that + if (cars.Count == 0) + Spawn(null, GlobalState.paceCar); + } + + public void RemoveUiReferences() + { + Camera cam = Camera.main; + + /////////////////////////////////////////////// + // Search scene to find these. + CameraFollow cameraFollow = cam.transform.GetComponent(); + MenuHandler menuHandler = GameObject.FindObjectOfType(); + Canvas canvas = GameObject.FindObjectOfType(); + GameObject panelMenu = getChildGameObject(canvas.gameObject, "Panel Menu"); + PID_UI pid_ui = null; + GameObject pidPanel = getChildGameObject(canvas.gameObject, "PIDPanel"); + /////////////////////////////////////////////// + + if (pidPanel) + pid_ui = pidPanel.GetComponent(); + + // set camera target follow tm + if (cameraFollow != null) + cameraFollow.target = null; + + // Set menu handler hooks + if (menuHandler != null) + { + menuHandler.PIDContoller = null; + menuHandler.Logger = null; + menuHandler.NetworkSteering = null; + menuHandler.carJSControl = null; + menuHandler.trainingManager = null; + } + + // Set the PID ui hooks + if (pid_ui != null) + { + pid_ui.pid = null; + pid_ui.logger = null; + } + + } + +} diff --git a/Scripts/CarSpawner.cs.meta b/Scripts/CarSpawner.cs.meta new file mode 100755 index 0000000..643f91e --- /dev/null +++ b/Scripts/CarSpawner.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f7b37e7667e6449eb95452eb5655f48f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/CollisionSensor.cs b/Scripts/CollisionSensor.cs new file mode 100755 index 0000000..9c4dd73 --- /dev/null +++ b/Scripts/CollisionSensor.cs @@ -0,0 +1,27 @@ +using UnityEngine; +using System.Collections; + +public class CollisionSensor : MonoBehaviour { + + public delegate void OnCollideCB(string objType); + + public OnCollideCB collideCB; + + + public void OnCollisionEnter(Collision collision) + { + if(collideCB != null) + { + string objType = "none"; + + foreach (ContactPoint contact in collision.contacts) + { + objType = contact.otherCollider.gameObject.tag; + //Debug.DrawRay(contact.point, contact.normal, Color.white); + } + + collideCB.Invoke(objType); + } + } + +} diff --git a/Scripts/CollisionSensor.cs.meta b/Scripts/CollisionSensor.cs.meta new file mode 100755 index 0000000..b7441d0 --- /dev/null +++ b/Scripts/CollisionSensor.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 3f04f1d47c6d243908093d88e03f355c +timeCreated: 1449512093 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/DrawLidar.cs b/Scripts/DrawLidar.cs new file mode 100755 index 0000000..973589e --- /dev/null +++ b/Scripts/DrawLidar.cs @@ -0,0 +1,72 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class DrawLidar : MonoBehaviour +{ + public Lidar lidar; + public GameObject car; + static Material lineMaterial; + + void OnPostRender() + { + if (GlobalState.drawLidar == true) + { + if (car == null) { return; } + if (lidar == null) + { + lidar = car.GetComponentInChildren(); + return; + } + else if (lidar.enabled == false) { return; } + Draw(); + } + } + + static void CreateLineMaterial() + { + if (!lineMaterial) + { + // Unity has a built-in shader that is useful for drawing + // simple colored things. + Shader shader = Shader.Find("Hidden/Internal-Colored"); + lineMaterial = new Material(shader); + lineMaterial.hideFlags = HideFlags.HideAndDontSave; + // Turn on alpha blending + lineMaterial.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha); + lineMaterial.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha); + // Turn backface culling off + lineMaterial.SetInt("_Cull", (int)UnityEngine.Rendering.CullMode.Off); + // Turn off depth writes + lineMaterial.SetInt("_ZWrite", 0); + } + } + + public void Draw() + { + CreateLineMaterial(); + lineMaterial.SetPass(0); // Apply the line material + + GL.Begin(GL.LINES); // Draw lines + + if (lidar.pointArr == null) + return; + + Vector3 lidarPos = lidar.transform.position; + foreach (LidarPoint p in lidar.pointArr.points) + { + if (p == null) + continue; + + GL.Color(new Color(1, 0, 0, 0.8F)); + + Vector3 ppoint = new Vector3(p.x, p.y, p.z) + lidarPos; + + GL.Vertex3(lidarPos.x, lidarPos.y, lidarPos.z); + GL.Vertex3(ppoint.x, ppoint.y, ppoint.z); + } + + GL.End(); + + } +} diff --git a/Scripts/DrawLidar.cs.meta b/Scripts/DrawLidar.cs.meta new file mode 100755 index 0000000..7455e8b --- /dev/null +++ b/Scripts/DrawLidar.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d34136b32b3d9f64fb84431d5f971b9e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor.meta b/Scripts/Editor.meta new file mode 100755 index 0000000..731c3d3 --- /dev/null +++ b/Scripts/Editor.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 4873f492b6b4141b1be922502923dcee +folderAsset: yes +timeCreated: 1503686012 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/BundleBuilder.cs b/Scripts/Editor/BundleBuilder.cs new file mode 100755 index 0000000..8f7c73d --- /dev/null +++ b/Scripts/Editor/BundleBuilder.cs @@ -0,0 +1,38 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; + +public class BundleBuilder : Editor +{ + [MenuItem("Build/ Build All AssetsBundles")] + static void BuildAllAssetsBundles() + { + BuildTarget[] platforms = new BuildTarget[] { BuildTarget.StandaloneWindows64, BuildTarget.StandaloneLinux64, BuildTarget.StandaloneOSX }; + foreach (BuildTarget platform in platforms) + { + BuildAssetBundles(platform); + } + } + + [MenuItem("Build/ Build Active Target AssetsBundles")] + static void BuildCurrentTargetAssetsBundles() + { + BuildTarget platform = EditorUserBuildSettings.activeBuildTarget; + BuildAssetBundles(platform); + } + + + static void BuildAssetBundles(BuildTarget platform) + { + string path = System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), "Assets/AssetBundles/" + platform.ToString()); + + if (System.IO.Directory.Exists(path) == false) + { + System.IO.Directory.CreateDirectory(path); + Debug.LogFormat("creating directory {0}", path); + } + + BuildPipeline.BuildAssetBundles(path, BuildAssetBundleOptions.ChunkBasedCompression, platform); + } +} diff --git a/Scripts/Editor/BundleBuilder.cs.meta b/Scripts/Editor/BundleBuilder.cs.meta new file mode 100755 index 0000000..80bd629 --- /dev/null +++ b/Scripts/Editor/BundleBuilder.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7ce8d5283b7d033478808c087bb7704d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/ConeChallengeEditor.cs b/Scripts/Editor/ConeChallengeEditor.cs new file mode 100755 index 0000000..cdd118e --- /dev/null +++ b/Scripts/Editor/ConeChallengeEditor.cs @@ -0,0 +1,19 @@ +using UnityEngine; +using UnityEditor; +using System; + +[CustomEditor(typeof(ConeChallenge))] +public class ConeChallengeEditor : Editor +{ + public override void OnInspectorGUI() + { + DrawDefaultInspector(); + + ConeChallenge challenge = (ConeChallenge)target; + + if (GUILayout.Button("Reset Challenge")) + { + challenge.ResetChallenge(); + } + } +} \ No newline at end of file diff --git a/Scripts/Editor/ConeChallengeEditor.cs.meta b/Scripts/Editor/ConeChallengeEditor.cs.meta new file mode 100755 index 0000000..fc7bff2 --- /dev/null +++ b/Scripts/Editor/ConeChallengeEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6d921735578446241a54a0dd088e0a5a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/LightChallengeEditor.cs b/Scripts/Editor/LightChallengeEditor.cs new file mode 100755 index 0000000..4b7b909 --- /dev/null +++ b/Scripts/Editor/LightChallengeEditor.cs @@ -0,0 +1,18 @@ +using UnityEngine; +using UnityEditor; +using System; + +[CustomEditor(typeof(LightChallenge))] +public class LightChallengeEditor : Editor +{ + public override void OnInspectorGUI() + { + DrawDefaultInspector(); + LightChallenge challenge = (LightChallenge)target; + + if (GUILayout.Button("Reset Challenge")) + { + challenge.ResetChallenge(); + } + } +} \ No newline at end of file diff --git a/Scripts/Editor/LightChallengeEditor.cs.meta b/Scripts/Editor/LightChallengeEditor.cs.meta new file mode 100755 index 0000000..06e218d --- /dev/null +++ b/Scripts/Editor/LightChallengeEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fd73e76678e66e446ae14987ca82acc7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/PlayerBuilder.cs b/Scripts/Editor/PlayerBuilder.cs new file mode 100755 index 0000000..ca28a5f --- /dev/null +++ b/Scripts/Editor/PlayerBuilder.cs @@ -0,0 +1,121 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using UnityEditor.Build.Reporting; + +public class PlayerBuilder : Editor +{ + + [MenuItem("Build/ Build Every Player")] + static void Build() + { + WinBuild(); + MacBuild(); + LinuxBuild(); + } + + [MenuItem("Build/ Build Windows Player")] + static void WinBuild() + { + EditorBuildSettingsScene[] scenes = EditorBuildSettings.scenes; + + BuildReport report = BuildPipeline.BuildPlayer(scenes, "Builds/DonkeySimWin/donkey_sim.exe", BuildTarget.StandaloneWindows64, BuildOptions.None); + BuildSummary summary = report.summary; + + + if (summary.result == BuildResult.Succeeded) + { + Debug.Log("Build succeeded: " + summary.totalSize + " bytes"); + + string assetBundlePath = "Assets/AssetBundles/" + BuildTarget.StandaloneWindows64.ToString(); + string destPath = "Builds/DonkeySimWin/donkey_sim_Data/StreamingAssets/"; + if (System.IO.Directory.Exists(assetBundlePath)) + { + CopyDirectory(assetBundlePath, destPath); + } + else + { + Debug.LogWarning("Asset bundle directory missing, skipping copy: " + assetBundlePath); + } + } + + if (summary.result == BuildResult.Failed) + { + Debug.Log("Build failed"); + } + } + [MenuItem("Build/ Build MacOS Player")] + static void MacBuild() + { + EditorBuildSettingsScene[] scenes = EditorBuildSettings.scenes; + + BuildReport report = BuildPipeline.BuildPlayer(scenes, "Builds/DonkeySimMac/donkey_sim.app", BuildTarget.StandaloneOSX, BuildOptions.None); + BuildSummary summary = report.summary; + + if (summary.result == BuildResult.Succeeded) + { + Debug.Log("Build succeeded: " + summary.totalSize + " bytes"); + + string assetBundlePath = "Assets/AssetBundles/" + BuildTarget.StandaloneOSX.ToString(); + string destPath = "Builds/DonkeySimMac/donkey_sim.app/Contents/Resources/Data/StreamingAssets"; + if (System.IO.Directory.Exists(assetBundlePath)) + { + CopyDirectory(assetBundlePath, destPath); + } + else + { + Debug.LogWarning("Asset bundle directory missing, skipping copy: " + assetBundlePath); + } + } + + if (summary.result == BuildResult.Failed) + { + Debug.Log("Build failed"); + } + } + [MenuItem("Build/ Build Linux Player")] + static void LinuxBuild() + { + EditorBuildSettingsScene[] scenes = EditorBuildSettings.scenes; + + BuildReport report = BuildPipeline.BuildPlayer(scenes, "Builds/DonkeySimLinux/donkey_sim.x86_64", BuildTarget.StandaloneLinux64, BuildOptions.None); + BuildSummary summary = report.summary; + + if (summary.result == BuildResult.Succeeded) + { + Debug.Log("Build succeeded: " + summary.totalSize + " bytes"); + + string assetBundlePath = "Assets/AssetBundles/" + BuildTarget.StandaloneLinux64.ToString(); + string destPath = "Builds/DonkeySimLinux/donkey_sim_Data/StreamingAssets"; + if (System.IO.Directory.Exists(assetBundlePath)) + { + CopyDirectory(assetBundlePath, destPath); + } + else + { + Debug.LogWarning("Asset bundle directory missing, skipping copy: " + assetBundlePath); + } + } + + if (summary.result == BuildResult.Failed) + { + Debug.Log("Build failed"); + } + } + + + static void CopyDirectory(string SourcePath, string DestinationPath) + { + + if (!System.IO.Directory.Exists(DestinationPath)) { System.IO.Directory.CreateDirectory(DestinationPath); } + + foreach (string dirPath in System.IO.Directory.GetDirectories(SourcePath, "*", + System.IO.SearchOption.AllDirectories)) + System.IO.Directory.CreateDirectory(dirPath.Replace(SourcePath, DestinationPath)); + + foreach (string newPath in System.IO.Directory.GetFiles(SourcePath, "*.*", + System.IO.SearchOption.AllDirectories)) + System.IO.File.Copy(newPath, newPath.Replace(SourcePath, DestinationPath), true); + } +} diff --git a/Scripts/Editor/PlayerBuilder.cs.meta b/Scripts/Editor/PlayerBuilder.cs.meta new file mode 100755 index 0000000..ac1f963 --- /dev/null +++ b/Scripts/Editor/PlayerBuilder.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0d217cd5ee5171749afe98509b2e1498 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/RandAssetsChallengeEditor.cs b/Scripts/Editor/RandAssetsChallengeEditor.cs new file mode 100755 index 0000000..93b6ab4 --- /dev/null +++ b/Scripts/Editor/RandAssetsChallengeEditor.cs @@ -0,0 +1,19 @@ +using UnityEngine; +using UnityEditor; +using System; + +[CustomEditor(typeof(RandAssetsChallenge))] +public class RandAssetsChallengeEditor : Editor +{ + public override void OnInspectorGUI() + { + DrawDefaultInspector(); + + RandAssetsChallenge challenge = (RandAssetsChallenge)target; + + if (GUILayout.Button("Reset Challenge")) + { + challenge.ResetChallenge(); + } + } +} \ No newline at end of file diff --git a/Scripts/Editor/RandAssetsChallengeEditor.cs.meta b/Scripts/Editor/RandAssetsChallengeEditor.cs.meta new file mode 100755 index 0000000..6a20bb2 --- /dev/null +++ b/Scripts/Editor/RandAssetsChallengeEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f4ecfc59e1e41924a9e9d0ca85bb2cea +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/RepeatObjectAlongEditor.cs b/Scripts/Editor/RepeatObjectAlongEditor.cs new file mode 100755 index 0000000..eade366 --- /dev/null +++ b/Scripts/Editor/RepeatObjectAlongEditor.cs @@ -0,0 +1,31 @@ +using UnityEngine; +using UnityEditor; +using System; + +[CustomEditor(typeof(RepeatObjectAlong))] +public class RepeatObjectAlongEditor : Editor +{ + public override void OnInspectorGUI() + { + DrawDefaultInspector(); + + RepeatObjectAlong repeatObjectAlong = (RepeatObjectAlong)target; + + if (GUILayout.Button("Generate Mesh (only at runtime)")) + { + repeatObjectAlong.Generate(); + } + + if (GUILayout.Button("Save Mesh")) + { + MeshFilter mf = repeatObjectAlong.GetComponent(); + Mesh mesh = mf.sharedMesh; + if (mesh == null) + { + Debug.LogWarning("Mesh is null, creating a new one"); + mesh = new Mesh(); + } + AssetDatabase.CreateAsset(mesh, repeatObjectAlong.savePath); + } + } +} \ No newline at end of file diff --git a/Scripts/Editor/RepeatObjectAlongEditor.cs.meta b/Scripts/Editor/RepeatObjectAlongEditor.cs.meta new file mode 100755 index 0000000..90edab1 --- /dev/null +++ b/Scripts/Editor/RepeatObjectAlongEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ac720408aac926645a88c7df0f96e232 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/RoadBuilderEditor.cs b/Scripts/Editor/RoadBuilderEditor.cs new file mode 100755 index 0000000..0231a64 --- /dev/null +++ b/Scripts/Editor/RoadBuilderEditor.cs @@ -0,0 +1,20 @@ +using UnityEngine; +using UnityEditor; +using System; + +[CustomEditor(typeof(RoadBuilder))] +public class RoadBuilderEditor : Editor +{ + public override void OnInspectorGUI() + { + DrawDefaultInspector(); + + RoadBuilder roadBuilder = (RoadBuilder)target; + + if (GUILayout.Button("Save Mesh") && roadBuilder.createdRoad != null) + { + MeshFilter mf = roadBuilder.createdRoad.GetComponent(); + AssetDatabase.CreateAsset(mf.mesh, roadBuilder.savePath); + } + } +} \ No newline at end of file diff --git a/Scripts/Editor/RoadBuilderEditor.cs.meta b/Scripts/Editor/RoadBuilderEditor.cs.meta new file mode 100755 index 0000000..fc3653a --- /dev/null +++ b/Scripts/Editor/RoadBuilderEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 87400f6f678666347be1cbc8553f01c5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/UiTools.cs b/Scripts/Editor/UiTools.cs new file mode 100755 index 0000000..df76860 --- /dev/null +++ b/Scripts/Editor/UiTools.cs @@ -0,0 +1,33 @@ +using UnityEngine; +using System.Collections; +using UnityEditor; + +public class UiTools : MonoBehaviour { + + [MenuItem("UiHelpers/Anchors to Corners %[")] + static void AnchorsToCorners(){ + RectTransform t = Selection.activeTransform as RectTransform; + RectTransform pt = Selection.activeTransform.parent as RectTransform; + + if(t == null || pt == null) return; + + Vector2 newAnchorsMin = new Vector2(t.anchorMin.x + t.offsetMin.x / pt.rect.width, + t.anchorMin.y + t.offsetMin.y / pt.rect.height); + Vector2 newAnchorsMax = new Vector2(t.anchorMax.x + t.offsetMax.x / pt.rect.width, + t.anchorMax.y + t.offsetMax.y / pt.rect.height); + + t.anchorMin = newAnchorsMin; + t.anchorMax = newAnchorsMax; + t.offsetMin = t.offsetMax = new Vector2(0, 0); + } + + [MenuItem("UiHelpers/Corners to Anchors %]")] + static void CornersToAnchors(){ + RectTransform t = Selection.activeTransform as RectTransform; + + if(t == null) return; + + t.offsetMin = t.offsetMax = new Vector2(0, 0); + } + +} diff --git a/Scripts/Editor/UiTools.cs.meta b/Scripts/Editor/UiTools.cs.meta new file mode 100755 index 0000000..b70e8b3 --- /dev/null +++ b/Scripts/Editor/UiTools.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: e7917617b75a2884d91c365a562495e0 +timeCreated: 1498592895 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/GenPathFromDriving.cs b/Scripts/GenPathFromDriving.cs new file mode 100755 index 0000000..18579a0 --- /dev/null +++ b/Scripts/GenPathFromDriving.cs @@ -0,0 +1,59 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using System.IO; + +public class GenPathFromDriving : MonoBehaviour { + + public GameObject carObj; + public ICar car; + private StreamWriter writer; + public string outputFilename; + public float sampleDist = 2.0f; + Vector3 lastSample = Vector3.zero; + + void Awake() + { + car = carObj.GetComponent(); + + if(car != null) + { + string filename = Application.dataPath + "/Resources/" + outputFilename; + + writer = new StreamWriter(filename); + + Debug.Log("Opening file for path at: " + filename); + } + + } + + // Update is called once per frame + void Update () + { + if(writer != null) + { + Vector3 p = carObj.transform.position; + + if((p - lastSample).magnitude > sampleDist) + { + lastSample = p; + writer.WriteLine(string.Format("{0},{1},{2}", p.x, p.y, p.z)); + } + } + } + + public void Shutdown() + { + if(writer != null) + { + writer.Close(); + writer = null; + } + } + + void OnDestroy() + { + Shutdown(); + } + +} diff --git a/Scripts/GenPathFromDriving.cs.meta b/Scripts/GenPathFromDriving.cs.meta new file mode 100755 index 0000000..a8a7d74 --- /dev/null +++ b/Scripts/GenPathFromDriving.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 803f435a5637a4143b85fa3e1ad3f5cf +timeCreated: 1486432165 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/GlobalState.cs b/Scripts/GlobalState.cs new file mode 100755 index 0000000..8226f0a --- /dev/null +++ b/Scripts/GlobalState.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using UnityEngine; + + +public static class GlobalState +{ + public static string version = "v25.06.10"; + public static string host = "0.0.0.0"; + public static int port = 9091; + public static int portPrivateAPI = 9092; + public static int fps = 60; + public static float timeScale = 1.0f; + public static int maxSplitScreen = 4; + public static bool bAutoHideSceneMenu = false; + + // should we create a car even though we don't have a network client? + public static bool bCreateCarWithoutNetworkClient = false; + public static string log_path = "default"; + public static bool extendedTelemetry = true; + public static bool generateTrees = true; + public static bool generateRandomCones = true; + public static bool randomLight = true; + public static bool overheadCamera = false; + public static bool raceCameras = false; + public static bool paceCar = false; + public static bool manualDriving = false; + public static float kp = 5.0f; + public static float kd = 5.0f; + public static float ki = 0.0f; + public static string privateKey = ""; + public static bool useSeed = false; + public static int seed = 20432814; + public static string additionnalContentPath = ""; + public static string[] sceneNames; + public static List bundleScenes = new List(); + public static bool drawLidar = true; + public static float timeOut = 300f; +} diff --git a/Scripts/GlobalState.cs.meta b/Scripts/GlobalState.cs.meta new file mode 100755 index 0000000..805c29a --- /dev/null +++ b/Scripts/GlobalState.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 91e0f36bb9310904886d185248b951c3 +timeCreated: 1516308787 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/GlobalStateEditor.cs b/Scripts/GlobalStateEditor.cs new file mode 100755 index 0000000..2655ae2 --- /dev/null +++ b/Scripts/GlobalStateEditor.cs @@ -0,0 +1,355 @@ +using UnityEngine; + +public class GlobalStateEditor : MonoBehaviour +{ + + public Rect rect; + private Vector2 scrollPosition = Vector2.zero; + + public int port + { + get { return GlobalState.port; } + set { GlobalState.port = value; } + } + public int portPrivateAPI + { + get { return GlobalState.portPrivateAPI; } + set { GlobalState.portPrivateAPI = value; } + } + public int fps + { + get { return GlobalState.fps; } + set { GlobalState.fps = value; } + } + public int maxSplitScreen + { + get { return GlobalState.maxSplitScreen; } + set { GlobalState.maxSplitScreen = value; } + } + public bool generateTrees + { + get { return GlobalState.generateTrees; } + set { GlobalState.generateTrees = value; } + } + public bool extendedTelemetry + { + get { return GlobalState.extendedTelemetry; } + set { GlobalState.extendedTelemetry = value; } + } + public bool generateRandomCones + { + get { return GlobalState.generateRandomCones; } + set { GlobalState.generateRandomCones = value; } + } + public bool randomLight + { + get { return GlobalState.randomLight; } + set { GlobalState.randomLight = value; } + } + public bool raceCameras + { + get { return GlobalState.raceCameras; } + set { GlobalState.raceCameras = value; } + } + public bool overheadCamera + { + get { return GlobalState.overheadCamera; } + set { GlobalState.overheadCamera = value; } + } + public bool drawLidar + { + get { return GlobalState.drawLidar; } + set { GlobalState.drawLidar = value; } + } + public bool paceCar + { + get { return GlobalState.paceCar; } + set { GlobalState.paceCar = value; } + } + public bool manualDriving + { + get { return GlobalState.manualDriving; } + set { GlobalState.manualDriving = value; } + } + public float kp + { + get { return GlobalState.kp; } + set { GlobalState.kp = value; } + } + public float kd + { + get { return GlobalState.kd; } + set { GlobalState.kd = value; } + } + public float ki + { + get { return GlobalState.ki; } + set { GlobalState.ki = value; } + } + public bool useSeed + { + get { return GlobalState.useSeed; } + set { GlobalState.useSeed = value; } + } + public int seed + { + get { return GlobalState.seed; } + set { GlobalState.seed = value; } + } + public string privateKey + { + get { return GlobalState.privateKey; } + set { GlobalState.privateKey = value; } + } + public string additionnalContentPath + { + get { return GlobalState.additionnalContentPath; } + set { GlobalState.additionnalContentPath = value; } + } + public float timeScale + { + get { return GlobalState.timeScale; } + set { GlobalState.timeScale = value; Time.timeScale = value; } + } + public float timeOut + { + get { return GlobalState.timeOut; } + set { GlobalState.timeOut = value; } + } + + private bool showPrivateKey = false; + private VersionCheck versionCheck; + void Awake() + { + LoadPlayerPrefs(); + SaveToPlayerPrefs(); + + versionCheck = gameObject.GetComponent(); + + //keep it processing even when not in focus. + Application.runInBackground = true; + + //Set desired frame rate as high as possible. + Application.targetFrameRate = GlobalState.fps; + } + + void OnGUI() + { + int pixXOffset = (int)(rect.x * Screen.width); + int pixYOffset = (int)(rect.y * Screen.height); + int width = (int)(rect.width * Screen.width); + int height = (int)(rect.height * Screen.height); + + int LabelXOffset = 100; + + int scrollHeight = 220; + int scrollWidth = 200; + + int YOffset = 0; + int Ysteps = 20; + + + GUI.BeginGroup(new Rect(pixXOffset, pixYOffset, width, height)); + scrollPosition = GUI.BeginScrollView(new Rect(0, 0, width, height), scrollPosition, new Rect(0, 0, scrollWidth, scrollHeight), false, false); + + GUI.Label(new Rect(0, YOffset, LabelXOffset, 20), "port"); + string portString = GUI.TextField(new Rect(LabelXOffset, YOffset, width-LabelXOffset, 20), port.ToString()); + int tmp_port = port; + int.TryParse(portString, out tmp_port); + if (tmp_port != port) + port = tmp_port; + YOffset += Ysteps; + + GUI.Label(new Rect(0, YOffset, LabelXOffset, 20), "portPrivateAPI"); + string portPrivateAPIString = GUI.TextField(new Rect(LabelXOffset, YOffset, width-LabelXOffset, 20), portPrivateAPI.ToString()); + int tmp_portPrivateAPI = portPrivateAPI; + int.TryParse(portPrivateAPIString, out tmp_portPrivateAPI); + if (tmp_portPrivateAPI != portPrivateAPI) + portPrivateAPI = tmp_portPrivateAPI; + YOffset += Ysteps; + + GUI.Label(new Rect(0, YOffset, LabelXOffset, 20), "FPS limit"); + string fpsString = GUI.TextField(new Rect(LabelXOffset, YOffset, width-LabelXOffset, 20), fps.ToString()); + int tmp_fps = fps; + int.TryParse(fpsString, out tmp_fps); + if (tmp_fps != fps) + fps = tmp_fps; + YOffset += Ysteps; + + GUI.Label(new Rect(0, YOffset, LabelXOffset, 20), "Time scale"); + string timeScaleString = GUI.TextField(new Rect(LabelXOffset, YOffset, width-LabelXOffset, 20), timeScale.ToString()); + float tmp_timeScale = timeScale; + float.TryParse(timeScaleString, out tmp_timeScale); + if (tmp_timeScale != timeScale) + timeScale = tmp_timeScale; + YOffset += Ysteps; + + GUI.Label(new Rect(0, YOffset, LabelXOffset, 20), "Time out"); + string timeOutString = GUI.TextField(new Rect(LabelXOffset, YOffset, width-LabelXOffset, 20), timeOut.ToString()); + float tmp_timeOut = timeOut; + float.TryParse(timeOutString, out tmp_timeOut); + if (tmp_timeOut != timeOut) + timeOut = tmp_timeOut; + YOffset += Ysteps; + + GUI.Label(new Rect(0, YOffset, LabelXOffset, 20), "Max SplitScreen"); + string maxspString = GUI.TextField(new Rect(LabelXOffset, YOffset, width-LabelXOffset, 20), maxSplitScreen.ToString()); + int tmp_maxsp = maxSplitScreen; + int.TryParse(maxspString, out tmp_maxsp); + if (tmp_maxsp != maxSplitScreen) + maxSplitScreen = tmp_maxsp; + YOffset += Ysteps; + + extendedTelemetry = GUI.Toggle(new Rect(0, YOffset, width, 20), extendedTelemetry, "extendedTelemetry"); + YOffset += Ysteps; + generateTrees = GUI.Toggle(new Rect(0, YOffset, width, 20), generateTrees, "generateTrees"); + YOffset += Ysteps; + generateRandomCones = GUI.Toggle(new Rect(0, YOffset, width, 20), generateRandomCones, "generateRandomCones"); + YOffset += Ysteps; + randomLight = GUI.Toggle(new Rect(0, YOffset, width, 20), randomLight, "randomLight"); + YOffset += Ysteps; + raceCameras = GUI.Toggle(new Rect(0, YOffset, width, 20), raceCameras, "raceCameras"); + YOffset += Ysteps; + overheadCamera = GUI.Toggle(new Rect(0, YOffset, width, 20), overheadCamera, "overheadCamera"); + YOffset += Ysteps; + drawLidar = GUI.Toggle(new Rect(0, YOffset, width, 20), drawLidar, "drawLidar"); + YOffset += Ysteps; + paceCar = GUI.Toggle(new Rect(0, YOffset, width, 20), paceCar, "paceCar"); + YOffset += Ysteps; + if (paceCar) + { + manualDriving = GUI.Toggle(new Rect(Ysteps, YOffset, width, 20), manualDriving, "manualDriving"); + YOffset += Ysteps; + + if (!manualDriving) + { + GUI.Label(new Rect(Ysteps, YOffset, LabelXOffset, 20), "kp"); + string kpString = GUI.TextField(new Rect(LabelXOffset+Ysteps, YOffset, width-LabelXOffset-Ysteps, 20), kp.ToString()); + float tmp_kp = kp; + float.TryParse(kpString, out tmp_kp); + if (tmp_kp != kp) + kp = tmp_kp; + YOffset += Ysteps; + + GUI.Label(new Rect(Ysteps, YOffset, LabelXOffset, 20), "kd"); + string kdString = GUI.TextField(new Rect(LabelXOffset+Ysteps, YOffset, width-LabelXOffset-Ysteps, 20), kd.ToString()); + float tmp_kd = kd; + float.TryParse(kdString, out tmp_kd); + if (tmp_kd != kd) + kd = tmp_kd; + YOffset += Ysteps; + + GUI.Label(new Rect(Ysteps, YOffset, LabelXOffset, 20), "ki"); + string kiString = GUI.TextField(new Rect(LabelXOffset+Ysteps, YOffset, width-LabelXOffset-Ysteps, 20), ki.ToString()); + float tmp_ki = ki; + float.TryParse(kiString, out tmp_ki); + if (tmp_ki != ki) + ki = tmp_ki; + YOffset += Ysteps; + } + } + + useSeed = GUI.Toggle(new Rect(0, YOffset, width, 20), useSeed, "useSeed"); + YOffset += Ysteps; + if (useSeed) + { + GUI.Label(new Rect(0, YOffset, LabelXOffset, 20), "Seed"); + string seedString = GUI.TextField(new Rect(LabelXOffset, YOffset, width, 20), seed.ToString()); + YOffset += Ysteps; + int tmp_seed = seed; + int.TryParse(seedString, out tmp_seed); + if (tmp_seed != seed) + seed = tmp_seed; + } + + YOffset += Ysteps; + bool doSave = GUI.Button(new Rect(0, YOffset, width, 20), "Save"); + YOffset += Ysteps; + + // Check if the version used is the latest version if not, notify the user ! + if (versionCheck.latest != GlobalState.version) + { + YOffset += Ysteps; + bool getLatest = GUI.Button(new Rect(0, YOffset, width, Ysteps * 2), "A new version is available, \n click here to get latest version !"); + YOffset += Ysteps * 2; + if (getLatest) { versionCheck.GetLatestVersion(); } + } + + YOffset += Ysteps; + showPrivateKey = GUI.Toggle(new Rect(0, YOffset, width, 20), showPrivateKey, "showPrivateKey"); + YOffset += Ysteps; + if (showPrivateKey) + { + GUI.Label(new Rect(0, YOffset, LabelXOffset, 20), "Private API Key"); + privateKey = GUI.TextField(new Rect(LabelXOffset, YOffset, width, 20), privateKey); + YOffset += Ysteps; + + bool doRandomize = GUI.Button(new Rect(0, YOffset, width, 20), "Randomize private key"); + YOffset += Ysteps; + if (doRandomize) { RandomizePrivateKey(); } + } + + if (doSave) { SaveToPlayerPrefs(); } + + GUI.EndScrollView(); + GUI.EndGroup(); + } + + void SaveToPlayerPrefs() + { + PlayerPrefs.SetInt("port", port); + PlayerPrefs.SetInt("portPrivateAPI", portPrivateAPI); + PlayerPrefs.SetInt("fps", fps); + PlayerPrefs.SetFloat("timeScale", timeScale); + PlayerPrefs.SetFloat("timeOut", timeOut); + PlayerPrefs.SetInt("maxSplitScreen", maxSplitScreen); + PlayerPrefs.SetInt("generateTrees", generateTrees ? 1 : 0); + PlayerPrefs.SetInt("extendedTelemetry", extendedTelemetry ? 1 : 0); + PlayerPrefs.SetInt("generateRandomCones", generateRandomCones ? 1 : 0); + PlayerPrefs.SetInt("randomLight", randomLight ? 1 : 0); + PlayerPrefs.SetInt("raceCameras", raceCameras ? 1 : 0); + PlayerPrefs.SetInt("overheadCamera", overheadCamera ? 1 : 0); + PlayerPrefs.SetInt("drawLidar", drawLidar ? 1 : 0); + PlayerPrefs.SetInt("paceCar", paceCar ? 1 : 0); + PlayerPrefs.SetInt("manualDriving", manualDriving ? 1 : 0); + PlayerPrefs.SetFloat("kp", kp); + PlayerPrefs.SetFloat("kd", kd); + PlayerPrefs.SetFloat("ki", ki); + PlayerPrefs.SetInt("useSeed", useSeed ? 1 : 0); + PlayerPrefs.SetString("privateKey", privateKey); + + PlayerPrefs.Save(); + } + + void LoadPlayerPrefs() + { + port = PlayerPrefs.GetInt("port", port); + portPrivateAPI = PlayerPrefs.GetInt("portPrivateAPI", portPrivateAPI); + fps = PlayerPrefs.GetInt("fps", fps); + timeScale = PlayerPrefs.GetFloat("timeScale", timeScale); + timeOut = PlayerPrefs.GetFloat("timeOut", timeOut); + maxSplitScreen = PlayerPrefs.GetInt("maxSplitScreen", maxSplitScreen); + generateTrees = PlayerPrefs.GetInt("generateTrees", generateTrees ? 1 : 0) == 1 ? true : false; + extendedTelemetry = PlayerPrefs.GetInt("extendedTelemetry", extendedTelemetry ? 1 : 0) == 1 ? true : false; + generateRandomCones = PlayerPrefs.GetInt("generateRandomCones", generateRandomCones ? 1 : 0) == 1 ? true : false; + randomLight = PlayerPrefs.GetInt("randomLight", randomLight ? 1 : 0) == 1 ? true : false; + raceCameras = PlayerPrefs.GetInt("raceCameras", raceCameras ? 1 : 0) == 1 ? true : false; + overheadCamera = PlayerPrefs.GetInt("overheadCamera", overheadCamera ? 1 : 0) == 1 ? true : false; + drawLidar = PlayerPrefs.GetInt("drawLidar", drawLidar ? 1 : 0) == 1 ? true : false; + paceCar = PlayerPrefs.GetInt("paceCar", paceCar ? 1 : 0) == 1 ? true : false; + manualDriving = PlayerPrefs.GetInt("manualDriving", manualDriving ? 1 : 0) == 1 ? true : false; + kp = PlayerPrefs.GetFloat("kp", kp); + kd = PlayerPrefs.GetFloat("kd", kd); + ki = PlayerPrefs.GetFloat("ki", ki); + useSeed = PlayerPrefs.GetInt("useSeed", useSeed ? 1 : 0) == 1 ? true : false; + privateKey = PlayerPrefs.GetString("privateKey", Random.Range(10000000, 99999999).ToString()); + additionnalContentPath = Application.streamingAssetsPath; + } + + void RandomizePrivateKey() + { + privateKey = Random.Range(10000000, 99999999).ToString(); + PlayerPrefs.SetString("privateKey", privateKey); + PlayerPrefs.Save(); + } +} \ No newline at end of file diff --git a/Scripts/GlobalStateEditor.cs.meta b/Scripts/GlobalStateEditor.cs.meta new file mode 100755 index 0000000..e1ce5b4 --- /dev/null +++ b/Scripts/GlobalStateEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1b637f9c31c45c549817af28ffef012a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: -1100 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/GlobalStateWatcher.cs b/Scripts/GlobalStateWatcher.cs new file mode 100755 index 0000000..5ce31e4 --- /dev/null +++ b/Scripts/GlobalStateWatcher.cs @@ -0,0 +1,23 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class GlobalStateWatcher : MonoBehaviour { + + public GameObject menuObj; + public GameObject networkObj; + + // Use this for initialization + void Start () { + + /* + if (GlobalState.bAutoHideSceneMenu && menuObj != null) + menuObj.SetActive(false); + + if (GlobalState.bAutoConnectToWebSocket && networkObj != null) + networkObj.SetActive(true); + */ + + } + +} diff --git a/Scripts/GlobalStateWatcher.cs.meta b/Scripts/GlobalStateWatcher.cs.meta new file mode 100755 index 0000000..7aeee41 --- /dev/null +++ b/Scripts/GlobalStateWatcher.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 4efaaf7922b6daa4e9eafc08b1a175ba +timeCreated: 1516309168 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/HUDFPS.cs b/Scripts/HUDFPS.cs new file mode 100755 index 0000000..8bbb31c --- /dev/null +++ b/Scripts/HUDFPS.cs @@ -0,0 +1,55 @@ +using UnityEngine; +using UnityEngine.UI; + +//Credit Poless on http://wiki.unity3d.com/index.php?title=FramesPerSecond + +public class HUDFPS : MonoBehaviour +{ + + // Attach this to a GUIText to make a frames/second indicator. + // + // It calculates frames/second over each updateInterval, + // so the display does not keep changing wildly. + // + // It is also fairly accurate at very low FPS counts (<10). + // We do this not by simply counting frames per interval, but + // by accumulating FPS for each frame. This way we end up with + // correct overall FPS even if the interval renders something like + // 5.5 frames. + + public float updateInterval = 3.0F; + + private float accum = 0; // time accumulated over the interval + private int frames = 0; // frames drawn over the interval + + public Text status; + + void Start() + { + if( !status) + { + Debug.Log("HUDFPS needs a Text component!"); + enabled = false; + return; + } + } + + void Update() + { + accum += Time.unscaledDeltaTime; + ++frames; + + // Interval ended - update GUI text and start new interval + if( accum > updateInterval && status != null) + { + // display two fractional digits (f2 format) + float fps = frames / accum; + string format = System.String.Format("FPS: {0:F1}",fps); + + status.text = format; + + accum = 0.0F; + frames = 0; + } + } +} \ No newline at end of file diff --git a/Scripts/HUDFPS.cs.meta b/Scripts/HUDFPS.cs.meta new file mode 100755 index 0000000..3e98718 --- /dev/null +++ b/Scripts/HUDFPS.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 2f5d34ac169a493488e5978a3359d2ef +timeCreated: 1504629867 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/ICar.cs b/Scripts/ICar.cs new file mode 100755 index 0000000..453d44b --- /dev/null +++ b/Scripts/ICar.cs @@ -0,0 +1,61 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +//view scripts at Aug 17 +//https://github.com/tawnkramer/sdsandbox/tree/7c88596a3fd8ffe32d11074ab1ff661d24602152/sdsim/Assets/Scripts + +//https://github.com/tawnkramer/sdsandbox/commit/7c88596a3fd8ffe32d11074ab1ff661d24602152 + +//Create an interface class that the sdsim will expect. We can use this to wrap other car implementations +//like the Unity standard asset car. +public interface ICar +{ + //all inputs require 0-1 input except steering which is in degrees, where 0 is center. + void RequestThrottle(float val); + + void RequestSteering(float val); + + void RequestFootBrake(float val); + + void RequestHandBrake(float val); + + + //query last input given. + float GetSteering(); + + float GetThrottle(); + + float GetFootBrake(); + + float GetHandBrake(); + + + //query state. + Transform GetTransform(); + + Vector3 GetVelocity(); + + Vector3 GetAccel(); + + Vector3 GetGyro(); + + //mark the current activity for partial selections when creating training sets later. + string GetActivity(); + + void SetActivity(string act); + + + //Save and restore State + void SavePosRot(); + + void RestorePosRot(); + + void SetMaxSteering(float val); + + float GetMaxSteering(); + + //get the name of the last object we collided with + string GetLastCollision(); + void ClearLastCollision(); +} diff --git a/Scripts/ICar.cs.meta b/Scripts/ICar.cs.meta new file mode 100755 index 0000000..36e2d00 --- /dev/null +++ b/Scripts/ICar.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: a6e9cc1b4fe7846dcbda44e6943a5132 +timeCreated: 1484592740 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/JoystickCarControl.cs b/Scripts/JoystickCarControl.cs new file mode 100755 index 0000000..446f7c2 --- /dev/null +++ b/Scripts/JoystickCarControl.cs @@ -0,0 +1,36 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityStandardAssets.CrossPlatformInput; + +public class JoystickCarControl : MonoBehaviour +{ + public GameObject carObj; + private ICar car; + + public float MaximumSteerAngle = 25.0f; //has to be kept in sync with the car, as that's a private var. + + void Awake() + { + if(carObj != null) + car = carObj.GetComponent(); + } + + private void OnDisable() + { + car.RequestThrottle(0.0f); + car.RequestHandBrake(1.0f); + car.RequestFootBrake(1.0f); + } + private void FixedUpdate() + { + // pass the input to the car! + float h = CrossPlatformInputManager.GetAxis("Horizontal"); + float v = CrossPlatformInputManager.GetAxis("Vertical"); + float handbrake = CrossPlatformInputManager.GetAxis("Jump"); + car.RequestSteering(h * MaximumSteerAngle); + car.RequestThrottle(v); + //car.RequestFootBrake(v); + car.RequestHandBrake(handbrake); + } +} diff --git a/Scripts/JoystickCarControl.cs.meta b/Scripts/JoystickCarControl.cs.meta new file mode 100755 index 0000000..66e92e3 --- /dev/null +++ b/Scripts/JoystickCarControl.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 23bb2fa9b8a0421478eb697c3a63f7f0 +timeCreated: 1498591643 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/LaneChangeTrainer.cs b/Scripts/LaneChangeTrainer.cs new file mode 100755 index 0000000..48fb709 --- /dev/null +++ b/Scripts/LaneChangeTrainer.cs @@ -0,0 +1,172 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class LaneChangeTrainer : MonoBehaviour { + + //how many lanes in total to change to/from + public int numLanes = 5; + + //how far side ways to the next lane + public float laneDist = 4.0f; + + //which lane do we begin? Far right is zero. + public int currentLane = 0; + + //over how long should we take to move to the next lane + public float transitionDist = 20.0f; + + //How long to stay in this lane before changing + public float laneKeepDist = 20.0f; + + //How much to smooth path. 0.0 - 1.0f + public float pathSmoothFactor = 0.5f; + + public void ModifyPath(ref CarPath path) + { + float curLaneDist = 0.0f; + Vector3 start = path.nodes[0].pos; + int changeLaneDir = 1; + float curTransDist = 0.0f; + bool initLaneChange = false; + + Vector3 offset = Vector3.zero; + Vector3 laneChangeDp = Vector3.zero; + + string activity = "keep_lane"; + + for(int iN = 1; iN < path.nodes.Count; iN++) + { + PathNode n = path.nodes[iN]; + Vector3 dp = n.pos - path.nodes[iN - 1].pos; + + if(curLaneDist < laneKeepDist) + { + n.activity = activity; + + activity = "keep_lane"; + + //stay in current lane. + curLaneDist += dp.magnitude; + initLaneChange = false; + } + else if(!initLaneChange) + { + initLaneChange = true; + laneChangeDp = new Vector3(-1 * NewLaneDir(changeLaneDir) * laneDist, 0.0f, laneKeepDist); + + GetActivity(ref changeLaneDir, ref activity); + + offset = laneChangeDp.normalized * dp.magnitude; + offset.z = 0.0f; + OffsetRemainingPath(ref path, iN, offset); + curTransDist += dp.magnitude; + + n.activity = activity; + } + else + { + offset = laneChangeDp.normalized * dp.magnitude; + offset.z = 0.0f; + OffsetRemainingPath(ref path, iN, offset); + curTransDist += dp.magnitude; + + n.activity = activity; + + if(curTransDist > transitionDist) + { + OnEnterNewLane(ref changeLaneDir); + + n = path.nodes[iN]; + + //do a small correction to make sure we are in the absolute center of the lane. + float lanePosAbsoluteX = (-1 * currentLane * laneDist) + start.x; + float errorX = lanePosAbsoluteX - n.pos.x; + Vector3 error = new Vector3(errorX, 0.0f, 0.0f); + OffsetRemainingPath(ref path, iN, error); + + //switch back to driving in the lane. + curLaneDist = 0.0f; + curTransDist = 0.0f; + } + } + } + + OffsetLabelsBack(ref path); + OffsetLabelsBack(ref path); + + path.SmoothPath(pathSmoothFactor); + } + + void OffsetLabelsBack(ref CarPath path) + { + //we are setting our activity labels one node too late. Try setting them a bit early. And removing them one early too. + for(int iN = 1; iN < path.nodes.Count; iN++) + { + PathNode p = path.nodes[iN - 1]; + PathNode c = path.nodes[iN]; + + if(p.activity == null || c.activity == null) + continue; + + if(c.activity != p.activity) + { + if(c.activity.StartsWith("cl_")) + { + p.activity = c.activity; + } + else if(p.activity.StartsWith("cl_")) + { + p.activity = c.activity; + } + } + } + } + + void OffsetRemainingPath(ref CarPath path, int iStart, Vector3 offset) + { + for(int iN = iStart; iN < path.nodes.Count; iN++) + { + PathNode n = path.nodes[iN]; + n.pos += offset; + } + } + + int NewLaneDir(int changeLaneDir) + { + if(currentLane == numLanes - 1) + { + return -1; + } + else if(currentLane == 0) + { + return 1; + } + + return changeLaneDir; + } + + void GetActivity(ref int changeLaneDir, ref string activity) + { + if(changeLaneDir > 0) + activity = "cl_left"; + else + activity = "cl_right"; + } + + void OnEnterNewLane(ref int changeLaneDir) + { + //choose a dir, left or right. + currentLane += changeLaneDir; + + if(currentLane == numLanes - 1) + { + changeLaneDir = -1; + + } + else if(currentLane == 0) + { + changeLaneDir = 1; + } + } +} diff --git a/Scripts/LaneChangeTrainer.cs.meta b/Scripts/LaneChangeTrainer.cs.meta new file mode 100755 index 0000000..25c8595 --- /dev/null +++ b/Scripts/LaneChangeTrainer.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 489eab0f2e6a3422f9b8ff53d6251c46 +timeCreated: 1484765183 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Lidar.cs b/Scripts/Lidar.cs new file mode 100755 index 0000000..db1bf9d --- /dev/null +++ b/Scripts/Lidar.cs @@ -0,0 +1,232 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using System; + +[Serializable] +public class LidarPoint +{ + public float x; + public float y; + public float z; + public float d; + public float rx; + public float ry; + + public LidarPoint(Vector3 p, float distance, float _rx, float _ry) + { + x = p.x; + y = p.y; + z = p.z; + d = distance; + rx = _rx; + ry = _ry; + } +} + +[Serializable] +public class V3 +{ + public float x; + public float y; + public float z; + + public V3(Vector3 p) + { + x = p.x; + y = p.y; + z = p.z; + } +} + +[Serializable] +public class LidarPointArray +{ + //All coordinates in World coordinate system + + public LidarPoint[] points; + + public void Init(int numPoints) + { + points = new LidarPoint[numPoints]; + } +} + +public class Lidar : MonoBehaviour +{ + + + public LidarPointArray pointArr; + + //as the ray sweeps around, how many degrees does it advance per sample + public float degPerSweepInc = 2f; + + //what is the starting angle for the initial sweep compared to the forward vector + public float degAngDown = 25f; + + //what angle change between sweeps + public float degAngDelta = -1f; + + //how many complete 360 sweeps + public int numSweepsLevels = 25; + + //what it max distance we will register a hit + public float maxRange = 50f; + + //how large radius to use when rendering debug display + public float gizmoSize = 0.1f; + + //what is the scalar on the perlin noise applied to point position + public float noise = 0.2f; + + public bool DisplayDebugInScene = false; + + // are there layers we don't want to collide with? + public string[] layerMaskNames; + + int collMask = 0; + + void Awake() + { + pointArr = new LidarPointArray(); + pointArr.Init((int)(360 / degPerSweepInc * numSweepsLevels)); + + int v = 0; + + foreach (string layerName in layerMaskNames) + { + int layer = LayerMask.NameToLayer(layerName); + v |= 1 << layer; + } + + collMask |= ~v; + + } + + public void SetConfig(float offset_x, float offset_y, float offset_z, float rot_x, + float _degPerSweepInc, float _degAngDown, float _degAngDelta, float _maxRange, float _noise, int _numSweepsLevels) + { + degPerSweepInc = _degPerSweepInc; + degAngDown = _degAngDown; + degAngDelta = _degAngDelta; + maxRange = _maxRange; + noise = _noise; + numSweepsLevels = _numSweepsLevels; + + if (offset_x != 0.0f || offset_y != 0.0f || offset_z != 0.0f) + transform.localPosition = new Vector3(offset_x, offset_y, offset_z); + + if (rot_x != 0.0f) + transform.localEulerAngles = new Vector3(rot_x, 0.0f, 0.0f); + + pointArr = new LidarPointArray(); + pointArr.Init((int)(360 / degPerSweepInc * numSweepsLevels)); + } + + public JSONObject GetOutputAsJson() + { + LidarPointArray points = GetOutput(); + JSONObject json = JSONObject.Create(); + foreach (LidarPoint p in points.points) + { + JSONObject vec = JSONObject.Create(); + try + { + vec.AddField("d", (float)Math.Round(p.d, 2)); + vec.AddField("rx", p.rx); + vec.AddField("ry", p.ry); + json.Add(vec); + } + catch + { + // just ignore points that don't resolve. + } + } + + return json; + } + + public LidarPointArray GetOutput() + { + int numSweep = (int)(360 / degPerSweepInc); + pointArr = new LidarPointArray(); + pointArr.Init(numSweep * numSweepsLevels); + + Ray ray = new Ray(transform.position, transform.forward); + RaycastHit hit; + + // Vertical rotation + Quaternion rotUp = Quaternion.AngleAxis(degAngDelta, transform.right); + + // Horizontal rotation + Quaternion rotSide = Quaternion.AngleAxis(degPerSweepInc, transform.up); + + //Sample the output texture to create rays. + int iP = 0; + float rx = 0.0f; + float ry = 0.0f; + + for (int iS = 0; iS < numSweepsLevels; iS++) + { + // reset the orientation of the ray + Quaternion rotDown = Quaternion.AngleAxis(degAngDown + iS * degAngDelta, transform.right); + ray.direction = rotDown * transform.forward; + rx = 0.0f; + + for (int iA = 0; iA < numSweep; iA++) + { + if (Physics.Raycast(ray, out hit, maxRange, collMask)) + { + //sample that ray at the depth given by the pixel. + Vector3 pos = hit.point - transform.position; + float distance = hit.distance; + + //shouldn't hit this unless user is messing around in the interface with things running. + if (iP >= pointArr.points.Length) + break; + + // add some noise to the point position + float noiseX = Mathf.PerlinNoise(UnityEngine.Random.Range(-1f, 1f), UnityEngine.Random.Range(-1f, 1f)); + float noiseY = Mathf.PerlinNoise(UnityEngine.Random.Range(-1f, 1f), UnityEngine.Random.Range(-1f, 1f)); + float noiseZ = Mathf.PerlinNoise(UnityEngine.Random.Range(-1f, 1f), UnityEngine.Random.Range(-1f, 1f)); + pos.x += noise * noiseX; + pos.y += noise * noiseY; + pos.z += noise * noiseZ; + + //set iPoint + pointArr.points[iP] = new LidarPoint(pos, distance, rx, ry); + iP++; + } + + ray.direction = rotSide * ray.direction; + rx += degPerSweepInc; + } + + ray.direction = rotUp * ray.direction; + ry += degAngDelta; + } + + return pointArr; + } + + void OnDrawGizmosSelected() + { + Gizmos.color = Color.red; + pointArr = GetOutput(); // not really efficient but won't be called in the app, just for visualization purpose + + Vector3 pos = Vector3.zero; + foreach (LidarPoint p in pointArr.points) + { + if (p == null) + continue; + + pos.x = p.x; + pos.y = p.y; + pos.z = p.z; + + //make points global space for drawing + pos += transform.position; + Gizmos.DrawSphere(pos, gizmoSize); + } + } +} diff --git a/Scripts/Lidar.cs.meta b/Scripts/Lidar.cs.meta new file mode 100755 index 0000000..2fbffb8 --- /dev/null +++ b/Scripts/Lidar.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 271bfe211179846878a1f16e517b854d +timeCreated: 1489425632 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Lines.cs b/Scripts/Lines.cs new file mode 100755 index 0000000..f5d67b0 --- /dev/null +++ b/Scripts/Lines.cs @@ -0,0 +1,101 @@ +using UnityEngine; +using System.Collections; + +//3d line +public class Line3d +{ + + public Line3d() {} + + public Line3d(ref Vector3 a, ref Vector3 b) + { + ConstructLine(ref a, ref b); + } + + //allow a line to be recomputed + public void ConstructLine(ref Vector3 a, ref Vector3 b) + { + m_origin = a; + m_dir = a - b; + m_dir.Normalize(); + } + + //produce a vector normal to this line passing through this point. + public Vector3 ClosestVectorTo(ref Vector3 point) + { + Vector3 deltaPoint = m_origin - point; + float dot = Vector3.Dot(deltaPoint, m_dir); + return (m_dir * dot) - deltaPoint; + } + + //transform the point by the normal vector that places it on the line + public Vector3 ClosestPointOnLineTo(ref Vector3 point) + { + Vector3 vectorTo = ClosestVectorTo(ref point); + return point - vectorTo; + } + + public float AbsAngleBetween(ref Line3d l) + { + return Mathf.Abs(Vector3.Angle( m_dir, l.m_dir)); + } + + public Vector3 m_origin, m_dir; +}; + +public class LineSeg3d : Line3d +{ + + public LineSeg3d(){} + + public LineSeg3d(ref Vector3 a, ref Vector3 b) + { + ConstructLineSeg(ref a, ref b); + } + + public void ConstructLineSeg(ref Vector3 a, ref Vector3 b) + { + ConstructLine(ref a, ref b); + m_end = b; + m_length = (a - b).magnitude; + } + + public enum SegResult + { + OnSpan, + LessThanOrigin, + GreaterThanEnd, + } + + //find the closest point, clamping it to the ends + public Vector3 ClosestPointOnSegmentTo(ref Vector3 point, ref SegResult res) + { + Vector3 deltaPoint = m_origin - point; + float dot = Vector3.Dot(deltaPoint, m_dir); + + //clamp to the ends of the line segment + if(dot <= 0.0f) + { + res = SegResult.LessThanOrigin; + return m_origin; + } + + if(dot >= m_length) + { + res = SegResult.GreaterThanEnd; + return m_end; + } + + res = SegResult.OnSpan; + Vector3 vectorTo = (m_dir * dot) - deltaPoint; + return point - vectorTo; + } + + public void Draw(Color c) + { + Debug.DrawLine(m_origin, m_end, c); + } + + public float m_length; + public Vector3 m_end; +}; \ No newline at end of file diff --git a/Scripts/Lines.cs.meta b/Scripts/Lines.cs.meta new file mode 100755 index 0000000..10c2935 --- /dev/null +++ b/Scripts/Lines.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 9f193da27afc443cab7d171c78f63a1b +timeCreated: 1449593992 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Localizer.cs b/Scripts/Localizer.cs new file mode 100755 index 0000000..21c11a7 --- /dev/null +++ b/Scripts/Localizer.cs @@ -0,0 +1,439 @@ +using UnityEngine; +using System.Collections; +using System.Collections.Generic; + +[System.Serializable] +public class Measurement +{ + public int id; + public int offsetX; + public int offsetZ; + + public Measurement(int _id, int offX, int offZ) + { + id = _id; + offsetX = offX; + offsetZ = offZ; + } +} + + +[System.Serializable] +public class Measurements +{ + public List m; + + public void Init(int count) + { + m = new List(count); + } +} + +[System.Serializable] +public class ProbMap +{ + public float[,] cells; + public int numX; + public int numZ; + + public void Init(int numXCells, int numZCells) + { + numX = numXCells; + numZ = numZCells; + cells = new float[numXCells, numZCells]; + } + + public void AllEqualProb() + { + int totalCells = numX * numZ; + + //At first we are equally likely to be in any cell. + //And since we like the probability to add to 1, we + ///divide by total cells. + float iniProb = 1.0f / totalCells; + + for(int iX = 0; iX < numX; iX++) + { + for(int iZ = 0; iZ < numZ; iZ++) + { + cells[iX, iZ] = iniProb; + } + } + } + + public void Zero() + { + for(int iX = 0; iX < numX; iX++) + { + for(int iZ = 0; iZ < numZ; iZ++) + { + cells[iX, iZ] = 0.0f; + } + } + } +} + +public class ProbMapVisualizer +{ + public List cellMarkers; + public List topCells; + + public void Init(int numToVisualize, GameObject prefab) + { + cellMarkers = new List(numToVisualize); + topCells = new List(numToVisualize); + + for(int i = 0; i < numToVisualize; i++) + { + GameObject go = GameObject.Instantiate(prefab) as GameObject; + cellMarkers.Add(go); + } + } + + public void Visualize(ProbMap pm, Map world) + { + topCells.Clear(); + int numX = pm.numX; + int numZ = pm.numZ; + + float thresh = 0.05f; + int iCM = 0; + + for(int iX = 0; iX < numX; iX++) + { + for(int iZ = 0; iZ < numZ; iZ++) + { + float p = pm.cells[iX, iZ]; + + if(p > thresh) + { + Vector3 pos = Vector3.zero; + pos = world.startPos; + pos.x += world.dX * iX; + pos.z += world.dZ * iZ; + + cellMarkers[iCM].transform.position = pos; + + Vector3 s = Vector3.one; + s.y = p * 100f; + s.x = world.dX; + s.z = world.dZ; + cellMarkers[iCM].transform.localScale = s; + + iCM++; + + if(iCM == cellMarkers.Count) + break; + } + } + } + + for(int iM = iCM; iM < cellMarkers.Count; iM++) + { + cellMarkers[iM].transform.position = Vector3.zero; + cellMarkers[iM].transform.localScale = Vector3.zero; + } + } +} + + +public class MonteCarloLocalizer +{ + public Map world; + public ProbMap probMap; + + //for memory efficiency, we will allocate + //two maps and swap between them. + bool useMapA; + ProbMap mapA; + ProbMap mapB; + + public void Init(Map worldMap) + { + world = worldMap; + mapA = new ProbMap(); + mapA.Init(world.numX, world.numZ); + mapA.AllEqualProb(); + useMapA = true; + probMap = mapA; + + mapB = new ProbMap(); + mapB.Init(world.numX, world.numZ); + } + + public void Move(int iMoveX, int iMoveZ, float probExact) + { + if(iMoveX == 0 && iMoveZ == 0) + return; + + ProbMap newMap = useMapA ? mapB : mapA; + newMap.Zero(); + + int numX = probMap.numX; + int numZ = probMap.numZ; + + for(int iX = 0; iX < numX; iX++) + { + for(int iZ = 0; iZ < numZ; iZ++) + { + int iTargetX = iX + iMoveX; + int iTargetZ = iZ + iMoveZ; + + if(iTargetX < 0 || iTargetX >= numX) + continue; + + if(iTargetZ < 0 || iTargetZ >= numZ) + continue; + + //int trailX = iMoveX > 0 ? - 1 : (iMoveX == 0 ? 0 : 1); + //int trailZ = iMoveZ > 0 ? - 1 : (iMoveZ == 0 ? 0 : 1); + + //most of old value moves to new + float probTarget = probMap.cells[iX, iZ] * probExact; + + //some probablity that we are still in old cell. + //float probTrail = probMap.cells[iX, iZ] * (1.0f - probExact); + + newMap.cells[iTargetX, iTargetZ] += probTarget; + //newMap.cells[iTargetX + trailX, iTargetZ + trailZ] += probTrail; + } + } + + probMap = newMap; + useMapA = !useMapA; + } + + public void Sense(Measurements m, float probExact) + { + for(int iM = 0; iM < m.m.Count; iM++) + { + Measurement _m = m.m[iM]; + + Sense(_m, probExact); + } + } + + public void Sense(Measurement m, float probHit) + { + int numX = probMap.numX; + int numZ = probMap.numZ; + float probMiss = 1.0f - probHit; + float t = 0f; + + for(int iX = 0; iX < numX; iX++) + { + for(int iZ = 0; iZ < numZ; iZ++) + { + float hit = 0f; + int iSenseX = iX + m.offsetX; + int iSenseZ = iZ + m.offsetZ; + + if(iSenseZ >= 0 && iSenseZ < numZ && + iSenseX >= 0 && iSenseX < numX) + { + hit = (m.id == world.cells[iSenseX, iSenseZ]) ? 1.0f : 0.0f; + } + + float p = probMap.cells[iX, iZ]; + + float np = p * (hit * probHit + (1.0f - hit) * probMiss); + + probMap.cells[iX, iZ] = np; + + t += np; + } + } + + //Normalize. + for(int iX = 0; iX < numX; iX++) + { + for(int iZ = 0; iZ < numZ; iZ++) + { + probMap.cells[iX, iZ] = probMap.cells[iX, iZ] / t; + } + } + } + + public void MostLikelyCell(out int cellX, out int cellZ) + { + float p = 0f; + int numX = probMap.numX; + int numZ = probMap.numZ; + cellX = 0; + cellZ = 0; + + for(int iX = 0; iX < numX; iX++) + { + for(int iZ = 0; iZ < numZ; iZ++) + { + float pc = probMap.cells[iX, iZ]; + + if(pc > p) + { + p = pc; + cellX = iX; + cellZ = iZ; + } + } + } + } + + public void MostLikelyPos(out Vector3 pos) + { + int iX = 0; + int iZ = 0; + MostLikelyCell(out iX, out iZ); + + pos = world.startPos; + pos.x += world.dX * iX; + pos.z += world.dZ * iZ; + } +} + +public class LocUnitTester +{ + public void Test(Map map, Measurements measurements, List moves, + int iExpectedX, int iExpectedZ) + { + MonteCarloLocalizer loc = new MonteCarloLocalizer(); + + loc.Init(map); + int iX = (int)moves[0].x; + int iY = (int)moves[0].y; + loc.Move(iX, iY, 1f); + + loc.Sense(measurements, 1f); + + int mlX, mlZ; + + loc.MostLikelyCell(out mlX, out mlZ); + + if(iExpectedX == mlX && iExpectedZ == mlZ) + { + Debug.Log("Correct!"); + } + else + { + Debug.LogError("Oops"); + } + } + + public void TestA() + { + Map map = new Map(); + map.Init(3, 3); + map.cells [1,1] = 1; //all the rest are zero. + Measurement m = new Measurement(1, 0, 0); + Measurements ma = new Measurements(); + ma.Init(1); + ma.m.Add(m); + List moves = new List(); + moves.Add(Vector2.zero); + + Test (map, ma, moves, 1, 1); + } + + public void TestB() + { + Map map = new Map(); + map.Init(3, 3); + map.cells [1,1] = 1; //all the rest are zero. + Measurement m = new Measurement(1, 0, -1); + Measurements ma = new Measurements(); + ma.Init(1); + ma.m.Add(m); + List moves = new List(); + moves.Add(Vector2.zero); + + Test (map, ma, moves, 1, 2); + } +} + + +public class Localizer : MonoBehaviour +{ + MonteCarloLocalizer loc; + ProbMapVisualizer vis; + + public MapManager mm; + int iPrevX = 0; + int iPrevZ = 0; + Vector3 prevPos; + + public float radiusSense = 100f; + public float threshMove = 0.1f; + public float probMoveExact = 0.8f; + public float probSenseExact = 0.8f; + + public Transform likelyTM; + + // Use this for initialization + void Start () + { + LocUnitTester tester = new LocUnitTester(); + tester.TestB(); + + loc = new MonteCarloLocalizer(); + loc.Init(mm.map); + + vis = new ProbMapVisualizer(); + vis.Init(25, likelyTM.gameObject ); + + //when we move at least full a cell dist, then sense again. + threshMove = mm.map.dX; + prevPos = Vector3.zero; + } + + // Update is called once per frame + void Update () + { + Vector3 newPos = transform.position; + int inewX = Mathf.RoundToInt(newPos.x / mm.map.dX); + int inewZ = Mathf.RoundToInt(newPos.z / mm.map.dZ); + if(inewX != iPrevX || inewZ != iPrevZ) + { + int iMoveX = inewX - iPrevX; + int iMoveZ = inewZ - iPrevZ; + SenseEnv(iMoveX, iMoveZ); + + iPrevX = inewX; + iPrevZ = inewZ; + prevPos = newPos; + } + + vis.Visualize(loc.probMap, loc.world); + } + + void SenseEnv(int iMoveX, int iMoveZ) + { + Marker[] allMarkers = GameObject.FindObjectsOfType(); + Vector3 pos = transform.position; + Measurements ma = new Measurements(); + ma.Init(100); + + foreach(Marker marker in allMarkers) + { + Vector3 delta = (marker.transform.position - pos); + + if(delta.magnitude < radiusSense) + { + int offX = Mathf.RoundToInt(delta.x / mm.map.dX); + int offZ = Mathf.RoundToInt(delta.z / mm.map.dZ); + + Measurement m = new Measurement(marker.id, offX, offZ); + + ma.m.Add(m); + } + } + + + //first move is huge and not needed. + if(prevPos != Vector3.zero) + loc.Move(iMoveX, iMoveZ, probMoveExact); + + loc.Sense(ma, probSenseExact); + + Vector3 likelyPos = Vector3.zero; + likelyTM.position = likelyPos; + } +} diff --git a/Scripts/Localizer.cs.meta b/Scripts/Localizer.cs.meta new file mode 100755 index 0000000..e61436a --- /dev/null +++ b/Scripts/Localizer.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 019bba83519c94d9e80c79b1b54e2b59 +timeCreated: 1449074642 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/LocationMarker.cs b/Scripts/LocationMarker.cs new file mode 100755 index 0000000..1d29c1d --- /dev/null +++ b/Scripts/LocationMarker.cs @@ -0,0 +1,29 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class LocationMarker : MonoBehaviour { + + public int id; + + public static int GetNearestLocMarker(Vector3 pos) + { + int closest = -1; + float dist = float.PositiveInfinity; + + var markers = GameObject.FindObjectsOfType(); + + foreach(var marker in markers) + { + float d = (marker.transform.position - pos).magnitude; + if(d < dist) + { + closest = marker.id; + dist = d; + } + } + + return closest; + } +} + diff --git a/Scripts/LocationMarker.cs.meta b/Scripts/LocationMarker.cs.meta new file mode 100755 index 0000000..ba5ba46 --- /dev/null +++ b/Scripts/LocationMarker.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 362da7465d0da47f7981adc02f1fcc80 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Logger.cs b/Scripts/Logger.cs new file mode 100755 index 0000000..6397125 --- /dev/null +++ b/Scripts/Logger.cs @@ -0,0 +1,373 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using System.IO; +using System.Threading; +using System; +using UnityEngine.UI; + +[Serializable] +public class MetaJson +{ + public string[] inputs; + public string[] types; + + public void Init(string[] _inputs, string[] _types) + { + inputs = _inputs; + types = _types; + } +} + +[Serializable] +public class DonkeyRecord +{ + public string cam_image_array; + public float user_throttle; + public float user_angle; + public string user_mode; + public int track_lap; + public int track_loc; + + public void Init(string image_name, float throttle, float angle, string mode, int lap, int loc) + { + cam_image_array = image_name; + user_throttle = throttle; + user_angle = angle; + user_mode = mode; + track_lap = lap; + track_loc = loc; + } + + public string AsString() + { + string json = JsonUtility.ToJson(this); + + //Can't name the variable names with a slash, so replace on output + json = json.Replace("cam_image", "cam/image"); + json = json.Replace("user_throttle", "user/throttle"); + json = json.Replace("user_angle", "user/angle"); + json = json.Replace("user_mode", "user/mode"); + json = json.Replace("track_lap", "track/lap"); + json = json.Replace("track_lap", "track/lap"); + json = json.Replace("track_loc", "track/loc"); + + return json; + } +} +public class Logger : MonoBehaviour { + + public GameObject carObj; + public ICar car; + public CameraSensor camSensor; + public CameraSensor optionlB_CamSensor; + public Lidar lidar; + + //what's the current frame index + public int frameCounter = 0; + + //which lap + public int lapCounter = 0; + + //is there an upper bound on the number of frames to log + public int maxFramesToLog = 14000; + + //should we log when we are enabled + public bool bDoLog = true; + + public int limitFPS = 30; + + float timeSinceLastCapture = 0.0f; + + //We can output our logs in the style that matched the output from the shark robot car platform - github/tawnkramer/shark + public bool SharkStyle = false; + + //We can output our logs in the style that matched the output from the udacity simulator + public bool UdacityStyle = false; + + //We can output our logs in the style that matched the output from the donkey robot car platform - donkeycar.com + public bool DonkeyStyle = false; + + //Tub style as prefered by Donkey2 + public bool DonkeyStyle2 = true; + + public Text logDisplay; + + string outputFilename = "log_car_controls.txt"; + private StreamWriter writer; + + class ImageSaveJob { + public string filename; + public byte[] bytes; + } + + List imagesToSave; + + Thread thread; + + string GetLogPath() + { + if(GlobalState.log_path != "default") + return GlobalState.log_path + "/"; + + return Application.dataPath + "/../log/"; + } + + void Awake() + { + car = carObj.GetComponent(); + + if(bDoLog && car != null) + { + if(UdacityStyle) + { + outputFilename = "driving_log.csv"; + } + + string filename = GetLogPath() + outputFilename; + + writer = new StreamWriter(filename); + + Debug.Log("Opening file for log at: " + filename); + + if(UdacityStyle) + { + writer.WriteLine("center,left,right,steering,throttle,brake,speed"); + } + + if(DonkeyStyle2) + { + MetaJson mjson = new MetaJson(); + string[] inputs = {"cam/image_array", "user/angle", "user/throttle", "user/mode", "track/lap", "track/loc"}; + string[] types = {"image_array", "float", "float", "str", "int", "int"}; + mjson.Init(inputs, types); + string json = JsonUtility.ToJson(mjson); + var f = File.CreateText(GetLogPath() + "meta.json"); + f.Write(json); + f.Close(); + } + } + + Canvas canvas = GameObject.FindObjectOfType(); + GameObject go = CarSpawner.getChildGameObject(canvas.gameObject, "LogCount"); + if (go != null) + logDisplay = go.GetComponent(); + + imagesToSave = new List(); + + thread = new Thread(SaverThread); + thread.Start(); + } + + // Update is called once per frame + void Update () + { + if(!bDoLog) + return; + + timeSinceLastCapture += Time.deltaTime; + + if (timeSinceLastCapture < 1.0f / limitFPS) + return; + + timeSinceLastCapture -= (1.0f / limitFPS); + + string activity = car.GetActivity(); + + if(writer != null) + { + if(UdacityStyle) + { + string image_filename = GetUdacityStyleImageFilename(); + float steering = car.GetSteering() / car.GetMaxSteering(); + writer.WriteLine(string.Format("{0},{1},{2},{3},{4},{5},{6},{7}", image_filename, + "none", "none", steering.ToString(), + car.GetThrottle().ToString(), "0", "0", lapCounter)); + } + else if(DonkeyStyle || SharkStyle) + { + + } + else if(DonkeyStyle2) + { + DonkeyRecord mjson = new DonkeyRecord(); + float steering = car.GetSteering() / car.GetMaxSteering(); + float throttle = car.GetThrottle(); + int loc = LocationMarker.GetNearestLocMarker(carObj.transform.position); + + //training code like steering clamped between -1, 1 + steering = Mathf.Clamp(steering, -1.0f, 1.0f); + + mjson.Init(string.Format("{0}_cam-image_array_.jpg", frameCounter), + throttle, steering, "user", lapCounter, loc); + + string json = mjson.AsString(); + string filename = string.Format("record_{0}.json", frameCounter); + var f = File.CreateText(GetLogPath() + filename); + f.Write(json); + f.Close(); + } + else + { + writer.WriteLine(string.Format("{0},{1},{2},{3}", frameCounter.ToString(), activity, car.GetSteering().ToString(), car.GetThrottle().ToString())); + } + } + + if(lidar != null && lidar.gameObject.activeInHierarchy) + { + LidarPointArray pa = lidar.GetOutput(); + + if(pa != null) + { + string json = JsonUtility.ToJson(pa); + var filename = string.Format("lidar_{0}_{1}.txt", frameCounter.ToString(), activity); + var f = File.CreateText(GetLogPath() + filename); + f.Write(json); + f.Close(); + } + } + + if (optionlB_CamSensor != null && optionlB_CamSensor.gameObject.activeInHierarchy) + { + SaveCamSensor(camSensor, activity, "_a"); + SaveCamSensor(optionlB_CamSensor, activity, "_b"); + } + else + { + SaveCamSensor(camSensor, activity, ""); + } + + if (maxFramesToLog != -1 && frameCounter >= maxFramesToLog) + { + Shutdown(); + this.gameObject.SetActive(false); + } + + frameCounter = frameCounter + 1; + + if (logDisplay != null) + logDisplay.text = "Log:" + frameCounter; + } + + string GetUdacityStyleImageFilename() + { + return GetLogPath() + string.Format("IMG/center_{0,8:D8}.jpg", frameCounter); + } + + string GetDonkeyStyleImageFilename() + { + float steering = car.GetSteering() / 25.0f; + float throttle = car.GetThrottle(); + return GetLogPath() + string.Format("frame_{0,6:D6}_ttl_{1}_agl_{2}_mil_0.0.jpg", + frameCounter, throttle, steering); + } + + string GetSharkStyleImageFilename() + { + int steering = (int)(car.GetSteering() / 25.0f * 32768.0f); + int throttle = (int)(car.GetThrottle() * 32768.0f); + return GetLogPath() + string.Format("frame_{0,6:D6}_st_{1}_th_{2}.jpg", + frameCounter, steering, throttle); + } + + string GetDonkey2StyleImageFilename() + { + return GetLogPath() + string.Format("{0}_cam-image_array_.jpg", frameCounter); + } + + //Save the camera sensor to an image. Use the suffix to distinguish between cameras. + void SaveCamSensor(CameraSensor cs, string prefix, string suffix) + { + if (cs != null) + { + Texture2D image = cs.GetImage(); + + ImageSaveJob ij = new ImageSaveJob(); + + if(UdacityStyle) + { + ij.filename = GetUdacityStyleImageFilename(); + + ij.bytes = image.EncodeToJPG(); + } + else if (DonkeyStyle) + { + ij.filename = GetDonkeyStyleImageFilename(); + + ij.bytes = image.EncodeToJPG(); + } + else if (DonkeyStyle2) + { + ij.filename = GetDonkey2StyleImageFilename(); + + ij.bytes = image.EncodeToJPG(); + } + else if(SharkStyle) + { + ij.filename = GetSharkStyleImageFilename(); + + ij.bytes = image.EncodeToJPG(); + } + else + { + ij.filename = GetLogPath() + string.Format("{0}_{1,8:D8}{2}.png", prefix, frameCounter, suffix); + + ij.bytes = image.EncodeToPNG(); + } + + lock (this) + { + imagesToSave.Add(ij); + } + } + } + + public void SaverThread() + { + while(true) + { + int count = 0; + + lock(this) + { + count = imagesToSave.Count; + } + + if(count > 0) + { + ImageSaveJob ij = imagesToSave[0]; + + //Debug.Log("saving: " + ij.filename); + + File.WriteAllBytes(ij.filename, ij.bytes); + + lock(this) + { + imagesToSave.RemoveAt(0); + } + } + } + } + + public void Shutdown() + { + if(writer != null) + { + writer.Close(); + writer = null; + } + + if(thread != null) + { + thread.Abort(); + thread = null; + } + + bDoLog = false; + } + + void OnDestroy() + { + Shutdown(); + } +} \ No newline at end of file diff --git a/Scripts/Logger.cs.meta b/Scripts/Logger.cs.meta new file mode 100755 index 0000000..addfaa1 --- /dev/null +++ b/Scripts/Logger.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: d99138db4ad8544028a31fd0a13333ef +timeCreated: 1481994783 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/MapManager.cs b/Scripts/MapManager.cs new file mode 100755 index 0000000..1138573 --- /dev/null +++ b/Scripts/MapManager.cs @@ -0,0 +1,82 @@ +using UnityEngine; +using System.Collections; + +[System.Serializable] +public class Map +{ + public int[,] cells; + public Vector3 startPos; //of 0,0 cell. + public float dX; //the delta X position to the next cell + public float dZ; //the delta Z position to the next cell + public int numX; + public int numZ; + + public void Init(int numXCells, int numZCells) + { + numX = numXCells; + numZ = numZCells; + + cells = new int[numXCells, numZCells]; + } +} + +public class MapManager : MonoBehaviour +{ + + //two node giving the extent of the map. + public Transform minLimit; + public Transform maxLimit; + public float cellSize = 1f; //how big is the cell in world units + public int numXCells = 1; + public int numZCells = 1; + public GameObject markerPrefab; + public int numMarkers = 1; + public int numMarkerIds = 10; + + public Map map; + + // Use this for initialization + void Awake () { + GenerateMap(); + } + + // Update is called once per frame + void GenerateMap () + { + Vector3 minPos = minLimit.position; + Vector3 maxPos = maxLimit.position; + numXCells = (int)((maxPos.x - minPos.x) / cellSize); + numZCells = (int)((maxPos.z - minPos.z) / cellSize); + float dx = (maxPos.x - minPos.x) / (numXCells); + float dz = (maxPos.z - minPos.z) / (numZCells); + + map = new Map(); + map.Init(numXCells, numZCells); + map.startPos = minPos; + map.dX = dx; + map.dZ = dz; + + for(int iM = 0; iM < numMarkers; iM++) + { + int iX = Random.Range(0, numXCells); + int iZ = Random.Range(0, numZCells); + Vector3 pos = Vector3.zero; + pos.x = iX * dx + minPos.x; + pos.z = iZ * dz + minPos.z; + + GameObject go = Instantiate(markerPrefab, pos, Quaternion.identity) as GameObject; + + Marker m = go.GetComponent(); + + //go.transform.parent = this.transform; + + if(m) + { + m.id = Random.Range(1, numMarkerIds); + + //map cell has this id now. + map.cells[iX, iZ] = m.id; + } + } + } +} diff --git a/Scripts/MapManager.cs.meta b/Scripts/MapManager.cs.meta new file mode 100755 index 0000000..60c5f2b --- /dev/null +++ b/Scripts/MapManager.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: f40a26aa70ad4435bb2d66bf90c1e3d6 +timeCreated: 1484267705 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/MapOverlay.cs b/Scripts/MapOverlay.cs new file mode 100755 index 0000000..8a6abb3 --- /dev/null +++ b/Scripts/MapOverlay.cs @@ -0,0 +1,156 @@ +using System.Collections.Generic; +using UnityEngine; + +// Draws a minimap of the generated road as a 2D overlay in the top-left corner, +// positioned just below the NN steering/throttle text. Shows white path lines +// and a red dot for the current car position. Auto-refreshes when the road changes. +public class MapOverlay : MonoBehaviour +{ + public PathManager pathManager; + public Transform carTransform; + + const int MAP_X = 5; + const int MAP_Y = 42; // below the one-line NN text (~25px tall + padding) + const int MAP_SIZE = 160; + + static Texture2D _whiteTex; + static Texture2D _bgTex; + static Texture2D _dotTex; + + static Texture2D WhiteTex + { + get + { + if (_whiteTex == null) + { + _whiteTex = new Texture2D(1, 1); + _whiteTex.SetPixel(0, 0, Color.white); + _whiteTex.Apply(); + } + return _whiteTex; + } + } + + List _nodes = new List(); + float _minX, _maxX, _minZ, _maxZ; + int _lastNodeCount = -1; + float _lastFirstNodeX = float.MaxValue; // catches regen with same node count + float _lastFirstNodeZ = float.MaxValue; + bool _hasPath = false; + + void Awake() + { + if (pathManager == null) + pathManager = FindObjectOfType(); + + _bgTex = new Texture2D(1, 1); + _bgTex.SetPixel(0, 0, new Color(0f, 0f, 0f, 0.55f)); + _bgTex.Apply(); + + _dotTex = new Texture2D(4, 4); + for (int y = 0; y < 4; y++) + for (int x = 0; x < 4; x++) + _dotTex.SetPixel(x, y, Color.red); + _dotTex.Apply(); + } + + void Update() + { + // Find car transform once it exists + if (carTransform == null) + { + Car c = FindObjectOfType(); + if (c != null) carTransform = c.transform; + } + + // Re-cache path whenever it changes (road regen). + // Check both count and the 10th node position — regen always produces the same + // count (numSpans=100) but different positions for different seeds. + if (pathManager != null && pathManager.carPath != null) + { + var nodes = pathManager.carPath.centerNodes; + int n = nodes.Count; + float fx = n > 10 ? nodes[10].pos.x : float.MaxValue; + float fz = n > 10 ? nodes[10].pos.z : float.MaxValue; + if (n != _lastNodeCount || fx != _lastFirstNodeX || fz != _lastFirstNodeZ) + { + _lastFirstNodeX = fx; + _lastFirstNodeZ = fz; + RefreshPath(); + } + } + } + + void RefreshPath() + { + _nodes.Clear(); + _minX = float.MaxValue; _maxX = float.MinValue; + _minZ = float.MaxValue; _maxZ = float.MinValue; + + foreach (PathNode node in pathManager.carPath.centerNodes) + { + _nodes.Add(node.pos); + if (node.pos.x < _minX) _minX = node.pos.x; + if (node.pos.x > _maxX) _maxX = node.pos.x; + if (node.pos.z < _minZ) _minZ = node.pos.z; + if (node.pos.z > _maxZ) _maxZ = node.pos.z; + } + + _lastNodeCount = _nodes.Count; + _hasPath = _nodes.Count > 1; + } + + Vector2 WorldToMap(Vector3 world) + { + float rangeX = _maxX - _minX; + float rangeZ = _maxZ - _minZ; + float range = Mathf.Max(rangeX, rangeZ, 1f); + + float pad = 6f; + float inner = MAP_SIZE - pad * 2f; + + float px = MAP_X + pad + (world.x - _minX) / range * inner; + float pz = MAP_Y + pad + (1f - (world.z - _minZ) / range) * inner; // flip Z → screen Y + return new Vector2(px, pz); + } + + static void DrawLine(Vector2 a, Vector2 b, Color color, float thickness = 1.5f) + { + Vector2 delta = b - a; + if (delta.sqrMagnitude < 0.01f) return; + + float angle = Mathf.Atan2(delta.y, delta.x) * Mathf.Rad2Deg; + float length = delta.magnitude; + + Color prev = GUI.color; + GUI.color = color; + Matrix4x4 saved = GUI.matrix; + GUIUtility.RotateAroundPivot(angle, a); + GUI.DrawTexture(new Rect(a.x, a.y - thickness * 0.5f, length, thickness), WhiteTex); + GUI.matrix = saved; + GUI.color = prev; + } + + void OnGUI() + { + if (!_hasPath) return; + + // Dark background + GUI.DrawTexture(new Rect(MAP_X - 1, MAP_Y - 1, MAP_SIZE + 2, MAP_SIZE + 2), _bgTex); + + // Path lines + for (int i = 0; i < _nodes.Count - 1; i++) + { + Vector2 a = WorldToMap(_nodes[i]); + Vector2 b = WorldToMap(_nodes[i + 1]); + DrawLine(a, b, Color.white, 1.5f); + } + + // Car dot + if (carTransform != null) + { + Vector2 cp = WorldToMap(carTransform.position); + GUI.DrawTexture(new Rect(cp.x - 3f, cp.y - 3f, 6f, 6f), _dotTex); + } + } +} diff --git a/Scripts/MapOverlay.cs.meta b/Scripts/MapOverlay.cs.meta new file mode 100755 index 0000000..5fbef3a --- /dev/null +++ b/Scripts/MapOverlay.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 20dd129af531d464c9226ba59011e113 \ No newline at end of file diff --git a/Scripts/Marker.cs b/Scripts/Marker.cs new file mode 100755 index 0000000..e8e78e4 --- /dev/null +++ b/Scripts/Marker.cs @@ -0,0 +1,7 @@ +using UnityEngine; +using System.Collections; + +public class Marker : MonoBehaviour +{ + public int id; +} diff --git a/Scripts/Marker.cs.meta b/Scripts/Marker.cs.meta new file mode 100755 index 0000000..b2520e8 --- /dev/null +++ b/Scripts/Marker.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: b0bd2d647423d4a38b51dd85df5e0bbc +timeCreated: 1449072422 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/MenuHandler.cs b/Scripts/MenuHandler.cs new file mode 100755 index 0000000..da69484 --- /dev/null +++ b/Scripts/MenuHandler.cs @@ -0,0 +1,171 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityStandardAssets.Vehicles.Car; + +public class MenuHandler : MonoBehaviour { + + public GameObject PIDContoller; + public GameObject Logger; + public GameObject NetworkSteering; + public GameObject menuPanel; + public GameObject stopPanel; + public GameObject exitPanel; + public GameObject carJSControl; + public GameObject PIDControls; + public TrainingManager trainingManager; + + public void Awake() + { + //auto link + Canvas canvas = GameObject.FindObjectOfType(); + menuPanel = getChildGameObject(canvas.gameObject, "Panel Menu"); + stopPanel = getChildGameObject(canvas.gameObject, "StopPanel"); + exitPanel = getChildGameObject(canvas.gameObject, "ExitPanel"); + PIDControls = getChildGameObject(canvas.gameObject, "PIDPanel"); + + menuPanel.SetActive(true); + stopPanel.SetActive(false); + exitPanel.SetActive(true); + } + static public GameObject getChildGameObject(GameObject fromGameObject, string withName) + { + //Author: Isaac Dart, June-13. + Transform[] ts = fromGameObject.transform.GetComponentsInChildren(true); + foreach (Transform t in ts) if (t.gameObject.name == withName) return t.gameObject; + + Debug.LogError("Failed to find: " + withName); + return null; + } + + public void OnPidGenerateTrainingData() + { + if(Logger != null) + Logger.SetActive(true); + + if(PIDContoller != null) + PIDContoller.SetActive(true); + + if(carJSControl != null) + carJSControl.SetActive(false); + + if(PIDControls != null) + PIDControls.SetActive(true); + + menuPanel.SetActive(false); + stopPanel.SetActive(true); + exitPanel.SetActive(false); + } + + public void OnManualGenerateTrainingData() + { + if(Logger != null) + Logger.SetActive(true); + + if(PIDContoller != null) + PIDContoller.SetActive(false); + + if(carJSControl != null) + carJSControl.SetActive(true); + + if(PIDControls != null) + PIDControls.SetActive(false); + + menuPanel.SetActive(false); + stopPanel.SetActive(true); + exitPanel.SetActive(false); + } + + public void OnUseNNNetworkSteering() + { + if(carJSControl != null) + carJSControl.SetActive(false); + + if(PIDControls != null) + PIDControls.SetActive(false); + + if(NetworkSteering != null) + NetworkSteering.SetActive(true); + + menuPanel.SetActive(false); + stopPanel.SetActive(true); + exitPanel.SetActive(false); + + CarSpawner spawner = GameObject.FindObjectOfType(); + + if (spawner) + spawner.RemoveGhostCars(); + } + + public void OnPidDrive() + { + if(PIDContoller != null) + PIDContoller.SetActive(true); + + if(carJSControl != null) + carJSControl.SetActive(false); + + if(PIDControls != null) + PIDControls.SetActive(true); + + menuPanel.SetActive(false); + stopPanel.SetActive(true); + exitPanel.SetActive(false); + } + + public void OnManualDrive() + { + if(PIDContoller != null) + PIDContoller.SetActive(false); + + if(carJSControl != null) + carJSControl.SetActive(true); + + if(PIDControls != null) + PIDControls.SetActive(false); + + menuPanel.SetActive(false); + stopPanel.SetActive(true); + exitPanel.SetActive(false); + } + + public void OnNextTrack() + { + if(trainingManager != null) + trainingManager.OnMenuNextTrack(); + } + + public void OnRegenTrack() + { + if(trainingManager != null) + trainingManager.OnMenuRegenTrack(); + } + + public void OnStop() + { + if (PIDContoller != null) + PIDContoller.SetActive(false); + + if (carJSControl != null) + carJSControl.SetActive(false); + + if(PIDControls != null) + PIDControls.SetActive(false); + + if(Logger != null) + Logger.SetActive(false); + + if(NetworkSteering != null) + NetworkSteering.SetActive(false); + + menuPanel.SetActive(true); + stopPanel.SetActive(false); + exitPanel.SetActive(true); + + CarSpawner spawner = GameObject.FindObjectOfType(); + + if (spawner) + spawner.EnsureOneCar(); + } + +} diff --git a/Scripts/MenuHandler.cs.meta b/Scripts/MenuHandler.cs.meta new file mode 100755 index 0000000..b9a0ee9 --- /dev/null +++ b/Scripts/MenuHandler.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 2839404bd363747759350375dfeda1bb +timeCreated: 1484448737 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Odometry.cs b/Scripts/Odometry.cs new file mode 100755 index 0000000..0116540 --- /dev/null +++ b/Scripts/Odometry.cs @@ -0,0 +1,37 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +// This just doesn't work. The tires spin too fast for this reading to mean anything. + +public class Odometry : MonoBehaviour +{ + public string Label = "tire"; + private Quaternion lastRotation; + private double numRotations = 0.0f; + + private void Start() + { + lastRotation = GetTireRotation(); + } + + Quaternion GetTireRotation() + { + return transform.localRotation; + } + + public double GetNumberRotations() + { + return numRotations; + } + + + public void FixedUpdate() + { + Quaternion currentRotation = GetTireRotation(); + Quaternion deltaRotation = Quaternion.Inverse(lastRotation) * currentRotation; + float deltaAngle = 2.0f * Mathf.Rad2Deg * Mathf.Atan2(deltaRotation.x, deltaRotation.w); + numRotations += Mathf.Abs(deltaAngle / 360.0f); + lastRotation = currentRotation; + } +} diff --git a/Scripts/Odometry.cs.meta b/Scripts/Odometry.cs.meta new file mode 100755 index 0000000..5329847 --- /dev/null +++ b/Scripts/Odometry.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d00d139775b4a954fbd7632542262525 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/PIDController.cs b/Scripts/PIDController.cs new file mode 100755 index 0000000..b83da1b --- /dev/null +++ b/Scripts/PIDController.cs @@ -0,0 +1,160 @@ +using UnityEngine; +using System.Collections; +using UnityEngine.UI; + + +public class PIDController : MonoBehaviour +{ + + public GameObject carObj; + public ICar car; + public PathManager pm; + + float errA, errB; + float Kp = GlobalState.kp; + float Kd = GlobalState.kd; + float Ki = GlobalState.ki; + + //Ks is the proportion of the current vel that + //we use to sample ahead of the vehicles actual position. + public float Kv = 0.0f; + + //Ks is the proportion of the current err that + //we use to change throtlle. + public float Kt = 1.0f; + + float diffErr = 0f; + public float prevErr = 0f; + public float steeringReq = 0.0f; + public float throttleVal = 0.2f; + public float totalError = 0f; + public float absTotalError = 0f; + public float totalAcc = 0f; + public float totalOscilation = 0f; + public float AccelErrFactor = 0.1f; + public float OscilErrFactor = 10f; + + public delegate void OnEndOfPathCB(); + public OnEndOfPathCB endOfPathCB; + + public bool isDriving = true; + public bool brakeOnEnd = false; + public bool looping = true; + + public float maxSpeed = 5.0f; + public int iActiveSpan = 0; + public int lookAhead = 1; + + public Text pid_steering; + + void Awake() + { + car = carObj.GetComponent(); + pm = GameObject.FindObjectOfType(); + + if (pm == null) + Debug.LogWarning("couldn't get PathManager reference"); + + Canvas canvas = GameObject.FindObjectOfType(); + GameObject go = CarSpawner.getChildGameObject(canvas.gameObject, "PIDSteering"); + if (go != null) + pid_steering = go.GetComponent(); + + } + + private void OnEnable() + { + StartDriving(); + } + + private void OnDisable() + { + StopDriving(); + } + + public void StartDriving() + { + if (pm == null || !pm.isActiveAndEnabled || pm.carPath == null) + return; + + isDriving = true; + steeringReq = 0f; + prevErr = 0f; + totalError = 0f; + totalAcc = 0f; + totalOscilation = 0f; + absTotalError = 0f; + + iActiveSpan = pm.carPath.GetClosestSpanIndex(carObj.transform.position); + } + + public void StopDriving() + { + isDriving = false; + car.RequestThrottle(0.0f); + car.RequestHandBrake(1.0f); + car.RequestFootBrake(1.0f); + } + + // Update is called once per frame + void Update() + { + if (!isDriving) { return; } + + PathNode n = pm.carPath.GetNode(iActiveSpan); + + float velMag = car.GetVelocity().magnitude; + Vector3 samplePos = car.GetTransform().position + (car.GetTransform().forward * velMag * Kv); + + float err = 0.0f; + bool cte_ret = pm.carPath.GetCrossTrackErr(samplePos, ref iActiveSpan, ref err, lookAhead); + + diffErr = (err - prevErr) / Time.deltaTime; + steeringReq = -(Kp * err) - (Kd * diffErr) - (Ki * totalError); + // Debug.Log(steeringReq); + steeringReq = Mathf.Clamp(steeringReq, -car.GetMaxSteering(), car.GetMaxSteering()); + + car.RequestSteering(steeringReq); + + // need to refactor this + if (car.GetVelocity().magnitude < maxSpeed) + car.RequestThrottle(throttleVal); + + + if (pid_steering != null) + pid_steering.text = string.Format("PID: {0}", steeringReq); + + totalError += err; + prevErr = err; + + if (cte_ret) // check wether we lapped + { + if (looping) + { + var foundObjects = FindObjectsOfType(); + + foreach (var logger in foundObjects) + logger.lapCounter++; + } + else if (brakeOnEnd) + { + car.RequestFootBrake(1.0f); + + if (car.GetAccel().magnitude < 0.0001f) + { + isDriving = false; + + if (endOfPathCB != null) + endOfPathCB.Invoke(); + } + } + else + { + isDriving = false; + + if (endOfPathCB != null) + endOfPathCB.Invoke(); + } + } + } +} diff --git a/Scripts/PIDController.cs.meta b/Scripts/PIDController.cs.meta new file mode 100755 index 0000000..05df62c --- /dev/null +++ b/Scripts/PIDController.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: fffda7e09ce0344008103c39ef2ffe0a +timeCreated: 1449528963 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/PID_UI.cs b/Scripts/PID_UI.cs new file mode 100755 index 0000000..d64035e --- /dev/null +++ b/Scripts/PID_UI.cs @@ -0,0 +1,59 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; + +public class PID_UI : MonoBehaviour { + + public PIDController pid; + public Logger logger; + + public Text maxSpeedText; + public Text P_Term; + public Text D_Term; + public Text steerMax; + public Slider SpeedSlider; + public Slider PropSlider; + public Slider DiffSlider; + public Slider steerMaxSlider; + + void Start() + { + + } + + public void OnEnable() + { + steerMaxSlider.interactable = !logger.isActiveAndEnabled; + + if (pid.car != null) + { + steerMaxSlider.value = pid.car.GetMaxSteering(); + OnSteerMaxChanged(steerMaxSlider.value); + } + + SpeedSlider.value = pid.maxSpeed; + } + + public void OnMaxSpeedChanged(float val) + { + maxSpeedText.text = "Max Speed: " + val; + pid.maxSpeed = val; + } + + public void OnPTermChanged(float val) + { + P_Term.text = "Prop: " + val; + } + + public void OnDTermChanged(float val) + { + D_Term.text = "Diff: " + val; + } + + public void OnSteerMaxChanged(float val) + { + val = steerMaxSlider.value; + steerMax.text = "Steer Max: " + val; + } +} diff --git a/Scripts/PID_UI.cs.meta b/Scripts/PID_UI.cs.meta new file mode 100755 index 0000000..a765c2d --- /dev/null +++ b/Scripts/PID_UI.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 0c21ced5da9c049a4a67a804faffeb2c +timeCreated: 1507587076 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/PathManager.cs b/Scripts/PathManager.cs new file mode 100755 index 0000000..09b7b5a --- /dev/null +++ b/Scripts/PathManager.cs @@ -0,0 +1,511 @@ +using UnityEngine; +using UnityEditor; +using System.Collections; +using System.Globalization; +using System.Collections.Generic; +using PathCreation; + + +public class PathManager : MonoBehaviour +{ + public CarPath carPath; + public PathCreator pathCreator; + + [Header("Path type")] + public bool doMakeRandomPath = true; + public bool doLoadScriptPath = false; + public bool doLoadPointPath = false; + public bool doLoadGameObjectPath = false; + + [Header("Path making")] + public Transform startPos; + public string pathToLoad = "none"; + public int smoothPathIter = 0; + public GameObject locationMarkerPrefab; + public int markerEveryN = 2; + public bool doChangeLanes = false; + public bool invertNodes = false; + + [Header("Random path parameters")] + public int numSpans = 100; + public float turnInc = 1f; + public float spanDist = 5f; + public bool sameRandomPath = true; + public int randSeed = 2; + + [Header("Debug")] + public bool doShowNodePath = false; + public bool doShowCenterNodePath = false; + public GameObject pathelem; + + [Header("Aux")] + public GameObject[] initAfterCarPathLoaded; // Scripts using the IWaitCarPath interface to init after loading the CarPath + public GameObject[] challenges; // Challenges using the IWaitCarPath interface to init after loading the CarPath or on private API call + + Vector3 span = Vector3.zero; + GameObject generated_mesh; + + void Awake() + { + if (sameRandomPath) + Random.InitState(randSeed); + + InitCarPath(); + } + + public void InitCarPath() + { + if (doMakeRandomPath) + { + MakeRandomPath(); + } + else if (doLoadScriptPath) + { + MakeScriptedPath(); + } + else if (doLoadPointPath) + { + MakePointPath(); + } + else if (doLoadGameObjectPath) + { + MakeGameObjectPath(); + } + + if (carPath == null) // if no carPath was created, skip the following block of code + { + return; + } + + if (invertNodes) + { + CarPath new_carPath = new CarPath(); + for (int i = carPath.nodes.Count - 1; i > 0; i--) + { + PathNode node = carPath.nodes[i]; + node.rotation = node.rotation * Quaternion.AngleAxis(180, Vector3.up); + new_carPath.nodes.Add(node); + new_carPath.centerNodes.Add(node); + } + carPath = new_carPath; + } + + if (startPos != null) + { + // Get the closest point to the start and make it index 0 of carPath + int startIndex = 0; + float closest = float.MaxValue; + for (int i = 0; i < carPath.nodes.Count; i++) + { + PathNode node = carPath.nodes[i]; + float distance = Vector3.Distance(node.pos, startPos.position); + if (distance < closest) + { + closest = distance; + startIndex = i; + } + } + + if (startIndex != 0) + { + CarPath new_carPath = new CarPath(); + for (int i = startIndex; i < carPath.nodes.Count + startIndex; i++) + { + if (i % carPath.nodes.Count == 0) { continue; } // avoid two consecutive values to be the same + + PathNode node = carPath.nodes[i % carPath.nodes.Count]; + new_carPath.nodes.Add(node); + new_carPath.centerNodes.Add(node); + + } + // // close the loop + // new_carPath.nodes.Add(new_carPath.nodes[0]); + // new_carPath.centerNodes.Add(new_carPath.nodes[0]); + + carPath = new_carPath; + } + } + + // execute in the next update loop + UnityMainThreadDispatcher.Instance().Enqueue(InitAfterCarPathLoaded(initAfterCarPathLoaded)); + UnityMainThreadDispatcher.Instance().Enqueue(InitAfterCarPathLoaded(challenges)); + + // if (locationMarkerPrefab != null && carPath != null) + // { + // int iLocId = 0; + // for (int iN = 0; iN < carPath.nodes.Count; iN += markerEveryN) + // { + // Vector3 np = carPath.nodes[iN].pos; + // GameObject go = Instantiate(locationMarkerPrefab, np, Quaternion.identity) as GameObject; + // go.transform.parent = this.transform; + // go.GetComponent().id = iLocId; + // iLocId++; + // } + // } + + if (doShowNodePath) + { + for (int iN = 0; iN < carPath.nodes.Count; iN++) + { + Vector3 np = carPath.nodes[iN].pos; + Quaternion rotation = carPath.nodes[iN].rotation; + GameObject go = Instantiate(pathelem, np, rotation) as GameObject; + go.tag = "pathNode"; + go.transform.parent = this.transform; + } + } + + if (doShowCenterNodePath) + { + for (int iN = 0; iN < carPath.centerNodes.Count; iN++) + { + Vector3 np = carPath.centerNodes[iN].pos; + Quaternion rotation = carPath.centerNodes[iN].rotation; + GameObject go = Instantiate(pathelem, np, rotation) as GameObject; + go.tag = "pathNode"; + go.transform.parent = this.transform; + } + } + } + + public IEnumerator InitAfterCarPathLoaded(GameObject[] scriptList) + { + if (carPath != null) + { + foreach (GameObject go in scriptList) // Init each Object that need a carPath + { + try + { + IWaitCarPath script = go.GetComponent(); + if (script != null) + { + script.Init(); + } + else + { + Debug.LogError("Provided GameObject doesn't contain an IWaitCarPath script"); + } + } + catch (System.Exception e) + { + Debug.LogError(string.Format("Could not initialize: {0}, Exception: {1}", go.name, e)); + } + } + } + + else + { + Debug.LogError("No carPath loaded"); yield return null; + } + yield return null; + } + + public Vector3 GetPathStart() + { + return startPos.position; + } + + public Vector3 GetPathEnd() + { + int iN = carPath.nodes.Count - 1; + + if (iN < 0) + return GetPathStart(); + + return carPath.nodes[iN].pos; + } + + float nfmod(float a, float b) // formula for negative and positive modulo + { + return a - b * Mathf.Floor(a / b); + } + + void MakeGameObjectPath(float precision = 3f) + { + carPath = new CarPath(); + + List points = new List(); + + float stepping = 1 / (pathCreator.path.length * precision); + for (float i = 0; i < 1; i += stepping) + { + points.Add(pathCreator.path.GetPointAtTime(i)); + } + + + while (smoothPathIter > 0) // not working for the moment, looking forward using the same system as MakePointPath with LookAt + { + points = Chaikin(points); + smoothPathIter--; + } + + for (int i = 0; i < points.Count; i++) + { + Vector3 point = points[(int)nfmod(i, (points.Count - 1))]; + Vector3 previous_point = points[(int)nfmod(i - 1, (points.Count - 1))]; + Vector3 next_point = points[(int)nfmod(i + 1, (points.Count - 1))]; + + PathNode p = new PathNode(); + p.pos = point; + p.rotation = Quaternion.LookRotation(next_point - previous_point, Vector3.up); + carPath.nodes.Add(p); + carPath.centerNodes.Add(p); + } + } + + void MakePointPath() + { + string filename = pathToLoad; + + TextAsset bindata = Resources.Load("Track/" + filename) as TextAsset; + + if (bindata == null) + return; + + string[] lines = bindata.text.Split('\n'); + + Debug.Log(string.Format("found {0} path points. to load", lines.Length)); + + carPath = new CarPath(); + + Vector3 np = Vector3.zero; + + float offsetY = -0.1f; + List points = new List(); + + foreach (string line in lines) + { + string[] tokens = line.Split(','); + + if (tokens.Length != 3) + continue; + np.x = float.Parse(tokens[0], CultureInfo.InvariantCulture.NumberFormat); + np.y = float.Parse(tokens[1], CultureInfo.InvariantCulture.NumberFormat) + offsetY; + np.z = float.Parse(tokens[2], CultureInfo.InvariantCulture.NumberFormat); + + points.Add(np); + } + + while (smoothPathIter > 0) + { + points = Chaikin(points); + smoothPathIter--; + } + + for (int i = 0; i < points.Count; i++) + { + Vector3 point = points[(int)nfmod(i, (points.Count))]; + Vector3 previous_point = points[(int)nfmod(i - 1, (points.Count))]; + Vector3 next_point = points[(int)nfmod(i + 1, (points.Count))]; + + PathNode p = new PathNode(); + p.pos = point; + p.rotation = Quaternion.LookRotation(next_point - previous_point, Vector3.up); ; + carPath.nodes.Add(p); + carPath.centerNodes.Add(p); + } + } + + public List Chaikin(List pts) + { + List newPts = new List(); + int ptsCount = pts.Count; + + for (int j = 0; j < ptsCount; j++) + { + int i = j; + if (j < 0) { i = j + ptsCount; } + newPts.Add(pts[i] + (pts[(i + 1)%ptsCount] - pts[i]) * 0.75f); + newPts.Add(pts[(i + 1)%ptsCount] + (pts[(i + 2)%ptsCount] - pts[(i + 1)%ptsCount]) * 0.25f); + } + + // newPts.Add(pts[pts.Count - 1]); + return newPts; + } + + + void MakeScriptedPath() + { + TrackScript script = new TrackScript(); + + if (script.Read(pathToLoad)) + { + carPath = new CarPath(); + TrackParams tparams = new TrackParams(); + tparams.numToSet = 0; + tparams.rotCur = Quaternion.identity; + tparams.lastPos = startPos.position; + + float dY = 0.0f; + float turn = 0f; + + Vector3 s = startPos.position; + s.y = 0.5f; + span.x = 0f; + span.y = 0f; + span.z = spanDist; + float turnVal = 10.0f; + + List points = new List(); + + foreach (TrackScriptElem se in script.track) + { + if (se.state == TrackParams.State.AngleDY) + { + turnVal = se.value; + } + else if (se.state == TrackParams.State.CurveY) + { + turn = 0.0f; + dY = se.value * turnVal; + } + else + { + dY = 0.0f; + turn = 0.0f; + } + + for (int i = 0; i < se.numToSet; i++) + { + + Vector3 np = s; + PathNode p = new PathNode(); + p.pos = np; + points.Add(np); + + turn = dY; + + Quaternion rot = Quaternion.Euler(0.0f, turn, 0f); + span = rot * span.normalized; + span *= spanDist; + s = s + span; + } + + } + + + for (int i = 0; i < points.Count; i++) + { + Vector3 point = points[(int)nfmod(i, (points.Count))]; + Vector3 previous_point = points[(int)nfmod(i - 1, (points.Count))]; + Vector3 next_point = points[(int)nfmod(i + 1, (points.Count))]; + + PathNode p = new PathNode(); + p.pos = point; + p.rotation = Quaternion.LookRotation(next_point - previous_point, Vector3.up); ; + carPath.nodes.Add(p); + carPath.centerNodes.Add(p); + } + + } + } + + List GenerateCandidatePath() + { + Vector3 s = startPos.position; + float turn = 0f; + s.y = 0.5f; + + Vector3 localSpan = new Vector3(0f, 0f, spanDist); + List points = new List(); + + for (int iS = 0; iS < numSpans; iS++) + { + Vector3 np = s; + points.Add(np); + + float t = UnityEngine.Random.Range(-1.0f * turnInc, turnInc); + turn += t; + + Quaternion rot = Quaternion.Euler(0.0f, turn, 0f); + localSpan = rot * localSpan.normalized; + + if (SegmentCrossesPath(np + (localSpan.normalized * 100.0f), 90.0f, points.ToArray())) + { + turn *= -0.5f; + rot = Quaternion.Euler(0.0f, turn, 0f); + localSpan = rot * localSpan.normalized; + } + + localSpan *= spanDist; + s = s + localSpan; + } + + return points; + } + + bool SegmentsIntersect2D(Vector3 a, Vector3 b, Vector3 c, Vector3 d) + { + float d1x = b.x - a.x, d1z = b.z - a.z; + float d2x = d.x - c.x, d2z = d.z - c.z; + float cross = d1x * d2z - d1z * d2x; + if (Mathf.Abs(cross) < 0.0001f) return false; + float t = ((c.x - a.x) * d2z - (c.z - a.z) * d2x) / cross; + float u = ((c.x - a.x) * d1z - (c.z - a.z) * d1x) / cross; + return t >= 0f && t <= 1f && u >= 0f && u <= 1f; + } + + bool PathSelfIntersects(List pts) + { + int n = pts.Count; + for (int i = 0; i < n - 1; i++) + for (int j = i + 2; j < n - 1; j++) + if (SegmentsIntersect2D(pts[i], pts[i + 1], pts[j], pts[j + 1])) + return true; + return false; + } + + void MakeRandomPath() + { + carPath = new CarPath(); + + List points = null; + for (int attempt = 0; attempt < 20; attempt++) + { + points = GenerateCandidatePath(); + if (!PathSelfIntersects(points)) break; + Debug.LogWarning(string.Format("MakeRandomPath: self-intersection attempt {0}/20, retrying.", attempt + 1)); + } + + for (int i = 0; i < points.Count; i++) + { + Vector3 point = points[(int)nfmod(i, (points.Count))]; + Vector3 previous_point = points[(int)nfmod(i - 1, (points.Count))]; + Vector3 next_point = points[(int)nfmod(i + 1, (points.Count))]; + + PathNode p = new PathNode(); + p.pos = point; + p.rotation = Quaternion.LookRotation(next_point - previous_point, Vector3.up); ; + carPath.nodes.Add(p); + carPath.centerNodes.Add(p); + } + } + + public bool SegmentCrossesPath(Vector3 posA, float rad, Vector3[] posN) + { + foreach (Vector3 pn in posN) + { + float d = (posA - pn).magnitude; + + if (d < rad) + return true; + } + + return false; + } + + public void SetPath(CarPath p) + { + carPath = p; + + GameObject[] prev = GameObject.FindGameObjectsWithTag("pathNode"); + + Debug.Log(string.Format("Cleaning up {0} old nodes. {1} new ones.", prev.Length, p.nodes.Count)); + + foreach (PathNode pn in carPath.nodes) + { + GameObject go = Instantiate(pathelem, pn.pos, Quaternion.identity) as GameObject; + go.tag = "pathNode"; + } + } +} diff --git a/Scripts/PathManager.cs.meta b/Scripts/PathManager.cs.meta new file mode 100755 index 0000000..a32a9e6 --- /dev/null +++ b/Scripts/PathManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6940724510810407baa499c3bb3fc03c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/PrivateAPI.cs b/Scripts/PrivateAPI.cs new file mode 100755 index 0000000..d3ce36c --- /dev/null +++ b/Scripts/PrivateAPI.cs @@ -0,0 +1,30 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class PrivateAPI : MonoBehaviour +{ + private List clients = new List(); + + public void Init(tk.JsonTcpClient _client) + { + tk.TcpPrivateAPIHandler tcpPrivateAPIHandler = gameObject.AddComponent(); + tcpPrivateAPIHandler.Init(_client); + clients.Add(tcpPrivateAPIHandler); + } + + public void CollisionWithStatingLine(string name, int startingLineIndex, float timeStamp) + { + foreach (tk.TcpPrivateAPIHandler client in clients) + { + UnityMainThreadDispatcher.Instance().Enqueue(client.SendCollisionWithStartingLine(name, startingLineIndex, timeStamp)); + } + } + public void CollisionWithChallenge(string name, int coneIndex, float timeStamp) + { + foreach (tk.TcpPrivateAPIHandler client in clients) + { + UnityMainThreadDispatcher.Instance().Enqueue(client.SendCollisionWithChallenge(name, coneIndex, timeStamp)); + } + } +} diff --git a/Scripts/PrivateAPI.cs.meta b/Scripts/PrivateAPI.cs.meta new file mode 100755 index 0000000..d4acd63 --- /dev/null +++ b/Scripts/PrivateAPI.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a805a69b7f665dc4f99d20b54dada289 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/RaceManager.cs b/Scripts/RaceManager.cs new file mode 100755 index 0000000..02402a5 --- /dev/null +++ b/Scripts/RaceManager.cs @@ -0,0 +1,73 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class RaceManager : MonoBehaviour +{ + + public GameObject StartButton; + public GameObject StopButton; + public GameObject SplitButton; + public GameObject ContinueButton; + + void Awake() + { + StartButton.SetActive(true); + StopButton.SetActive(false); + SplitButton.SetActive(false); + ContinueButton.SetActive(false); + } + + public void StartRace() + { + Timer[] timers = GameObject.FindObjectsOfType(); + foreach(Timer t in timers) + { + t.StartTimer(); + } + StartButton.SetActive(false); + StopButton.SetActive(true); + SplitButton.SetActive(true); + ContinueButton.SetActive(false); + } + + public void StopRace() + { + Timer[] timers = GameObject.FindObjectsOfType(); + foreach(Timer t in timers) + { + t.DisableTimer(); + } + StartButton.SetActive(true); + StopButton.SetActive(false); + SplitButton.SetActive(false); + ContinueButton.SetActive(false); + } + + public void Split() + { + Timer[] timers = GameObject.FindObjectsOfType(); + foreach(Timer t in timers) + { + t.SplitTime(); + } + StartButton.SetActive(false); + StopButton.SetActive(true); + SplitButton.SetActive(false); + ContinueButton.SetActive(true); + } + + public void Continue() + { + Timer[] timers = GameObject.FindObjectsOfType(); + foreach(Timer t in timers) + { + t.ContinueTime(); + } + StartButton.SetActive(false); + StopButton.SetActive(true); + SplitButton.SetActive(true); + ContinueButton.SetActive(false); + } + +} diff --git a/Scripts/RaceManager.cs.meta b/Scripts/RaceManager.cs.meta new file mode 100755 index 0000000..5da3dca --- /dev/null +++ b/Scripts/RaceManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2b5d7e89bff8cc642b923500bc055f37 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/RaceStatus.cs b/Scripts/RaceStatus.cs new file mode 100755 index 0000000..0091abb --- /dev/null +++ b/Scripts/RaceStatus.cs @@ -0,0 +1,45 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; + +public class RaceStatus : MonoBehaviour +{ + public Timer timer; + public Text racerName; + public Text currentTotTime; + public Text penaltiesTime; + tk.JsonTcpClient client; + + public void Init(Timer _timer, tk.JsonTcpClient _client) + { + timer = _timer; + client = _client; + } + + void Update() + { + if(timer != null) + { + racerName.text = timer.racerName; + + if(timer.currentTotTimeDisp.gameObject.activeSelf && timer.enabled_timer) + { + currentTotTime.text = timer.currentTotTimeDisp.text; + } + else + { + currentTotTime.text = "--:--"; + } + + if(timer.penaltiesDisp.gameObject.activeSelf && timer.enabled_timer) + { + penaltiesTime.text = timer.penaltiesDisp.text; + } + else + { + penaltiesTime.text = "--:--"; + } + } + } +} diff --git a/Scripts/RaceStatus.cs.meta b/Scripts/RaceStatus.cs.meta new file mode 100755 index 0000000..d153556 --- /dev/null +++ b/Scripts/RaceStatus.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6c4893631ec4f1146be60e87b6de5367 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/RepeatObjectAlong.cs b/Scripts/RepeatObjectAlong.cs new file mode 100755 index 0000000..af73d00 --- /dev/null +++ b/Scripts/RepeatObjectAlong.cs @@ -0,0 +1,226 @@ +using UnityEngine; +using System.Collections; +using System.Collections.Generic; + + +[RequireComponent(typeof(MeshFilter))] +[RequireComponent(typeof(MeshRenderer))] +[RequireComponent(typeof(MeshCollider))] +public class RepeatObjectAlong : MonoBehaviour, IWaitCarPath +{ + public PathManager pathManager; // we will use the carPath from the pathManager + public Mesh[] meshesToRepeat; + + [Header("Offsets")] + public float leftWidthOffset = 1f; + public float rightWidthOffset = 1f; + public float[] yOffset; + public float[] xOffset; + public float[] rotateYOffset; + public float[] rotateXOffset; + + [Header("Repeating params")] + public Vector3 MeshScale = new Vector3(1, 1, 1); + public float placeEvery = 5f; + + [Header("Generation params")] + public bool generateAtRuntime = true; + public bool leftside = true; + public bool rightside = true; + public bool mirrorRotateObject = true; // apply a mirror rotation to the right side + + public string savePath = "Assets\\object_mesh.asset"; + + private List combineInstances; + + public void Generate() + { + if (pathManager != null && pathManager.carPath != null) + { + + if (xOffset.Length != meshesToRepeat.Length) + { + xOffset = new float[meshesToRepeat.Length]; + } + + if (yOffset.Length != meshesToRepeat.Length) + { + yOffset = new float[meshesToRepeat.Length]; + } + + if (rotateYOffset.Length != meshesToRepeat.Length) + { + rotateYOffset = new float[meshesToRepeat.Length]; + } + + if (rotateXOffset.Length != meshesToRepeat.Length) + { + rotateXOffset = new float[meshesToRepeat.Length]; + } + + + combineInstances = new List(); + + Vector3 leftScaling = MeshScale; + Vector3 rightScaling = MeshScale; + if (mirrorRotateObject) + { + rightScaling.x = -rightScaling.x; + } + + List> leftPositions = new List>(); + List> rightPositions = new List>(); + + for (int iM = 0; iM < meshesToRepeat.Length; iM++) + { + leftPositions.Add(new List()); + rightPositions.Add(new List()); + + PathNode node = pathManager.carPath.centerNodes[0]; + PathNode prevNode = pathManager.carPath.centerNodes[pathManager.carPath.centerNodes.Count - 1]; + + Vector3 leftPrevPosition = prevNode.pos + node.rotation * (Vector3.left * leftWidthOffset) + node.rotation * (Vector3.forward * xOffset[iM]) + yOffset[iM] * Vector3.up; + Vector3 rightPrevPosition = prevNode.pos + node.rotation * (Vector3.right * rightWidthOffset) + node.rotation * (Vector3.forward * xOffset[iM]) + yOffset[iM] * Vector3.up; + + for (int i = 0; i < pathManager.carPath.centerNodes.Count + 2; i++) + { + + node = pathManager.carPath.centerNodes[i % pathManager.carPath.centerNodes.Count]; + + if (leftside) + { + Vector3 tmp_point = node.pos + node.rotation * (Vector3.left * leftWidthOffset) + node.rotation * (Vector3.forward * xOffset[iM]) + yOffset[iM] * Vector3.up; + float prev_distance = Vector3.Distance(leftPrevPosition, tmp_point); + + if (prev_distance >= placeEvery) + { + float t_factor = placeEvery / prev_distance; + + Vector3 position = Vector3.Lerp(leftPrevPosition, tmp_point, t_factor); + leftPositions[iM].Add(position); + + leftPrevPosition = position; + } + } + + if (rightside) + { + Vector3 tmp_point = node.pos + node.rotation * (Vector3.right * rightWidthOffset) + node.rotation * (Vector3.forward * xOffset[iM]) + yOffset[iM] * Vector3.up; + float prev_distance = Vector3.Distance(rightPrevPosition, tmp_point); + + if (prev_distance >= placeEvery) + { + float t_factor = placeEvery / prev_distance; + + Vector3 position = Vector3.Lerp(rightPrevPosition, tmp_point, t_factor); + rightPositions[iM].Add(position); + + rightPrevPosition = position; + } + } + prevNode = node; + } + + } + + // go through every points and try to snap the mesh on them + + for (int iM = 0; iM < meshesToRepeat.Length; iM++) + { + for (int i = 0; i < leftPositions[iM].Count; i++) // left side + { + + Vector3 position = leftPositions[iM][i]; + Vector3 nextPosition = leftPositions[iM][(i + 1) % leftPositions[iM].Count]; + + Quaternion rot = Quaternion.LookRotation(nextPosition - position, Vector3.up) * Quaternion.AngleAxis(rotateYOffset[iM], Vector3.up) * Quaternion.AngleAxis(rotateXOffset[iM], Vector3.left); + Matrix4x4 transform = Matrix4x4.TRS((nextPosition + position) / 2, rot, leftScaling); + + Mesh mesh = Instantiate(meshesToRepeat[iM]); + CombineInstance comb = new CombineInstance(); + comb.mesh = mesh; + comb.transform = transform; + comb.subMeshIndex = iM; + combineInstances.Add(comb); + } + } + + for (int iM = 0; iM < meshesToRepeat.Length; iM++) + { + for (int i = 0; i < rightPositions[iM].Count; i++) // right side + { + + Vector3 position = rightPositions[iM][i]; + Vector3 nextPosition = rightPositions[iM][(i + 1) % rightPositions[iM].Count]; + + Quaternion rot = Quaternion.LookRotation(nextPosition - position, Vector3.up) * Quaternion.AngleAxis(-rotateYOffset[iM], Vector3.up) * Quaternion.AngleAxis(rotateXOffset[iM], Vector3.left); + Matrix4x4 transform = Matrix4x4.TRS((nextPosition + position) / 2, rot, rightScaling); + + Mesh mesh = Instantiate(meshesToRepeat[iM]); + CombineInstance comb = new CombineInstance(); + comb.mesh = mesh; + comb.transform = transform; + comb.subMeshIndex = iM; + combineInstances.Add(comb); + } + } + + // combine meshes and sub meshes into a unique mesh + MeshFilter mf = GetComponent(); + MeshRenderer mr = GetComponent(); + MeshCollider mc = GetComponent(); + Mesh finalMesh = mf.sharedMesh; + + // sort submeshes by index, seperate them + List> intermediateComb = new List>(); + for (int iM = 0; iM < meshesToRepeat.Length; iM++) + { + intermediateComb.Add(new List()); + } + for (int i = 0; i < combineInstances.Count; i++) + { + CombineInstance comb = combineInstances[i]; + int idx = comb.subMeshIndex; + comb.subMeshIndex = 0; + intermediateComb[idx].Add(comb); + } + + // once the submeshes are combined, combine the bigger meshes + List finalMeshCombs = new List(); + foreach (List submesh_combs in intermediateComb) + { + Mesh submesh = new Mesh(); + submesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; + submesh.CombineMeshes(submesh_combs.ToArray(), true, true); + + CombineInstance comb = new CombineInstance(); + comb.mesh = submesh; + finalMeshCombs.Add(comb); + } + + if (finalMesh == null) + { + finalMesh = new Mesh(); + } + + finalMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; + finalMesh.subMeshCount = meshesToRepeat.Length; + finalMesh.CombineMeshes(finalMeshCombs.ToArray(), false, false); + finalMesh.subMeshCount = meshesToRepeat.Length; + finalMesh.Optimize(); + finalMesh.RecalculateBounds(); + finalMesh.RecalculateNormals(); + finalMesh.RecalculateTangents(); + + mf.sharedMesh = finalMesh; + mc.sharedMesh = finalMesh; + } + } + public void Init() + { + if (generateAtRuntime) + { + Generate(); + } + } +} \ No newline at end of file diff --git a/Scripts/RepeatObjectAlong.cs.meta b/Scripts/RepeatObjectAlong.cs.meta new file mode 100755 index 0000000..e800e7a --- /dev/null +++ b/Scripts/RepeatObjectAlong.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a9709d87926686447ac11b06babeb6b3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/RoadBuilder.cs b/Scripts/RoadBuilder.cs new file mode 100755 index 0000000..9510cf8 --- /dev/null +++ b/Scripts/RoadBuilder.cs @@ -0,0 +1,537 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class RoadBuilder : MonoBehaviour, IWaitCarPath +{ + + public PathManager pathManager; + + [Header("Road building params")] + public bool doBuildRoad = false; + public bool doAddBarriers = true; + public bool closeLoop = true; + public float barrierHeight = 3.0f; + public float barrierOffset = 0.3f; + public float barrierThickness = 1.0f; + public float roadWidth = 1.0f; + public float roadHeightOffset = 0.0f; + public float roadOffsetW = 0.0f; + public GameObject roadPrefabMesh; + public int iRoadMaterial = 0; + public Material[] roadMaterials; + public float[] roadOffsets; + public float[] roadWidths; + + [Header("Terrain params (not working)")] + public bool doFlattenAtStart = true; + public bool doErodeTerrain = true; + public bool doGenerateTerrain = true; + public bool doFlattenArroundRoad = true; + public bool doLiftRoadToTerrain = false; + public TerrainToolkit terToolkit; + public Terrain terrain; + + [Header("Aux")] + public string savePath = "Assets\\generated_mesh.asset"; + + Texture customRoadTexure; + public GameObject createdRoad; + List debugCenterLine = new List(); + List debugLeftRoadEdge = new List(); + List debugRightRoadEdge = new List(); + List debugLeftBarrierEdge = new List(); + List debugRightBarrierEdge = new List(); + + [Header("Debug")] + public bool drawDebugEdges = false; + public bool showBarrierMeshes = false; + public Color barrierDebugColor = new Color(1.0f, 0.15f, 0.15f, 0.35f); + + void Start() + { + if (terToolkit != null && doErodeTerrain) + { + terToolkit.SmoothTerrain(10, 1.0f); + } + } + + public void Init() + { + if (doBuildRoad) + { + InitRoad(pathManager.carPath); + } + } + + public void DestroyRoad() + { + GameObject[] prev = GameObject.FindGameObjectsWithTag("road_mesh"); + + foreach (GameObject g in prev) + Destroy(g); + + iRoadMaterial += 1; + } + + public void SetNewRoadVariation(int iVariation) + { + if (roadMaterials.Length > 0) + customRoadTexure = roadMaterials[iVariation % roadMaterials.Length].mainTexture; + + if (roadOffsets.Length > 0) + roadOffsetW = roadOffsets[iVariation % roadOffsets.Length]; + + if (roadWidths.Length > 0) + roadWidth = roadWidths[iVariation % roadWidths.Length]; + } + + public void NegateYTiling() + { + if (createdRoad == null) + return; + + MeshRenderer mr = createdRoad.GetComponent(); + Vector2 ms = mr.material.mainTextureScale; + ms.y *= -1.0f; + mr.material.mainTextureScale = ms; + } + + Vector3 GetSegmentWidthNormal(Vector3 from, Vector3 to, Vector3 fallback) + { + Vector3 length = to - from; + if (length.sqrMagnitude < 1e-6f) + return fallback; + + Vector3 width = Vector3.Cross(length, Vector3.up); + if (width.sqrMagnitude < 1e-6f) + return fallback; + + return width.normalized; + } + + Vector3 GetNodeWidthNormal(List nodes, int index) + { + Vector3 fallback = Vector3.right; + int count = nodes.Count; + if (count < 2) + return fallback; + + if (closeLoop) + { + int prevIndex = (index - 1 + count) % count; + int nextIndex = (index + 1) % count; + Vector3 incoming = GetSegmentWidthNormal(nodes[prevIndex].pos, nodes[index].pos, fallback); + Vector3 outgoing = GetSegmentWidthNormal(nodes[index].pos, nodes[nextIndex].pos, incoming); + Vector3 combined = incoming + outgoing; + if (combined.sqrMagnitude < 1e-6f) + return outgoing; + return combined.normalized; + } + + if (index <= 0) + return GetSegmentWidthNormal(nodes[0].pos, nodes[1].pos, fallback); + + if (index >= count - 1) + return GetSegmentWidthNormal(nodes[count - 2].pos, nodes[count - 1].pos, fallback); + + Vector3 prev = GetSegmentWidthNormal(nodes[index - 1].pos, nodes[index].pos, fallback); + Vector3 next = GetSegmentWidthNormal(nodes[index].pos, nodes[index + 1].pos, prev); + Vector3 blend = prev + next; + if (blend.sqrMagnitude < 1e-6f) + return next; + return blend.normalized; + } + + List BuildOffsetEdge(List baseEdge, List widthNormals, float outwardOffset, bool isLeftSide) + { + List edge = new List(); + int uniqueCount = baseEdge.Count; + if (uniqueCount < 2) + return edge; + + if (closeLoop && (baseEdge[0] - baseEdge[uniqueCount - 1]).sqrMagnitude < 1e-6f) + uniqueCount -= 1; + + if (uniqueCount < 2) + return edge; + + float side = isLeftSide ? 1.0f : -1.0f; + + for (int i = 0; i < uniqueCount; i++) + { + Vector3 edgeOffset; + + if (!closeLoop && i == 0) + { + edgeOffset = widthNormals[i] * side * outwardOffset; + } + else if (!closeLoop && i == uniqueCount - 1) + { + edgeOffset = widthNormals[i - 1] * side * outwardOffset; + } + else + { + int prevIndex = closeLoop ? (i - 1 + uniqueCount) % uniqueCount : i - 1; + int nextIndex = i; + + Vector3 prevNormal = widthNormals[prevIndex] * side; + Vector3 nextNormal = widthNormals[nextIndex] * side; + + Vector3 bisector = prevNormal + nextNormal; + if (bisector.sqrMagnitude < 1e-6f) + { + edgeOffset = nextNormal * outwardOffset; + } + else + { + bisector.Normalize(); + float denom = Mathf.Abs(Vector3.Dot(bisector, nextNormal)); + denom = Mathf.Max(denom, 0.35f); + + float miterLength = outwardOffset / denom; + miterLength = Mathf.Min(miterLength, outwardOffset * 1.5f); + edgeOffset = bisector * miterLength; + } + } + + edge.Add(baseEdge[i] + edgeOffset); + } + + return edge; + } + + public GameObject InitRoad(CarPath path) + { + if (path == null) + { + Debug.LogWarning("No path in RoadBuilder::InitRoad"); + return null; + } + + if (terToolkit != null && doFlattenAtStart) + terToolkit.Flatten(); + + if (terToolkit != null && doGenerateTerrain) + terToolkit.PerlinGenerator(1, 0.1f, 10, 0.5f); + + GameObject go = GameObject.Instantiate(roadPrefabMesh); + MeshRenderer mr = go.GetComponent(); + MeshFilter mf = go.GetComponent(); + MeshCollider mc = go.GetComponent(); + Mesh mesh = new Mesh(); + createdRoad = go; + + if (customRoadTexure != null) + { + mr.material.mainTexture = customRoadTexure; + } + else if (roadMaterials != null && iRoadMaterial < roadMaterials.Length) + { + Material material = roadMaterials[iRoadMaterial]; + if (mr != null && material != null) + mr.material = material; + } + + go.tag = "road_mesh"; + + int numQuads = closeLoop ? path.nodes.Count : path.nodes.Count - 1; + int numVerts = path.nodes.Count * 2; + int numTris = numQuads * 2; + + Vector3[] vertices = new Vector3[numVerts]; + int[] tri = new int[numTris * 3]; + Vector3[] normals = new Vector3[numVerts]; + Vector2[] uv = new Vector2[numVerts]; + + for (int iN = 0; iN < numVerts; iN++) + normals[iN] = Vector3.up; + + path.centerNodes = new List(); + + List centerLine = new List(); + List leftRoadEdge = new List(); + List rightRoadEdge = new List(); + List widthNormals = new List(); + + for (int iNode = 0; iNode < path.nodes.Count; iNode++) + { + PathNode node = path.nodes[iNode]; + Vector3 pos = node.pos; + + Vector3 widthNormal = GetNodeWidthNormal(path.nodes, iNode); + + if (terToolkit != null && doFlattenArroundRoad && (iNode % 5) == 0) + terToolkit.FlattenArround(pos + widthNormal * roadOffsetW, 10.0f, 30.0f); + + if (doLiftRoadToTerrain) + pos.y = terrain.SampleHeight(pos) + 1.0f; + + pos.y += roadHeightOffset; + + Vector3 roadCenter = pos + widthNormal * roadOffsetW; + Vector3 leftPos = roadCenter + widthNormal * roadWidth; + Vector3 rightPos = roadCenter - widthNormal * roadWidth; + + PathNode centerNode = new PathNode(); + centerNode.pos = roadCenter; + centerNode.rotation = node.rotation; + path.centerNodes.Add(centerNode); + centerLine.Add(roadCenter); + leftRoadEdge.Add(leftPos); + rightRoadEdge.Add(rightPos); + widthNormals.Add(widthNormal); + + int iVert = iNode * 2; + vertices[iVert] = leftPos; + vertices[iVert + 1] = rightPos; + uv[iVert] = new Vector2(0.2f * iNode, 0.0f); + uv[iVert + 1] = new Vector2(0.2f * iNode, 1.0f); + } + + int iVertOffset = 0; + int iTriOffset = 0; + + for (int iQuad = 0; iQuad < numQuads; iQuad++) + { + int nextVertOffset = closeLoop ? (iVertOffset + 2) % numVerts : iVertOffset + 2; + tri[0 + iTriOffset] = iVertOffset; + tri[1 + iTriOffset] = nextVertOffset; + tri[2 + iTriOffset] = iVertOffset + 1; + tri[3 + iTriOffset] = nextVertOffset; + tri[4 + iTriOffset] = nextVertOffset + 1; + tri[5 + iTriOffset] = iVertOffset + 1; + iVertOffset += 2; + iTriOffset += 6; + } + + mesh.vertices = vertices; + mesh.triangles = tri; + mesh.normals = normals; + mesh.uv = uv; + mesh.Optimize(); + mesh.RecalculateBounds(); + mf.mesh = mesh; + mc.sharedMesh = mesh; + + if (doAddBarriers && centerLine.Count > 1) + { + List leftEdge = BuildOffsetEdge(leftRoadEdge, widthNormals, barrierOffset, true); + List rightEdge = BuildOffsetEdge(rightRoadEdge, widthNormals, barrierOffset, false); + + if (leftEdge.Count > 1 && rightEdge.Count > 1) + { + if (closeLoop) + { + leftEdge.Add(leftEdge[0]); + rightEdge.Add(rightEdge[0]); + } + CreateBarrier(leftEdge, "left_barrier", go.transform); + CreateBarrier(rightEdge, "right_barrier", go.transform); + + if (!closeLoop && centerLine.Count >= 2) + { + // Seal the open ends so the car cannot drive off either end of the track. + // Push the start cap 1 m back along the road so it doesn't sit on the spawn point. + Vector3 startDir = (centerLine[1] - centerLine[0]).normalized; + CreateEndCap( + leftEdge[0] - startDir, + rightEdge[0] - startDir, + "start_cap", go.transform); + CreateEndCap( + leftEdge[leftEdge.Count - 1], + rightEdge[rightEdge.Count - 1], + "end_cap", go.transform); + } + } + + debugLeftBarrierEdge = new List(leftEdge); + debugRightBarrierEdge = new List(rightEdge); + } + else + { + debugLeftBarrierEdge = new List(); + debugRightBarrierEdge = new List(); + } + + debugCenterLine = new List(centerLine); + debugLeftRoadEdge = new List(leftRoadEdge); + debugRightRoadEdge = new List(rightRoadEdge); + + if (terToolkit != null && doErodeTerrain) + { + terToolkit.SmoothTerrain(10, 1.0f); + + if (doFlattenArroundRoad) + { + foreach (PathNode n in path.nodes) + terToolkit.FlattenArround(n.pos, 8.0f, 10.0f); + } + } + + return go; + } + + // Creates a series of solid BoxColliders along a barrier edge path. + // Each segment gets its own box with real 3D volume so fast-moving cars + // cannot tunnel through between physics frames. + void CreateBarrier(List edgePositions, string barrierName, Transform parent) + { + int n = edgePositions.Count; + if (n < 2) return; + + GameObject containerGo = new GameObject(barrierName); + containerGo.transform.parent = parent; + containerGo.transform.localPosition = Vector3.zero; + containerGo.transform.localRotation = Quaternion.identity; + + // Overlap each segment slightly into its neighbours to close the tiny + // gap that would otherwise exist at the miter point between two boxes + // meeting at a corner. + float overlap = barrierThickness * 0.5f; + + for (int i = 0; i < n - 1; i++) + { + Vector3 a = edgePositions[i]; + Vector3 b = edgePositions[i + 1]; + Vector3 delta = b - a; + float segLen = delta.magnitude; + if (segLen < 0.001f) continue; + + // Box centre: midpoint horizontally, half-height up + Vector3 center = (a + b) * 0.5f + Vector3.up * (barrierHeight * 0.5f); + Quaternion rot = Quaternion.LookRotation(delta.normalized, Vector3.up); + + GameObject segGo = new GameObject(barrierName + "_seg" + i); + segGo.transform.parent = containerGo.transform; + segGo.transform.position = center; + segGo.transform.rotation = rot; + + // X = thickness (perpendicular to road surface — blocks lateral escape) + // Y = height + // Z = length along segment + overlap to close corner gaps + BoxCollider bc = segGo.AddComponent(); + bc.size = new Vector3(barrierThickness, barrierHeight, segLen + overlap); + bc.center = Vector3.zero; + + if (showBarrierMeshes) + { + MeshFilter meshFilter = segGo.AddComponent(); + meshFilter.sharedMesh = CreateBoxMesh(barrierThickness, barrierHeight, segLen + overlap); + MeshRenderer meshRend = segGo.AddComponent(); + meshRend.sharedMaterial = CreateBarrierDebugMaterial(); + meshRend.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; + meshRend.receiveShadows = false; + } + } + } + + // Solid wall across the track at an open end — prevents the car driving off + // the start or finish of a non-looping track. + void CreateEndCap(Vector3 leftPoint, Vector3 rightPoint, string capName, Transform parent) + { + Vector3 delta = rightPoint - leftPoint; + float width = delta.magnitude; + if (width < 0.001f) return; + + Vector3 center = (leftPoint + rightPoint) * 0.5f + Vector3.up * (barrierHeight * 0.5f); + // LookRotation: Z axis points left→right, so the box spans the full width + Quaternion rot = Quaternion.LookRotation(delta.normalized, Vector3.up); + + GameObject capGo = new GameObject(capName); + capGo.transform.parent = parent; + capGo.transform.position = center; + capGo.transform.rotation = rot; + + BoxCollider bc = capGo.AddComponent(); + // Z = left-to-right span (plus thickness on each side to overlap the side barriers) + // X = road-direction thickness + bc.size = new Vector3(barrierThickness, barrierHeight, width + barrierThickness * 2f); + bc.center = Vector3.zero; + + if (showBarrierMeshes) + { + MeshFilter meshFilter = capGo.AddComponent(); + meshFilter.sharedMesh = CreateBoxMesh(barrierThickness, barrierHeight, width + barrierThickness * 2f); + MeshRenderer meshRend = capGo.AddComponent(); + meshRend.sharedMaterial = CreateBarrierDebugMaterial(); + meshRend.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; + meshRend.receiveShadows = false; + } + } + + // Unit box mesh centred at origin — used for debug barrier visualisation. + Mesh CreateBoxMesh(float w, float h, float d) + { + float hw = w * 0.5f, hh = h * 0.5f, hd = d * 0.5f; + + Vector3[] verts = new Vector3[] + { + new Vector3(-hw, -hh, -hd), new Vector3( hw, -hh, -hd), + new Vector3( hw, hh, -hd), new Vector3(-hw, hh, -hd), + new Vector3(-hw, -hh, hd), new Vector3( hw, -hh, hd), + new Vector3( hw, hh, hd), new Vector3(-hw, hh, hd), + }; + + int[] tris = new int[] + { + 0,2,1, 0,3,2, // back + 4,5,6, 4,6,7, // front + 0,1,5, 0,5,4, // bottom + 3,7,6, 3,6,2, // top + 0,4,7, 0,7,3, // left + 1,2,6, 1,6,5, // right + }; + + Mesh m = new Mesh(); + m.vertices = verts; + m.triangles = tris; + m.RecalculateNormals(); + m.RecalculateBounds(); + return m; + } + + Material CreateBarrierDebugMaterial() + { + Material mat = new Material(Shader.Find("Standard")); + Color c = barrierDebugColor; + if (c.a <= 0.0f || (c.r + c.g + c.b) / 3.0f < 0.1f) + c = new Color(1.0f, 0.75f, 0.75f, 0.35f); + mat.color = c; + mat.SetFloat("_Mode", 3.0f); + mat.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha); + mat.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha); + mat.SetInt("_ZWrite", 0); + mat.SetInt("_Cull", (int)UnityEngine.Rendering.CullMode.Off); + mat.EnableKeyword("_ALPHABLEND_ON"); + mat.EnableKeyword("_EMISSION"); + mat.SetColor("_EmissionColor", c); + mat.renderQueue = 3000; + return mat; + } + + void OnDrawGizmosSelected() + { + if (!drawDebugEdges) + return; + + DrawDebugPolyline(debugCenterLine, Color.cyan); + DrawDebugPolyline(debugLeftRoadEdge, Color.green); + DrawDebugPolyline(debugRightRoadEdge, Color.green); + DrawDebugPolyline(debugLeftBarrierEdge, Color.red); + DrawDebugPolyline(debugRightBarrierEdge, Color.red); + } + + void DrawDebugPolyline(List points, Color color) + { + if (points == null || points.Count < 2) + return; + + Gizmos.color = color; + for (int i = 0; i < points.Count - 1; i++) + Gizmos.DrawLine(points[i], points[i + 1]); + + if (closeLoop && points.Count > 2) + Gizmos.DrawLine(points[points.Count - 1], points[0]); + } +} diff --git a/Scripts/RoadBuilder.cs.meta b/Scripts/RoadBuilder.cs.meta new file mode 100755 index 0000000..521b235 --- /dev/null +++ b/Scripts/RoadBuilder.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 7fff0d08bfd0e46d2abf6e21594718f1 +timeCreated: 1481929032 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/SandboxClient.cs b/Scripts/SandboxClient.cs new file mode 100755 index 0000000..8aa89c9 --- /dev/null +++ b/Scripts/SandboxClient.cs @@ -0,0 +1,22 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +[RequireComponent(typeof(tk.TcpClient))] +public class SandboxClient : MonoBehaviour +{ + tk.TcpClient _client = null; + + public void Awake() + { + _client = GetComponent(); + _client.onDataRecvCB += new tk.TcpClient.OnDataRecv(OnRecvData); + } + + public void OnRecvData(byte[] data) + { + /// echo client... + _client.SendDataToPeers(data); + } + +} diff --git a/Scripts/SandboxClient.cs.meta b/Scripts/SandboxClient.cs.meta new file mode 100755 index 0000000..7369540 --- /dev/null +++ b/Scripts/SandboxClient.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 317a964f6484fd34f82c29b63d342a00 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/SandboxServer.cs b/Scripts/SandboxServer.cs new file mode 100755 index 0000000..1fc562f --- /dev/null +++ b/Scripts/SandboxServer.cs @@ -0,0 +1,182 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using tk; +using System.Net; +using System.Net.Sockets; +using System; + +[RequireComponent(typeof(tk.TcpServer))] +public class SandboxServer : MonoBehaviour +{ + public string host; + public int port; + + tk.TcpServer _server = null; + + public GameObject clientTemplateObj = null; + public Transform spawn_pt; + public bool spawnCarswClients = true; + public bool privateAPI = false; + bool argHost = false; + bool argPort = false; + + public void CheckCommandLineConnectArgs() + { + string[] args = System.Environment.GetCommandLineArgs(); + + + if (!privateAPI) + { + for (int i = 0; i < args.Length; i++) + { + if (args[i] == "--host") + { + host = args[i + 1]; + argHost = true; + } + else if (args[i] == "--port") + { + port = int.Parse(args[i + 1]); + argPort = true; + } + } + } + + if (argHost == false) { host = GlobalState.host; } + if (argPort == false) { port = GlobalState.port; } + + if (privateAPI) + { + port = GlobalState.portPrivateAPI; + } + + } + + private void Awake() + { + _server = GetComponent(); + } + + // Start is called before the first frame update + void Start() + { + CheckCommandLineConnectArgs(); + + Debug.Log("SDSandbox Server starting."); + _server.onClientConntedCB += new tk.TcpServer.OnClientConnected(OnClientConnected); + _server.onClientDisconntedCB += new tk.TcpServer.OnClientDisconnected(OnClientDisconnected); + + _server.Run(host, port); + } + + // It's our responsibility to create a GameObject with a TcpClient + // and return it to the server. + public tk.TcpClient OnClientConnected() + { + if (clientTemplateObj == null) + { + Debug.LogError("client template object was null."); + return null; + } + + if (_server.debug) + Debug.Log("creating client obj"); + + GameObject go = GameObject.Instantiate(clientTemplateObj) as GameObject; + + go.transform.parent = this.transform; + + if (spawn_pt != null) + go.transform.position = spawn_pt.position + UnityEngine.Random.insideUnitSphere * 2; + + tk.TcpClient client = go.GetComponent(); + + InitClient(client); + + return client; + } + + private void InitClient(tk.TcpClient client) + { + if (privateAPI) // private API client server + { + PrivateAPI privateAPIHandler = GameObject.FindObjectOfType(); + if (privateAPIHandler != null) + { + if (_server.debug) + Debug.Log("init private API handler."); + + privateAPIHandler.Init(client.gameObject.GetComponent()); + } + } + + else // normal client server + { + if (spawnCarswClients) // we are on in a track scene + { + CarSpawner spawner = GameObject.FindObjectOfType(); + if (spawner != null) + { + if (_server.debug) + Debug.Log("spawning car."); + + spawner.Spawn(client.gameObject.GetComponent(), false); + } + } + else //we are in the menu + { + + tk.TcpMenuHandler handler = GameObject.FindObjectOfType(); + if (handler != null) + { + if (_server.debug) + Debug.Log("init menu handler."); + + handler.Init(client.gameObject.GetComponent()); + } + } + } + } + + + public void OnSceneLoaded(bool bFrontEnd) + { + spawnCarswClients = !bFrontEnd; + + List clients = _server.GetClients(); + + foreach (tk.TcpClient client in clients) + { + if (_server.debug) + Debug.Log("init network client."); + + InitClient(client); + } + + if (GlobalState.paceCar && !bFrontEnd) // && clients.Count == 0 + { + CarSpawner spawner = GameObject.FindObjectOfType(); + if (spawner) + { + spawner.EnsureOneCar(); + } + } + } + + public void OnClientDisconnected(tk.TcpClient client) + { + if (privateAPI) + { + } + else + { + CarSpawner spawner = GameObject.FindObjectOfType(); + if (spawner) + { + spawner.RemoveCar(client.gameObject.GetComponent()); + } + } + GameObject.Destroy(client.gameObject); + } +} diff --git a/Scripts/SandboxServer.cs.meta b/Scripts/SandboxServer.cs.meta new file mode 100755 index 0000000..1101fcd --- /dev/null +++ b/Scripts/SandboxServer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: de681c6241cee914386221fded32d866 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/SceneLoaded.cs b/Scripts/SceneLoaded.cs new file mode 100755 index 0000000..f558401 --- /dev/null +++ b/Scripts/SceneLoaded.cs @@ -0,0 +1,17 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class SceneLoaded : MonoBehaviour +{ + public bool inFrontEnd = false; + + // Start is called before the first frame update + void Start() + { + SandboxServer server = GameObject.FindObjectOfType(); + + if (server) + server.OnSceneLoaded(inFrontEnd); + } +} diff --git a/Scripts/SceneLoaded.cs.meta b/Scripts/SceneLoaded.cs.meta new file mode 100755 index 0000000..56a3fc9 --- /dev/null +++ b/Scripts/SceneLoaded.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2fb2ad5f6d9c3324583cced767b49b71 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/SceneLoader.cs b/Scripts/SceneLoader.cs new file mode 100755 index 0000000..d185078 --- /dev/null +++ b/Scripts/SceneLoader.cs @@ -0,0 +1,65 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.SceneManagement; +using SimpleFileBrowser; + +public class SceneLoader : MonoBehaviour +{ + + public void LoadMenuScene() + { + SceneManager.LoadSceneAsync("menu"); + } + + public void LoadScene(string scene_name) + { + Debug.Log(scene_name); + SceneManager.LoadSceneAsync(scene_name); + } + + public void QuitApplication() + { + Application.Quit(); + } + + public void SetLogDir() + { + // Show a select folder dialog + // onSuccess event: print the selected folder's path + // onCancel event: print "Canceled" + // Load file/folder: folder, Initial path: default (Documents), Title: "Select Folder", submit button text: "Select" + FileBrowser.ShowLoadDialog((path) => { OnSetLogDir(path); }, + () => { Debug.Log("Canceled"); }, + true, null, "Select Log Folder", "Select"); + } + + public void OnSetLogDir(string path) + { + Debug.Log("Selected: " + path); + GlobalState.log_path = path; + } + + public string[] LoadScenePathsFromFile(string directoryPath) + { + List scenePaths = new List(); + + // search in the directory + string[] paths = System.IO.Directory.GetFiles(directoryPath, "*", System.IO.SearchOption.AllDirectories); + foreach (string path in paths) + { + string extension = System.IO.Path.GetExtension(path); + if (extension != "") { continue; } // get only bundle Assets (no file extension) + + AssetBundle bundle = AssetBundle.LoadFromFile(path); + + if (GlobalState.bundleScenes != null) + { + scenePaths.AddRange(bundle.GetAllScenePaths()); + GlobalState.bundleScenes.Add(bundle); + } + + } + return scenePaths.ToArray(); + } +} diff --git a/Scripts/SceneLoader.cs.meta b/Scripts/SceneLoader.cs.meta new file mode 100755 index 0000000..673ba0d --- /dev/null +++ b/Scripts/SceneLoader.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 7ff08d1721e4860458a69f7963cb2dc9 +timeCreated: 1507228994 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/ScreenSaver.cs b/Scripts/ScreenSaver.cs new file mode 100755 index 0000000..e47f6ed --- /dev/null +++ b/Scripts/ScreenSaver.cs @@ -0,0 +1,35 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class ScreenSaver : MonoBehaviour +{ + public bool bRecordOnAwake = true; + public bool bRecording = false; + public static int frameCounter = 0; + public string filenameRoot = "capture"; + + void Awake() + { + if(bRecordOnAwake) + bRecording = true; + } + + // Update is called once per frame + void Update () { + + if(bRecording) + { + string filename = string.Format("{0}{1,8:D8}.png", filenameRoot, frameCounter); + StartCoroutine(TakeShot(filename)); + frameCounter += 1; + } + } + + IEnumerator TakeShot(string filename) + { + yield return new WaitForEndOfFrame(); + + ScreenCapture.CaptureScreenshot(filename); + } +} diff --git a/Scripts/ScreenSaver.cs.meta b/Scripts/ScreenSaver.cs.meta new file mode 100755 index 0000000..e168583 --- /dev/null +++ b/Scripts/ScreenSaver.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 086585b56b80c48c9af3ff2757a66f04 +timeCreated: 1481994703 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/SocketIO.meta b/Scripts/SocketIO.meta new file mode 100755 index 0000000..0b6a346 --- /dev/null +++ b/Scripts/SocketIO.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: a20872284e6eb454d8f02d7167239b44 +folderAsset: yes +timeCreated: 1499897222 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/SocketIO/JSONObject.meta b/Scripts/SocketIO/JSONObject.meta new file mode 100755 index 0000000..f3ef362 --- /dev/null +++ b/Scripts/SocketIO/JSONObject.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: c29bd351b460e1f47956b54df85ccea5 +folderAsset: yes +timeCreated: 1499897222 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/SocketIO/JSONObject/Editor.meta b/Scripts/SocketIO/JSONObject/Editor.meta new file mode 100755 index 0000000..86455e5 --- /dev/null +++ b/Scripts/SocketIO/JSONObject/Editor.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 8e4c39246a2f8ee4c91719c704d541ea +folderAsset: yes +timeCreated: 1499897222 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/SocketIO/JSONObject/Editor/JSONChecker.cs b/Scripts/SocketIO/JSONObject/Editor/JSONChecker.cs new file mode 100755 index 0000000..0b34878 --- /dev/null +++ b/Scripts/SocketIO/JSONObject/Editor/JSONChecker.cs @@ -0,0 +1,51 @@ +//#define PERFTEST //For testing performance of parse/stringify. Turn on editor profiling to see how we're doing + +using UnityEngine; +using UnityEditor; + +public class JSONChecker : EditorWindow { + string JSON = @"{ + ""TestObject"": { + ""SomeText"": ""Blah"", + ""SomeObject"": { + ""SomeNumber"": 42, + ""SomeBool"": true, + ""SomeNull"": null + }, + + ""SomeEmptyObject"": { }, + ""SomeEmptyArray"": [ ], + ""EmbeddedObject"": ""{\""field\"":\""Value with \\\""escaped quotes\\\""\""}"" + } +}"; //dat string literal... + JSONObject j; + [MenuItem("Window/JSONChecker")] + static void Init() { + GetWindow(typeof(JSONChecker)); + } + void OnGUI() { + JSON = EditorGUILayout.TextArea(JSON); + GUI.enabled = !string.IsNullOrEmpty(JSON); + if(GUILayout.Button("Check JSON")) { +#if PERFTEST + Profiler.BeginSample("JSONParse"); + j = JSONObject.Create(JSON); + Profiler.EndSample(); + Profiler.BeginSample("JSONStringify"); + j.ToString(true); + Profiler.EndSample(); +#else + j = JSONObject.Create(JSON); +#endif + Debug.Log(j.ToString(true)); + } + if(j) { + //Debug.Log(System.GC.GetTotalMemory(false) + ""); + if(j.type == JSONObject.Type.NULL) + GUILayout.Label("JSON fail:\n" + j.ToString(true)); + else + GUILayout.Label("JSON success:\n" + j.ToString(true)); + + } + } +} diff --git a/Scripts/SocketIO/JSONObject/Editor/JSONChecker.cs.meta b/Scripts/SocketIO/JSONObject/Editor/JSONChecker.cs.meta new file mode 100755 index 0000000..c4ef6c8 --- /dev/null +++ b/Scripts/SocketIO/JSONObject/Editor/JSONChecker.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: b74cee98b41467c4293db42882ce83b6 +timeCreated: 1499897224 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/SocketIO/JSONObject/JSONObject.cs b/Scripts/SocketIO/JSONObject/JSONObject.cs new file mode 100755 index 0000000..2e7c8ac --- /dev/null +++ b/Scripts/SocketIO/JSONObject/JSONObject.cs @@ -0,0 +1,1020 @@ +#define PRETTY //Comment out when you no longer need to read JSON to disable pretty Print system-wide +//Using doubles will cause errors in VectorTemplates.cs; Unity speaks floats +//#define USEFLOAT //Use floats for numbers instead of doubles (enable if you're getting too many significant digits in string output) +//#define POOLING //Currently using a build setting for this one (also it's experimental) + +using System.Diagnostics; +using UnityEngine; +using System.Collections; +using System.Collections.Generic; +using System.Text; +using Debug = UnityEngine.Debug; + +/* + * http://www.opensource.org/licenses/lgpl-2.1.php + * JSONObject class + * for use with Unity + * Copyright Matt Schoen 2010 - 2013 + */ + +public class JSONObject { +#if POOLING + const int MAX_POOL_SIZE = 10000; + public static Queue releaseQueue = new Queue(); +#endif + + const int MAX_DEPTH = 100; + const string INFINITY = "\"INFINITY\""; + const string NEGINFINITY = "\"NEGINFINITY\""; + const string NaN = "\"NaN\""; + static readonly char[] WHITESPACE = new[] { ' ', '\r', '\n', '\t' }; + public enum Type { NULL, STRING, NUMBER, OBJECT, ARRAY, BOOL, BAKED } + public bool isContainer { get { return (type == Type.ARRAY || type == Type.OBJECT); } } + public Type type = Type.NULL; + public int Count { + get { + if(list == null) + return -1; + return list.Count; + } + } + public List list; + public List keys; + public string str; +#if USEFLOAT + public float n; + public float f { + get { + return n; + } + } +#else + public double n; + public float f { + get { + return (float)n; + } + } +#endif + public bool b; + public delegate void AddJSONConents(JSONObject self); + + public static JSONObject nullJO { get { return Create(Type.NULL); } } //an empty, null object + public static JSONObject obj { get { return Create(Type.OBJECT); } } //an empty object + public static JSONObject arr { get { return Create(Type.ARRAY); } } //an empty array + + public JSONObject(Type t) { + type = t; + switch(t) { + case Type.ARRAY: + list = new List(); + break; + case Type.OBJECT: + list = new List(); + keys = new List(); + break; + } + } + public JSONObject(bool b) { + type = Type.BOOL; + this.b = b; + } +#if USEFLOAT + public JSONObject(float f) { + type = Type.NUMBER; + n = f; + } +#else + public JSONObject(double d) { + type = Type.NUMBER; + n = d; + } +#endif + public JSONObject(Dictionary dic) { + type = Type.OBJECT; + keys = new List(); + list = new List(); + //Not sure if it's worth removing the foreach here + foreach(KeyValuePair kvp in dic) { + keys.Add(kvp.Key); + list.Add(CreateStringObject(kvp.Value)); + } + } + public JSONObject(Dictionary dic) { + type = Type.OBJECT; + keys = new List(); + list = new List(); + //Not sure if it's worth removing the foreach here + foreach(KeyValuePair kvp in dic) { + keys.Add(kvp.Key); + list.Add(kvp.Value); + } + } + public JSONObject(AddJSONConents content) { + content.Invoke(this); + } + public JSONObject(JSONObject[] objs) { + type = Type.ARRAY; + list = new List(objs); + } + //Convenience function for creating a JSONObject containing a string. This is not part of the constructor so that malformed JSON data doesn't just turn into a string object + public static JSONObject StringObject(string val) { return CreateStringObject(val); } + public void Absorb(JSONObject obj) { + list.AddRange(obj.list); + keys.AddRange(obj.keys); + str = obj.str; + n = obj.n; + b = obj.b; + type = obj.type; + } + public static JSONObject Create() { +#if POOLING + JSONObject result = null; + while(result == null && releaseQueue.Count > 0) { + result = releaseQueue.Dequeue(); +#if DEV + //The following cases should NEVER HAPPEN (but they do...) + if(result == null) + Debug.Log("wtf " + releaseQueue.Count); + else if(result.list != null) + Debug.Log("wtflist " + result.list.Count); +#endif + } + if(result != null) + return result; +#endif + return new JSONObject(); + } + public static JSONObject Create(Type t) { + JSONObject obj = Create(); + obj.type = t; + switch(t) { + case Type.ARRAY: + obj.list = new List(); + break; + case Type.OBJECT: + obj.list = new List(); + obj.keys = new List(); + break; + } + return obj; + } + public static JSONObject Create(bool val) { + JSONObject obj = Create(); + obj.type = Type.BOOL; + obj.b = val; + return obj; + } + public static JSONObject Create(float val) { + JSONObject obj = Create(); + obj.type = Type.NUMBER; + obj.n = val; + return obj; + } + public static JSONObject Create(double val) { + JSONObject obj = Create(); + obj.type = Type.NUMBER; + obj.n = val; + return obj; + } + public static JSONObject Create(int val) { + JSONObject obj = Create(); + obj.type = Type.NUMBER; + obj.n = val; + return obj; + } + public static JSONObject CreateStringObject(string val) { + JSONObject obj = Create(); + obj.type = Type.STRING; + obj.str = val; + return obj; + } + public static JSONObject CreateBakedObject(string val) { + JSONObject bakedObject = Create(); + bakedObject.type = Type.BAKED; + bakedObject.str = val; + return bakedObject; + } + + + + /// + /// Create a JSONObject by parsing string data + /// + /// The string to be parsed + /// The maximum depth for the parser to search. Set this to to 1 for the first level, + /// 2 for the first 2 levels, etc. It defaults to -2 because -1 is the depth value that is parsed (see below) + /// Whether to store levels beyond maxDepth in baked JSONObjects + /// Whether to be strict in the parsing. For example, non-strict parsing will successfully + /// parse "a string" into a string-type + /// + /// + + // Default parameters fix + public static JSONObject Create(string val) { return Create(val, -2, false, false); } + public static JSONObject Create(string val, int maxDepth) { return Create(val, maxDepth, false, false); } + public static JSONObject Create(string val, int maxDepth, bool storeExcessLevels) { return Create(val, maxDepth, storeExcessLevels, false); } + public static JSONObject Create(string val, int maxDepth, bool storeExcessLevels, bool strict) { + JSONObject obj = Create(); + obj.Parse(val, maxDepth, storeExcessLevels, strict); + return obj; + } + public static JSONObject Create(AddJSONConents content) { + JSONObject obj = Create(); + content.Invoke(obj); + return obj; + } + public static JSONObject Create(Dictionary dic) { + JSONObject obj = Create(); + obj.type = Type.OBJECT; + obj.keys = new List(); + obj.list = new List(); + //Not sure if it's worth removing the foreach here + foreach(KeyValuePair kvp in dic) { + obj.keys.Add(kvp.Key); + obj.list.Add(CreateStringObject(kvp.Value)); + } + return obj; + } + public JSONObject() { } + #region PARSE + + // Default parameters fix + public JSONObject(string str){ Parse(str, -2, false, false); } + public JSONObject(string str, int maxDepth, bool storeExcessLevels, bool strict) { //create a new JSONObject from a string (this will also create any children, and parse the whole string) + Parse(str, maxDepth, storeExcessLevels, strict); + } + + // Default parameters fix + void Parse(string str) { Parse(str, -2, false, false); } + void Parse(string str, int maxDepth, bool storeExcessLevels, bool strict) { + if(!string.IsNullOrEmpty(str)) { + str = str.Trim(WHITESPACE); + if(strict) { + if(str[0] != '[' && str[0] != '{') { + type = Type.NULL; + Debug.LogWarning("Improper (strict) JSON formatting. First character must be [ or {"); + return; + } + } + if(str.Length > 0) { + if(string.Compare(str, "true", true) == 0) { + type = Type.BOOL; + b = true; + } else if(string.Compare(str, "false", true) == 0) { + type = Type.BOOL; + b = false; + } else if(string.Compare(str, "null", true) == 0) { + type = Type.NULL; +#if USEFLOAT + } else if(str == INFINITY) { + type = Type.NUMBER; + n = float.PositiveInfinity; + } else if(str == NEGINFINITY) { + type = Type.NUMBER; + n = float.NegativeInfinity; + } else if(str == NaN) { + type = Type.NUMBER; + n = float.NaN; +#else + } else if(str == INFINITY) { + type = Type.NUMBER; + n = double.PositiveInfinity; + } else if(str == NEGINFINITY) { + type = Type.NUMBER; + n = double.NegativeInfinity; + } else if(str == NaN) { + type = Type.NUMBER; + n = double.NaN; +#endif + } else if(str[0] == '"') { + type = Type.STRING; + this.str = str.Substring(1, str.Length - 2); + } else { + int tokenTmp = 1; + /* + * Checking for the following formatting (www.json.org) + * object - {"field1":value,"field2":value} + * array - [value,value,value] + * value - string - "string" + * - number - 0.0 + * - bool - true -or- false + * - null - null + */ + int offset = 0; + switch(str[offset]) { + case '{': + type = Type.OBJECT; + keys = new List(); + list = new List(); + break; + case '[': + type = Type.ARRAY; + list = new List(); + break; + default: + try { +#if USEFLOAT + n = System.Convert.ToSingle(str); +#else + n = System.Convert.ToDouble(str); +#endif + type = Type.NUMBER; + } catch(System.FormatException) { + type = Type.NULL; + Debug.LogWarning("improper JSON formatting:" + str); + } + return; + } + string propName = ""; + bool openQuote = false; + bool inProp = false; + int depth = 0; + while(++offset < str.Length) { + if(System.Array.IndexOf(WHITESPACE, str[offset]) > -1) + continue; + if(str[offset] == '\\') { + offset += 1; + continue; + } + if(str[offset] == '"') { + if(openQuote) { + if(!inProp && depth == 0 && type == Type.OBJECT) + propName = str.Substring(tokenTmp + 1, offset - tokenTmp - 1); + openQuote = false; + } else { + if(depth == 0 && type == Type.OBJECT) + tokenTmp = offset; + openQuote = true; + } + } + if(openQuote) + continue; + if(type == Type.OBJECT && depth == 0) { + if(str[offset] == ':') { + tokenTmp = offset + 1; + inProp = true; + } + } + + if(str[offset] == '[' || str[offset] == '{') { + depth++; + } else if(str[offset] == ']' || str[offset] == '}') { + depth--; + } + //if (encounter a ',' at top level) || a closing ]/} + if((str[offset] == ',' && depth == 0) || depth < 0) { + inProp = false; + string inner = str.Substring(tokenTmp, offset - tokenTmp).Trim(WHITESPACE); + if(inner.Length > 0) { + if(type == Type.OBJECT) + keys.Add(propName); + if(maxDepth != -1) //maxDepth of -1 is the end of the line + list.Add(Create(inner, (maxDepth < -1) ? -2 : maxDepth - 1)); + else if(storeExcessLevels) + list.Add(CreateBakedObject(inner)); + + } + tokenTmp = offset + 1; + } + } + } + } else type = Type.NULL; + } else type = Type.NULL; //If the string is missing, this is a null + //Profiler.EndSample(); + } + #endregion + public bool IsNumber { get { return type == Type.NUMBER; } } + public bool IsNull { get { return type == Type.NULL; } } + public bool IsString { get { return type == Type.STRING; } } + public bool IsBool { get { return type == Type.BOOL; } } + public bool IsArray { get { return type == Type.ARRAY; } } + public bool IsObject { get { return type == Type.OBJECT; } } + public void Add(bool val) { + Add(Create(val)); + } + public void Add(float val) { + Add(Create(val)); + } + public void Add(int val) { + Add(Create(val)); + } + public void Add(string str) { + Add(CreateStringObject(str)); + } + public void Add(AddJSONConents content) { + Add(Create(content)); + } + public void Add(JSONObject obj) { + if(obj) { //Don't do anything if the object is null + if(type != Type.ARRAY) { + type = Type.ARRAY; //Congratulations, son, you're an ARRAY now + if(list == null) + list = new List(); + } + list.Add(obj); + } + } + public void AddField(string name, bool val) { + AddField(name, Create(val)); + } + public void AddField(string name, float val) { + AddField(name, Create(val)); + } + public void AddField(string name, double val) { + AddField(name, Create(val)); + } + public void AddField(string name, int val) { + AddField(name, Create(val)); + } + public void AddField(string name, AddJSONConents content) { + AddField(name, Create(content)); + } + public void AddField(string name, string val) { + AddField(name, CreateStringObject(val)); + } + public void AddField(string name, JSONObject obj) { + if(obj) { //Don't do anything if the object is null + if(type != Type.OBJECT) { + if(keys == null) + keys = new List(); + if(type == Type.ARRAY) { + for(int i = 0; i < list.Count; i++) + keys.Add(i + ""); + } else + if(list == null) + list = new List(); + type = Type.OBJECT; //Congratulations, son, you're an OBJECT now + } + keys.Add(name); + list.Add(obj); + } + } + public void SetField(string name, bool val) { SetField(name, Create(val)); } + public void SetField(string name, float val) { SetField(name, Create(val)); } + public void SetField(string name, int val) { SetField(name, Create(val)); } + public void SetField(string name, JSONObject obj) { + if(HasField(name)) { + list.Remove(this[name]); + keys.Remove(name); + } + AddField(name, obj); + } + public void RemoveField(string name) { + if(keys.IndexOf(name) > -1) { + list.RemoveAt(keys.IndexOf(name)); + keys.Remove(name); + } + } + public delegate void FieldNotFound(string name); + public delegate void GetFieldResponse(JSONObject obj); + + // Default parameters fix + public void GetField(ref bool field, string name) { GetField(ref field, name, null); } + public void GetField(ref bool field, string name, FieldNotFound fail) { + if(type == Type.OBJECT) { + int index = keys.IndexOf(name); + if(index >= 0) { + field = list[index].b; + return; + } + } + if(fail != null) fail.Invoke(name); + } +#if USEFLOAT + // Default parameters fix + public void GetField(ref float field, string name) { GetField(ref field, name, null); } + public void GetField(ref float field, string name, FieldNotFound fail) { +#else + // Default parameters fix + public void GetField(ref double field, string name) { GetField(ref field, name, null); } + public void GetField(ref double field, string name, FieldNotFound fail) { +#endif + if(type == Type.OBJECT) { + int index = keys.IndexOf(name); + if(index >= 0) { + field = list[index].n; + return; + } + } + if(fail != null) fail.Invoke(name); + } + + // Default parameters fix + public void GetField(ref int field, string name) { GetField(ref field, name, null); } + public void GetField(ref int field, string name, FieldNotFound fail) { + if(type == Type.OBJECT) { + int index = keys.IndexOf(name); + if(index >= 0) { + field = (int)list[index].n; + return; + } + } + if(fail != null) fail.Invoke(name); + } + + // Default parameters fix + public void GetField(ref uint field, string name) { GetField(ref field, name, null); } + public void GetField(ref uint field, string name, FieldNotFound fail) { + if(type == Type.OBJECT) { + int index = keys.IndexOf(name); + if(index >= 0) { + field = (uint)list[index].n; + return; + } + } + if(fail != null) fail.Invoke(name); + } + + // Default parameters fix + public void GetField(ref string field, string name) { GetField(ref field, name, null); } + public void GetField(ref string field, string name, FieldNotFound fail) { + if(type == Type.OBJECT) { + int index = keys.IndexOf(name); + if(index >= 0) { + field = list[index].str; + return; + } + } + if(fail != null) fail.Invoke(name); + } + + // Default parameters fix + public void GetField(string name, GetFieldResponse response) { GetField(name, response, null); } + public void GetField(string name, GetFieldResponse response, FieldNotFound fail) { + if(response != null && type == Type.OBJECT) { + int index = keys.IndexOf(name); + if(index >= 0) { + response.Invoke(list[index]); + return; + } + } + if(fail != null) fail.Invoke(name); + } + public JSONObject GetField(string name) { + if(type == Type.OBJECT) + for(int i = 0; i < keys.Count; i++) + if(keys[i] == name) + return list[i]; + return null; + } + public bool HasFields(string[] names) { + for(int i = 0; i < names.Length; i++) + if(!keys.Contains(names[i])) + return false; + return true; + } + public bool HasField(string name) { + if(type == Type.OBJECT) + for(int i = 0; i < keys.Count; i++) + if(keys[i] == name) + return true; + return false; + } + public void Clear() { + type = Type.NULL; + if(list != null) + list.Clear(); + if(keys != null) + keys.Clear(); + str = ""; + n = 0; + b = false; + } + /// + /// Copy a JSONObject. This could probably work better + /// + /// + public JSONObject Copy() { + return Create(Print()); + } + /* + * The Merge function is experimental. Use at your own risk. + */ + public void Merge(JSONObject obj) { + MergeRecur(this, obj); + } + /// + /// Merge object right into left recursively + /// + /// The left (base) object + /// The right (new) object + static void MergeRecur(JSONObject left, JSONObject right) { + if(left.type == Type.NULL) + left.Absorb(right); + else if(left.type == Type.OBJECT && right.type == Type.OBJECT) { + for(int i = 0; i < right.list.Count; i++) { + string key = right.keys[i]; + if(right[i].isContainer) { + if(left.HasField(key)) + MergeRecur(left[key], right[i]); + else + left.AddField(key, right[i]); + } else { + if(left.HasField(key)) + left.SetField(key, right[i]); + else + left.AddField(key, right[i]); + } + } + } else if(left.type == Type.ARRAY && right.type == Type.ARRAY) { + if(right.Count > left.Count) { + Debug.LogError("Cannot merge arrays when right object has more elements"); + return; + } + for(int i = 0; i < right.list.Count; i++) { + if(left[i].type == right[i].type) { //Only overwrite with the same type + if(left[i].isContainer) + MergeRecur(left[i], right[i]); + else { + left[i] = right[i]; + } + } + } + } + } + public void Bake() { + if(type != Type.BAKED) { + str = Print(); + type = Type.BAKED; + } + } + public IEnumerable BakeAsync() { + if(type != Type.BAKED) { + foreach(string s in PrintAsync()) { + if(s == null) + yield return s; + else { + str = s; + } + } + type = Type.BAKED; + } + } +#pragma warning disable 219 + // Default parameters fix + public string Print() { return Print(false); } + public string Print(bool pretty) { + StringBuilder builder = new StringBuilder(); + Stringify(0, builder, pretty); + return builder.ToString(); + } + // Default parameters fix + public IEnumerable PrintAsync() { return PrintAsync(false); } + public IEnumerable PrintAsync(bool pretty) { + StringBuilder builder = new StringBuilder(); + printWatch.Reset(); + printWatch.Start(); + foreach(IEnumerable e in StringifyAsync(0, builder, pretty)) { + yield return null; + } + yield return builder.ToString(); + } +#pragma warning restore 219 + #region STRINGIFY + const float maxFrameTime = 0.008f; + static readonly Stopwatch printWatch = new Stopwatch(); + + // Default parameters fix + IEnumerable StringifyAsync(int depth, StringBuilder builder) { return StringifyAsync(depth, builder, false); } + IEnumerable StringifyAsync(int depth, StringBuilder builder, bool pretty) { //Convert the JSONObject into a string + //Profiler.BeginSample("JSONprint"); + if(depth++ > MAX_DEPTH) { + Debug.Log("reached max depth!"); + yield break; + } + if(printWatch.Elapsed.TotalSeconds > maxFrameTime) { + printWatch.Reset(); + yield return null; + printWatch.Start(); + } + switch(type) { + case Type.BAKED: + builder.Append(str); + break; + case Type.STRING: + builder.AppendFormat("\"{0}\"", str); + break; + case Type.NUMBER: +#if USEFLOAT + if(float.IsInfinity(n)) + builder.Append(INFINITY); + else if(float.IsNegativeInfinity(n)) + builder.Append(NEGINFINITY); + else if(float.IsNaN(n)) + builder.Append(NaN); +#else + if(double.IsInfinity(n)) + builder.Append(INFINITY); + else if(double.IsNegativeInfinity(n)) + builder.Append(NEGINFINITY); + else if(double.IsNaN(n)) + builder.Append(NaN); +#endif + else + builder.Append(n.ToString()); + break; + case Type.OBJECT: + builder.Append("{"); + if(list.Count > 0) { +#if(PRETTY) //for a bit more readability, comment the define above to disable system-wide + if(pretty) + builder.Append("\n"); +#endif + for(int i = 0; i < list.Count; i++) { + string key = keys[i]; + JSONObject obj = list[i]; + if(obj) { +#if(PRETTY) + if(pretty) + for(int j = 0; j < depth; j++) + builder.Append("\t"); //for a bit more readability +#endif + builder.AppendFormat("\"{0}\":", key); + foreach(IEnumerable e in obj.StringifyAsync(depth, builder, pretty)) + yield return e; + builder.Append(","); +#if(PRETTY) + if(pretty) + builder.Append("\n"); +#endif + } + } +#if(PRETTY) + if(pretty) + builder.Length -= 2; + else +#endif + builder.Length--; + } +#if(PRETTY) + if(pretty && list.Count > 0) { + builder.Append("\n"); + for(int j = 0; j < depth - 1; j++) + builder.Append("\t"); //for a bit more readability + } +#endif + builder.Append("}"); + break; + case Type.ARRAY: + builder.Append("["); + if(list.Count > 0) { +#if(PRETTY) + if(pretty) + builder.Append("\n"); //for a bit more readability +#endif + for(int i = 0; i < list.Count; i++) { + if(list[i]) { +#if(PRETTY) + if(pretty) + for(int j = 0; j < depth; j++) + builder.Append("\t"); //for a bit more readability +#endif + foreach(IEnumerable e in list[i].StringifyAsync(depth, builder, pretty)) + yield return e; + builder.Append(","); +#if(PRETTY) + if(pretty) + builder.Append("\n"); //for a bit more readability +#endif + } + } +#if(PRETTY) + if(pretty) + builder.Length -= 2; + else +#endif + builder.Length--; + } +#if(PRETTY) + if(pretty && list.Count > 0) { + builder.Append("\n"); + for(int j = 0; j < depth - 1; j++) + builder.Append("\t"); //for a bit more readability + } +#endif + builder.Append("]"); + break; + case Type.BOOL: + if(b) + builder.Append("true"); + else + builder.Append("false"); + break; + case Type.NULL: + builder.Append("null"); + break; + } + //Profiler.EndSample(); + } + //TODO: Refactor Stringify functions to share core logic + /* + * I know, I know, this is really bad form. It turns out that there is a + * significant amount of garbage created when calling as a coroutine, so this + * method is duplicated. Hopefully there won't be too many future changes, but + * I would still like a more elegant way to optionaly yield + */ + // Default parameters fix + void Stringify(int depth, StringBuilder builder){ Stringify(depth, builder, false); } + void Stringify(int depth, StringBuilder builder, bool pretty) { //Convert the JSONObject into a string + //Profiler.BeginSample("JSONprint"); + if(depth++ > MAX_DEPTH) { + Debug.Log("reached max depth!"); + return; + } + switch(type) { + case Type.BAKED: + builder.Append(str); + break; + case Type.STRING: + builder.AppendFormat("\"{0}\"", str); + break; + case Type.NUMBER: +#if USEFLOAT + if(float.IsInfinity(n)) + builder.Append(INFINITY); + else if(float.IsNegativeInfinity(n)) + builder.Append(NEGINFINITY); + else if(float.IsNaN(n)) + builder.Append(NaN); +#else + if(double.IsInfinity(n)) + builder.Append(INFINITY); + else if(double.IsNegativeInfinity(n)) + builder.Append(NEGINFINITY); + else if(double.IsNaN(n)) + builder.Append(NaN); +#endif + else + builder.Append(n.ToString()); + break; + case Type.OBJECT: + builder.Append("{"); + if(list.Count > 0) { +#if(PRETTY) //for a bit more readability, comment the define above to disable system-wide + if(pretty) + builder.Append("\n"); +#endif + for(int i = 0; i < list.Count; i++) { + string key = keys[i]; + JSONObject obj = list[i]; + if(obj) { +#if(PRETTY) + if(pretty) + for(int j = 0; j < depth; j++) + builder.Append("\t"); //for a bit more readability +#endif + builder.AppendFormat("\"{0}\":", key); + obj.Stringify(depth, builder, pretty); + builder.Append(","); +#if(PRETTY) + if(pretty) + builder.Append("\n"); +#endif + } + } +#if(PRETTY) + if(pretty) + builder.Length -= 2; + else +#endif + builder.Length--; + } +#if(PRETTY) + if(pretty && list.Count > 0) { + builder.Append("\n"); + for(int j = 0; j < depth - 1; j++) + builder.Append("\t"); //for a bit more readability + } +#endif + builder.Append("}"); + break; + case Type.ARRAY: + builder.Append("["); + if(list.Count > 0) { +#if(PRETTY) + if(pretty) + builder.Append("\n"); //for a bit more readability +#endif + for(int i = 0; i < list.Count; i++) { + if(list[i]) { +#if(PRETTY) + if(pretty) + for(int j = 0; j < depth; j++) + builder.Append("\t"); //for a bit more readability +#endif + list[i].Stringify(depth, builder, pretty); + builder.Append(","); +#if(PRETTY) + if(pretty) + builder.Append("\n"); //for a bit more readability +#endif + } + } +#if(PRETTY) + if(pretty) + builder.Length -= 2; + else +#endif + builder.Length--; + } +#if(PRETTY) + if(pretty && list.Count > 0) { + builder.Append("\n"); + for(int j = 0; j < depth - 1; j++) + builder.Append("\t"); //for a bit more readability + } +#endif + builder.Append("]"); + break; + case Type.BOOL: + if(b) + builder.Append("true"); + else + builder.Append("false"); + break; + case Type.NULL: + builder.Append("null"); + break; + } + //Profiler.EndSample(); + } + #endregion + public static implicit operator WWWForm(JSONObject obj) { + WWWForm form = new WWWForm(); + for(int i = 0; i < obj.list.Count; i++) { + string key = i + ""; + if(obj.type == Type.OBJECT) + key = obj.keys[i]; + string val = obj.list[i].ToString(); + if(obj.list[i].type == Type.STRING) + val = val.Replace("\"", ""); + form.AddField(key, val); + } + return form; + } + public JSONObject this[int index] { + get { + if(list.Count > index) return list[index]; + return null; + } + set { + if(list.Count > index) + list[index] = value; + } + } + public JSONObject this[string index] { + get { + return GetField(index); + } + set { + SetField(index, value); + } + } + public override string ToString() { + return Print(); + } + public string ToString(bool pretty) { + return Print(pretty); + } + public Dictionary ToDictionary() { + if(type == Type.OBJECT) { + Dictionary result = new Dictionary(); + for(int i = 0; i < list.Count; i++) { + JSONObject val = list[i]; + switch(val.type) { + case Type.STRING: result.Add(keys[i], val.str); break; + case Type.NUMBER: result.Add(keys[i], val.n + ""); break; + case Type.BOOL: result.Add(keys[i], val.b + ""); break; + default: Debug.LogWarning("Omitting object: " + keys[i] + " in dictionary conversion"); break; + } + } + return result; + } + Debug.LogWarning("Tried to turn non-Object JSONObject into a dictionary"); + return null; + } + public static implicit operator bool(JSONObject o) { + return o != null; + } +#if POOLING + static bool pool = true; + public static void ClearPool() { + pool = false; + releaseQueue.Clear(); + pool = true; + } + + ~JSONObject() { + if(pool && releaseQueue.Count < MAX_POOL_SIZE) { + type = Type.NULL; + list = null; + keys = null; + str = ""; + n = 0; + b = false; + releaseQueue.Enqueue(this); + } + } +#endif +} \ No newline at end of file diff --git a/Scripts/SocketIO/JSONObject/JSONObject.cs.meta b/Scripts/SocketIO/JSONObject/JSONObject.cs.meta new file mode 100755 index 0000000..487b431 --- /dev/null +++ b/Scripts/SocketIO/JSONObject/JSONObject.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: faf7fcad305b3684cb778d7cd4dcce17 +timeCreated: 1499897225 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/SocketIO/JSONObject/JSONTemplates.cs b/Scripts/SocketIO/JSONObject/JSONTemplates.cs new file mode 100755 index 0000000..749107d --- /dev/null +++ b/Scripts/SocketIO/JSONObject/JSONTemplates.cs @@ -0,0 +1,88 @@ +using UnityEngine; +using System.Collections.Generic; +using System.Reflection; + +/* + * http://www.opensource.org/licenses/lgpl-2.1.php + * JSONTemplates class + * for use with Unity + * Copyright Matt Schoen 2010 + */ + +public static partial class JSONTemplates { + static readonly HashSet touched = new HashSet(); + + public static JSONObject TOJSON(object obj) { //For a generic guess + if(touched.Add(obj)) { + JSONObject result = JSONObject.obj; + //Fields + FieldInfo[] fieldinfo = obj.GetType().GetFields(); + foreach(FieldInfo fi in fieldinfo) { + JSONObject val = JSONObject.nullJO; + if(!fi.GetValue(obj).Equals(null)) { + MethodInfo info = typeof(JSONTemplates).GetMethod("From" + fi.FieldType.Name); + if(info != null) { + object[] parms = new object[1]; + parms[0] = fi.GetValue(obj); + val = (JSONObject)info.Invoke(null, parms); + } else if(fi.FieldType == typeof(string)) + val = JSONObject.CreateStringObject(fi.GetValue(obj).ToString()); + else + val = JSONObject.Create(fi.GetValue(obj).ToString()); + } + if(val) { + if(val.type != JSONObject.Type.NULL) + result.AddField(fi.Name, val); + else Debug.LogWarning("Null for this non-null object, property " + fi.Name + " of class " + obj.GetType().Name + ". Object type is " + fi.FieldType.Name); + } + } + //Properties + PropertyInfo[] propertyInfo = obj.GetType().GetProperties(); + foreach(PropertyInfo pi in propertyInfo) { + //This section should mirror part of AssetFactory.AddScripts() + JSONObject val = JSONObject.nullJO; + if(!pi.GetValue(obj, null).Equals(null)) { + MethodInfo info = typeof(JSONTemplates).GetMethod("From" + pi.PropertyType.Name); + if(info != null) { + object[] parms = new object[1]; + parms[0] = pi.GetValue(obj, null); + val = (JSONObject)info.Invoke(null, parms); + } else if(pi.PropertyType == typeof(string)) + val = JSONObject.CreateStringObject(pi.GetValue(obj, null).ToString()); + else + val = JSONObject.Create(pi.GetValue(obj, null).ToString()); + } + if(val) { + if(val.type != JSONObject.Type.NULL) + result.AddField(pi.Name, val); + else Debug.LogWarning("Null for this non-null object, property " + pi.Name + " of class " + obj.GetType().Name + ". Object type is " + pi.PropertyType.Name); + } + } + return result; + } + Debug.LogWarning("trying to save the same data twice"); + return JSONObject.nullJO; + } +} + +/* + * Some helpful code templates for the JSON class + * + * LOOP THROUGH OBJECT +for(int i = 0; i < obj.Count; i++){ + if(obj.keys[i] != null){ + switch((string)obj.keys[i]){ + case "key1": + do stuff with (JSONObject)obj.list[i]; + break; + case "key2": + do stuff with (JSONObject)obj.list[i]; + break; + } + } +} + * + * LOOP THROUGH ARRAY +foreach(JSONObject ob in obj.list) + do stuff with ob; + */ diff --git a/Scripts/SocketIO/JSONObject/JSONTemplates.cs.meta b/Scripts/SocketIO/JSONObject/JSONTemplates.cs.meta new file mode 100755 index 0000000..23920da --- /dev/null +++ b/Scripts/SocketIO/JSONObject/JSONTemplates.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 2ca085121cb2619499a2c12ca529bd8f +timeCreated: 1499897223 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/SocketIO/JSONObject/VectorTemplates.cs b/Scripts/SocketIO/JSONObject/VectorTemplates.cs new file mode 100755 index 0000000..7dd7da7 --- /dev/null +++ b/Scripts/SocketIO/JSONObject/VectorTemplates.cs @@ -0,0 +1,237 @@ +using UnityEngine; + +public static partial class JSONTemplates { + + /* + * Vector2 + */ + public static Vector2 ToVector2(JSONObject obj) { + float x = obj["x"] ? obj["x"].f : 0; + float y = obj["y"] ? obj["y"].f : 0; + return new Vector2(x, y); + } + public static JSONObject FromVector2(Vector2 v) { + JSONObject vdata = JSONObject.obj; + if(v.x != 0) vdata.AddField("x", v.x); + if(v.y != 0) vdata.AddField("y", v.y); + return vdata; + } + /* + * Vector3 + */ + public static JSONObject FromVector3(Vector3 v) { + JSONObject vdata = JSONObject.obj; + if(v.x != 0) vdata.AddField("x", v.x); + if(v.y != 0) vdata.AddField("y", v.y); + if(v.z != 0) vdata.AddField("z", v.z); + return vdata; + } + public static Vector3 ToVector3(JSONObject obj) { + float x = obj["x"] ? obj["x"].f : 0; + float y = obj["y"] ? obj["y"].f : 0; + float z = obj["z"] ? obj["z"].f : 0; + return new Vector3(x, y, z); + } + /* + * Vector4 + */ + public static JSONObject FromVector4(Vector4 v) { + JSONObject vdata = JSONObject.obj; + if(v.x != 0) vdata.AddField("x", v.x); + if(v.y != 0) vdata.AddField("y", v.y); + if(v.z != 0) vdata.AddField("z", v.z); + if(v.w != 0) vdata.AddField("w", v.w); + return vdata; + } + public static Vector4 ToVector4(JSONObject obj) { + float x = obj["x"] ? obj["x"].f : 0; + float y = obj["y"] ? obj["y"].f : 0; + float z = obj["z"] ? obj["z"].f : 0; + float w = obj["w"] ? obj["w"].f : 0; + return new Vector4(x, y, z, w); + } + /* + * Matrix4x4 + */ + public static JSONObject FromMatrix4x4(Matrix4x4 m) { + JSONObject mdata = JSONObject.obj; + if(m.m00 != 0) mdata.AddField("m00", m.m00); + if(m.m01 != 0) mdata.AddField("m01", m.m01); + if(m.m02 != 0) mdata.AddField("m02", m.m02); + if(m.m03 != 0) mdata.AddField("m03", m.m03); + if(m.m10 != 0) mdata.AddField("m10", m.m10); + if(m.m11 != 0) mdata.AddField("m11", m.m11); + if(m.m12 != 0) mdata.AddField("m12", m.m12); + if(m.m13 != 0) mdata.AddField("m13", m.m13); + if(m.m20 != 0) mdata.AddField("m20", m.m20); + if(m.m21 != 0) mdata.AddField("m21", m.m21); + if(m.m22 != 0) mdata.AddField("m22", m.m22); + if(m.m23 != 0) mdata.AddField("m23", m.m23); + if(m.m30 != 0) mdata.AddField("m30", m.m30); + if(m.m31 != 0) mdata.AddField("m31", m.m31); + if(m.m32 != 0) mdata.AddField("m32", m.m32); + if(m.m33 != 0) mdata.AddField("m33", m.m33); + return mdata; + } + public static Matrix4x4 ToMatrix4x4(JSONObject obj) { + Matrix4x4 result = new Matrix4x4(); + if(obj["m00"]) result.m00 = obj["m00"].f; + if(obj["m01"]) result.m01 = obj["m01"].f; + if(obj["m02"]) result.m02 = obj["m02"].f; + if(obj["m03"]) result.m03 = obj["m03"].f; + if(obj["m10"]) result.m10 = obj["m10"].f; + if(obj["m11"]) result.m11 = obj["m11"].f; + if(obj["m12"]) result.m12 = obj["m12"].f; + if(obj["m13"]) result.m13 = obj["m13"].f; + if(obj["m20"]) result.m20 = obj["m20"].f; + if(obj["m21"]) result.m21 = obj["m21"].f; + if(obj["m22"]) result.m22 = obj["m22"].f; + if(obj["m23"]) result.m23 = obj["m23"].f; + if(obj["m30"]) result.m30 = obj["m30"].f; + if(obj["m31"]) result.m31 = obj["m31"].f; + if(obj["m32"]) result.m32 = obj["m32"].f; + if(obj["m33"]) result.m33 = obj["m33"].f; + return result; + } + /* + * Quaternion + */ + public static JSONObject FromQuaternion(Quaternion q) { + JSONObject qdata = JSONObject.obj; + if(q.w != 0) qdata.AddField("w", q.w); + if(q.x != 0) qdata.AddField("x", q.x); + if(q.y != 0) qdata.AddField("y", q.y); + if(q.z != 0) qdata.AddField("z", q.z); + return qdata; + } + public static Quaternion ToQuaternion(JSONObject obj) { + float x = obj["x"] ? obj["x"].f : 0; + float y = obj["y"] ? obj["y"].f : 0; + float z = obj["z"] ? obj["z"].f : 0; + float w = obj["w"] ? obj["w"].f : 0; + return new Quaternion(x, y, z, w); + } + /* + * Color + */ + public static JSONObject FromColor(Color c) { + JSONObject cdata = JSONObject.obj; + if(c.r != 0) cdata.AddField("r", c.r); + if(c.g != 0) cdata.AddField("g", c.g); + if(c.b != 0) cdata.AddField("b", c.b); + if(c.a != 0) cdata.AddField("a", c.a); + return cdata; + } + public static Color ToColor(JSONObject obj) { + Color c = new Color(); + for(int i = 0; i < obj.Count; i++) { + switch(obj.keys[i]) { + case "r": c.r = obj[i].f; break; + case "g": c.g = obj[i].f; break; + case "b": c.b = obj[i].f; break; + case "a": c.a = obj[i].f; break; + } + } + return c; + } + /* + * Layer Mask + */ + public static JSONObject FromLayerMask(LayerMask l) { + JSONObject result = JSONObject.obj; + result.AddField("value", l.value); + return result; + } + public static LayerMask ToLayerMask(JSONObject obj) { + LayerMask l = new LayerMask {value = (int)obj["value"].n}; + return l; + } + public static JSONObject FromRect(Rect r) { + JSONObject result = JSONObject.obj; + if(r.x != 0) result.AddField("x", r.x); + if(r.y != 0) result.AddField("y", r.y); + if(r.height != 0) result.AddField("height", r.height); + if(r.width != 0) result.AddField("width", r.width); + return result; + } + public static Rect ToRect(JSONObject obj) { + Rect r = new Rect(); + for(int i = 0; i < obj.Count; i++) { + switch(obj.keys[i]) { + case "x": r.x = obj[i].f; break; + case "y": r.y = obj[i].f; break; + case "height": r.height = obj[i].f; break; + case "width": r.width = obj[i].f; break; + } + } + return r; + } + public static JSONObject FromRectOffset(RectOffset r) { + JSONObject result = JSONObject.obj; + if(r.bottom != 0) result.AddField("bottom", r.bottom); + if(r.left != 0) result.AddField("left", r.left); + if(r.right != 0) result.AddField("right", r.right); + if(r.top != 0) result.AddField("top", r.top); + return result; + } + public static RectOffset ToRectOffset(JSONObject obj) { + RectOffset r = new RectOffset(); + for(int i = 0; i < obj.Count; i++) { + switch(obj.keys[i]) { + case "bottom": r.bottom = (int)obj[i].n; break; + case "left": r.left = (int)obj[i].n; break; + case "right": r.right = (int)obj[i].n; break; + case "top": r.top = (int)obj[i].n; break; + } + } + return r; + } + + public static AnimationCurve ToAnimationCurve(JSONObject obj){ + AnimationCurve a = new AnimationCurve(); + if(obj.HasField("keys")){ + JSONObject keys = obj.GetField("keys"); + for(int i =0; i < keys.list.Count;i++){ + a.AddKey(ToKeyframe(keys[i])); + } + } + if(obj.HasField("preWrapMode")) + a.preWrapMode = (WrapMode)((int)obj.GetField("preWrapMode").n); + if(obj.HasField("postWrapMode")) + a.postWrapMode = (WrapMode)((int)obj.GetField("postWrapMode").n); + return a; + } + + public static JSONObject FromAnimationCurve(AnimationCurve a){ + JSONObject result = JSONObject.obj; + result.AddField("preWrapMode", a.preWrapMode.ToString()); + result.AddField("postWrapMode", a.postWrapMode.ToString()); + if(a.keys.Length > 0){ + JSONObject keysJSON = JSONObject.Create(); + for(int i =0; i < a.keys.Length;i++){ + keysJSON.Add(FromKeyframe(a.keys[i])); + } + result.AddField("keys", keysJSON); + } + return result; + } + + public static Keyframe ToKeyframe(JSONObject obj){ + Keyframe k = new Keyframe((float)(obj.HasField("time")? obj.GetField("time").n : 0), (float)(obj.HasField("value")? obj.GetField("value").n : 0)); + if(obj.HasField("inTangent")) k.inTangent = (float)obj.GetField("inTangent").n; + if(obj.HasField("outTangent")) k.outTangent = (float)obj.GetField("outTangent").n; + //if(obj.HasField("tangentMode")) k.tangentMode = (int)obj.GetField("tangentMode").n; + + return k; + } + public static JSONObject FromKeyframe(Keyframe k){ + JSONObject result = JSONObject.obj; + if(k.inTangent != 0) result.AddField("inTangent", k.inTangent); + if(k.outTangent != 0) result.AddField("outTangent", k.outTangent); + //if(k.tangentMode != 0) result.AddField("tangentMode", k.tangentMode); + if(k.time != 0) result.AddField("time", k.time); + if(k.value != 0) result.AddField("value", k.value); + return result; + } + +} diff --git a/Scripts/SocketIO/JSONObject/VectorTemplates.cs.meta b/Scripts/SocketIO/JSONObject/VectorTemplates.cs.meta new file mode 100755 index 0000000..a621a3a --- /dev/null +++ b/Scripts/SocketIO/JSONObject/VectorTemplates.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: d8720047220c0ad479a7a892f9062052 +timeCreated: 1499897224 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/SocketIO/JSONObject/readme.txt b/Scripts/SocketIO/JSONObject/readme.txt new file mode 100755 index 0000000..b7044d4 --- /dev/null +++ b/Scripts/SocketIO/JSONObject/readme.txt @@ -0,0 +1,188 @@ +==Author== +[mailto:schoen@defectivestudios.com Matt Schoen] of [http://www.defectivestudios.com Defective Studios] + +==Download== +[[Media:JSONObject.zip|Download JSONObject.zip]] + += Intro = +I came across the need to send structured data to and from a server on one of my projects, and figured it would be worth my while to use JSON. When I looked into the issue, I tried a few of the C# implementations listed on [http://json.org json.org], but found them to be too complicated to work with and expand upon. So, I've written a very simple JSONObject class, which can be generically used to encode/decode data into a simple container. This page assumes that you know what JSON is, and how it works. It's rather simple, just go to json.org for a visual description of the encoding format. + +As an aside, this class is pretty central to the [[AssetCloud]] content management system, from Defective Studios. + +Update: The code has been updated to version 1.4 to incorporate user-submitted patches and bug reports. This fixes issues dealing with whitespace in the format, as well as empty arrays and objects, and escaped quotes within strings. + += Usage = +Users should not have to modify the JSONObject class themselves, and must follow the very simple proceedures outlined below: + +Sample data (in JSON format): + +{ + "TestObject": { + "SomeText": "Blah", + "SomeObject": { + "SomeNumber": 42, + "SomeBool": true, + "SomeNull": null + }, + + "SomeEmptyObject": { }, + "SomeEmptyArray": [ ], + "EmbeddedObject": "{\"field\":\"Value with \\\"escaped quotes\\\"\"}" + } +} + += Features = + +*Decode JSON-formatted strings into a usable data structure +*Encode structured data into a JSON-formatted string +*Interoperable with Dictionary and WWWForm +*Optimized parse/stringify functions -- minimal (unavoidable) garbage creation +*Asynchronous stringify function for serializing lots of data without frame drops +*MaxDepth parsing will skip over nested data that you don't need +*Special (non-compliant) "Baked" object type can store stringified data within parsed objects +*Copy to new JSONObject +*Merge with another JSONObject (experimental) +*Random access (with [int] or [string]) +*ToString() returns JSON data with optional "pretty" flag to include newlines and tabs +*Switch between double and float for numeric storage depending on level of precision needed (and to ensure that numbers are parsed/stringified correctly) +*Supports Infinity and NaN values +*JSONTemplates static class provides serialization functions for common classes like Vector3, Matrix4x4 +*Object pool implementation (experimental) +*Handy JSONChecker window to test parsing on sample data + +It should be pretty obvious what this parser can and cannot do. If anyone reading this is a JSON buff (is there such a thing?) please feel free to expand and modify the parser to be more compliant. Currently I am using the .NET System.Convert namespace functions for parsing the data itself. It parses strings and numbers, which was all that I needed of it, but unless the formatting is supported by System.Convert, it may not incorporate all proper JSON strings. Also, having never written a JSON parser before, I don't doubt that I could improve the efficiency or correctness of the parser. It serves my purpose, and hopefully will help you with your project! Let me know if you make any improvements :) + +Also, you JSON buffs (really, who would admit to being a JSON buff...) might also notice from my feature list that this thing isn't exactly to specifications. Here is where it differs: +*"a string" is considered valid JSON. There is an optional "strict" parameter to the parser which will bomb out on such input, in case that matters to you. +*The "Baked" mode is totally made up. +*The "MaxDepth" parsing is totally made up. +*NaN and Infinity aren't officially supported by JSON ([http://stackoverflow.com/questions/1423081/json-left-out-infinity-and-nan-json-status-in-ecmascript read more] about this issue... I lol'd @ the first comment on the first answer) +*I have no idea about edge cases in my parsing strategy. I have been using this code for about 3 years now and have only had to modify the parser because other people's use cases (still valid JSON) didn't parse correctly. In my experience, anything that this code generates is parsed just fine. + +== Encoding == + +Encoding is something of a hard-coded process. This is because I have no idea what your data is! It would be great if this were some sort of interface for taking an entire class and encoding it's number/string fields, but it's not. I've come up with a few clever ways of using loops and/or recursive methods to cut down of the amount of code I have to write when I use this tool, but they're pretty project-specific. + +Note: This section used to be WRONG! And now it's OLD! Will update later... this will all still work, but there are now a couple of ways to skin this cat. + + +//Note: your data can only be numbers and strings. This is not a solution for object serialization or anything like that. +JSONObject j = new JSONObject(JSONObject.Type.OBJECT); +//number +j.AddField("field1", 0.5); +//string +j.AddField("field2", "sampletext"); +//array +JSONObject arr = new JSONObject(JSONObject.Type.ARRAY); +j.AddField("field3", arr); + +arr.Add(1); +arr.Add(2); +arr.Add(3); + +string encodedString = j.print(); + + +NEW! The constructor, Add, and AddField functions now support a nested delegate structure. This is useful if you need to create a nested JSONObject in a single line. For example: + +DoRequest(URL, new JSONObject(delegate(JSONObject request) { + request.AddField("sort", delegate(JSONObject sort) { + sort.AddField("_timestamp", "desc"); + }); + request.AddField("query", new JSONObject(delegate(JSONObject query) { + query.AddField("match_all", JSONObject.obj); + })); + request.AddField("fields", delegate(JSONObject fields) { + fields.Add("_timestamp"); + }); +}).ToString()); + + +== Decoding == +Decoding is much simpler on the input end, and again, what you do with the JSONObject will vary on a per-project basis. One of the more complicated way to extract the data is with a recursive function, as drafted below. Calling the constructor with a properly formatted JSON string will return the root object (or array) containing all of its children, in one neat reference! The data is in a public ArrayList called list, with a matching key list (called keys!) if the root is an Object. If that's confusing, take a glance over the following code and the print() method in the JSONOBject class. If there is an error in the JSON formatting (or if there's an error with my code!) the debug console will read "improper JSON formatting". + + + +string encodedString = "{\"field1\": 0.5,\"field2\": \"sampletext\",\"field3\": [1,2,3]}"; +JSONObject j = new JSONObject(encodedString); +accessData(j); +//access data (and print it) +void accessData(JSONObject obj){ + switch(obj.type){ + case JSONObject.Type.OBJECT: + for(int i = 0; i < obj.list.Count; i++){ + string key = (string)obj.keys[i]; + JSONObject j = (JSONObject)obj.list[i]; + Debug.Log(key); + accessData(j); + } + break; + case JSONObject.Type.ARRAY: + foreach(JSONObject j in obj.list){ + accessData(j); + } + break; + case JSONObject.Type.STRING: + Debug.Log(obj.str); + break; + case JSONObject.Type.NUMBER: + Debug.Log(obj.n); + break; + case JSONObject.Type.BOOL: + Debug.Log(obj.b); + break; + case JSONObject.Type.NULL: + Debug.Log("NULL"); + break; + + } +} + + +NEW! Decoding now also supports a delegate format which will automatically check if a field exists before processing the data, providing an optional parameter for an OnFieldNotFound response. For example: + +new JSONObject(data); +list.GetField("hits", delegate(JSONObject hits) { + hits.GetField("hits", delegate(JSONObject hits2) { + foreach (JSONObject gameSession in hits2.list) { + Debug.Log(gameSession); + } + }); +}, delegate(string name) { //"name" will be equal to the name of the missing field. In this case, "hits" + Debug.LogWarning("no game sessions"); +}); + + +===Not So New! (O(n)) Random access!=== +I've added a string and int [] index to the class, so you can now retrieve data as such (from above): + +JSONObject arr = obj["field3"]; +Debug.log(arr[2].n); //Should ouptut "3" + + +---- + +---Code omitted from readme--- + +=Change Log= +==v1.4== +Big update! +*Better GC performance. Enough of that garbage! +**Remaining culprits are internal garbage from StringBuilder.Append/AppendFormat, String.Substring, List.Add/GrowIfNeeded, Single.ToString +*Added asynchronous Stringify function for serializing large amounts of data at runtime without frame drops +*Added Baked type +*Added MaxDepth to parsing function +*Various cleanup refactors recommended by ReSharper + +==v1.3.2== +*Added support for NaN +*Added strict mode to fail on purpose for improper formatting. Right now this just means that if the parse string doesn't start with [ or {, it will print a warning and return a null JO. +*Changed infinity and NaN implementation to use float and double instead of Mathf +*Handles empty objects/arrays better +*Added a flag to print and ToString to turn on/off pretty print. The define on top is now an override to system-wide disable +==Earlier Versions== +I'll fill these in later... +[[Category:C Sharp]] +[[Category:Scripts]] +[[Category:Utility]] +[[Category:JSON]] diff --git a/Scripts/SocketIO/JSONObject/readme.txt.meta b/Scripts/SocketIO/JSONObject/readme.txt.meta new file mode 100755 index 0000000..18a34ec --- /dev/null +++ b/Scripts/SocketIO/JSONObject/readme.txt.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7a92bad5dfc0fc942b67e3b48062dbc4 +timeCreated: 1499897225 +licenseType: Free +TextScriptImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/ThrottleManager.cs b/Scripts/ThrottleManager.cs new file mode 100755 index 0000000..b9972e3 --- /dev/null +++ b/Scripts/ThrottleManager.cs @@ -0,0 +1,71 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; + +public class ThrottleManager : MonoBehaviour { + + public GameObject carObj; + public ICar car; + + public bool doControlThrottle = true; + + public Text speedometerUI; + public Text speedFactorUI; + + public float idealSpeed = 15f; + public float speedFactor = 1.0f; + public float brakeThresh = 200.0f; + + public float constThrottleReq = 0.5f; + public float brakePerc = 0.0001f; + + public float turnSlowFactor = 3.0f; + + //give the network time to connect before turning on the throttle. + public float delayBeforeStart = 2.0f; + + void Awake() + { + car = carObj.GetComponent(); + } + + // Update is called once per frame + void Update () + { + if(delayBeforeStart > 0.0f) + { + delayBeforeStart -= Time.deltaTime; + return; + } + + speedFactor = car.GetVelocity().magnitude * (1.0f + Mathf.Abs(car.GetSteering())); + + if(speedometerUI != null) + speedometerUI.text = car.GetVelocity().magnitude.ToString(); + + if(speedFactorUI != null) + speedFactorUI.text = speedFactor.ToString(); + + float idealSpeedAdjusted = idealSpeed - (turnSlowFactor * Mathf.Abs(car.GetSteering())); + + if(doControlThrottle) + { + if(speedFactor > brakeThresh) + { + car.RequestThrottle(0.0f); + car.RequestFootBrake(1.0f); + } + else if(car.GetVelocity().magnitude < idealSpeedAdjusted) + { + car.RequestFootBrake(0.0f); + car.RequestThrottle(constThrottleReq); + } + else + { + car.RequestThrottle(0.0f); + car.RequestFootBrake(0.0f); + } + } + } +} diff --git a/Scripts/ThrottleManager.cs.meta b/Scripts/ThrottleManager.cs.meta new file mode 100755 index 0000000..c33ebc6 --- /dev/null +++ b/Scripts/ThrottleManager.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 3e84602586f6b497ba1429e7cbbcbb22 +timeCreated: 1484016062 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Timer.cs b/Scripts/Timer.cs new file mode 100755 index 0000000..8d4df54 --- /dev/null +++ b/Scripts/Timer.cs @@ -0,0 +1,104 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class Timer : MonoBehaviour +{ + public TextMesh currentTotTimeDisp; + public TextMesh penaltiesDisp; + public bool enabled_timer = false; + public string racerName; + public float penalties = 0.0f; //seconds + public float currentStart = 0.0f; //seconds + bool freezed = false; + + void Awake() + { + if(enabled_timer) + { + StartTimer(); + } + else + { + DisableTimer(); + } + } + + public void StartTimer() + { + currentTotTimeDisp.gameObject.SetActive(true); + penaltiesDisp.gameObject.SetActive(true); + penalties = 0.0f; + currentStart = GetTime(); + enabled_timer = true; + } + + public void DisableTimer() + { + currentTotTimeDisp.gameObject.SetActive(false); + penaltiesDisp.gameObject.SetActive(false); + penalties = 0.0f; + currentStart = 0.0f; + enabled_timer = false; + freezed = false; + } + + public void ResetTimer() + { + penalties = 0.0f; + currentStart = GetTime(); + enabled_timer = true; + } + public void SplitTime() + { + freezed = true; + } + public void ContinueTime() + { + freezed = false; + } + + + float GetTime() + { + return Time.time; + } + float GetPenalties() + { + return penalties; + } + float GetCurrentTime() + { + return (GetTime() - currentStart) + GetPenalties(); + } + + public void OnCollideChallenge(float penalty) + { + if(enabled_timer == false) + return; + + penalties += penalty; + if(penaltiesDisp.gameObject.activeSelf){ + float penalties = GetPenalties(); + penaltiesDisp.text = penalties.ToString("00.00"); + Debug.Log("Added penalty"); + } + } + + // Update is called once per frame + void Update() + { + if(enabled_timer == false) + return; + + if(currentTotTimeDisp.gameObject.activeSelf & !freezed) + { + float currentTime = GetCurrentTime(); + currentTotTimeDisp.text = currentTime.ToString("00.00"); + } + if(penaltiesDisp.text != penalties.ToString("00.00") & !freezed) + { + penaltiesDisp.text = penalties.ToString("00.00"); + } + } +} diff --git a/Scripts/Timer.cs.meta b/Scripts/Timer.cs.meta new file mode 100755 index 0000000..b55e90d --- /dev/null +++ b/Scripts/Timer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 627d024375eb4174fb875493cd226904 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/TrackScript.cs b/Scripts/TrackScript.cs new file mode 100755 index 0000000..3d3f561 --- /dev/null +++ b/Scripts/TrackScript.cs @@ -0,0 +1,217 @@ +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using UnityEngine; +using System.Text; + +public class TrackParams +{ + public enum State + { + Straight, + CurveX, + CurveY, + CurveZ, + SpeedLimit, + AngleDX, //when changing the dx curve setting + AngleDY, //when changing the dy curve setting + AngleDZ, //when changing the dz curve setting + ForkA, //when laying a track element that splits in two. + ForkB, //when starting the second line that splits from the fork + MergeA, //when we should look for the nearest track to merge from a fork + MergeB, //when we should look for the nearest track to merge from a fork + End //terminate current line + } + + public State state; + public int numToSet; + public Quaternion rotCur; + public Quaternion dRot; + public Vector3 lastPos; +} + +[System.Serializable] +public class TrackScriptElem +{ + public TrackParams.State state; + public int numToSet; + public float value; + + public TrackScriptElem(TrackParams.State s = TrackParams.State.Straight, float si = 1.0f, int num = 1) + { + state = s; + numToSet = num; + value = si; + } +} + +public class TrackScript +{ + public List track; + + public void Build(TrackScriptElem el) + { + if(track.Count == 0) + { + track.Add(el); + } + else + { + TrackScriptElem lastElem = track[track.Count - 1]; + + if(lastElem.state == el.state && lastElem.value == el.value) + { + lastElem.numToSet += 1; + } + else + { + track.Add(el); + } + } + } + + public bool Write(string filename) + { + StringBuilder sb = new StringBuilder(); + + System.IO.File.WriteAllText(filename, sb.ToString()); + + return true; + } + + public bool Read(string filename) + { + track = new List(); + + Debug.Log("loading: " + filename); + + TextAsset bindata = Resources.Load("Track/"+filename) as TextAsset; + + if(bindata == null) + return false; + + string[] lines = bindata.text.Split('\n'); + + foreach(string line in lines) + { + string[] tokens = line.Split(' '); + + if (tokens.Length < 2) + continue; + + string command = tokens[0]; + string args = tokens[1]; + + if (command.StartsWith("//")) + continue; + + TrackScriptElem tse = new TrackScriptElem(); + + if (command == "U") + { + tse.state = TrackParams.State.CurveZ; + tse.value = 1f; + tse.numToSet = int.Parse(args); + } + else if(command == "S") + { + tse.state = TrackParams.State.Straight; + tse.value = 1f; + tse.numToSet = int.Parse(args); + } + else if (command == "D") + { + tse.state = TrackParams.State.CurveZ; + tse.value = -1f; + tse.numToSet = int.Parse(args); + } + else if (command == "L") + { + tse.state = TrackParams.State.CurveY; + tse.value = -1f; + tse.numToSet = int.Parse(args); + } + else if (command == "R") + { + tse.state = TrackParams.State.CurveY; + tse.value = 1f; + tse.numToSet = int.Parse(args); + } + else if (command == "RL") + { + tse.state = TrackParams.State.CurveX; + tse.value = 1f; + tse.numToSet = int.Parse(args); + } + else if (command == "RR") + { + tse.state = TrackParams.State.CurveX; + tse.value = -1f; + tse.numToSet = int.Parse(args); + } + else if (command == "SPEED_LIMIT") + { + tse.state = TrackParams.State.SpeedLimit; + tse.value = float.Parse(args, CultureInfo.InvariantCulture.NumberFormat); + tse.numToSet = 0; + } + else if (command == "DX") + { + tse.state = TrackParams.State.AngleDX; + tse.value = float.Parse(args, CultureInfo.InvariantCulture.NumberFormat); + tse.numToSet = 0; + } + else if (command == "DY") + { + tse.state = TrackParams.State.AngleDY; + tse.value = float.Parse(args, CultureInfo.InvariantCulture.NumberFormat); + tse.numToSet = 0; + } + else if (command == "DZ") + { + tse.state = TrackParams.State.AngleDZ; + tse.value = float.Parse(args, CultureInfo.InvariantCulture.NumberFormat); + tse.numToSet = 0; + } + else if (command == "FORK_A") + { + tse.state = TrackParams.State.ForkA; + tse.value = float.Parse(args, CultureInfo.InvariantCulture.NumberFormat); + tse.numToSet = 0; + } + else if (command == "FORK_B") + { + tse.state = TrackParams.State.ForkB; + tse.value = float.Parse(args, CultureInfo.InvariantCulture.NumberFormat); + tse.numToSet = 0; + } + else if (command == "MERGE_A") + { + tse.state = TrackParams.State.MergeA; + tse.value = float.Parse(args, CultureInfo.InvariantCulture.NumberFormat); + tse.numToSet = 0; + } + else if (command == "MERGE_B") + { + tse.state = TrackParams.State.MergeB; + tse.value = float.Parse(args, CultureInfo.InvariantCulture.NumberFormat); + tse.numToSet = 0; + } + else if (command == "END") + { + tse.state = TrackParams.State.End; + tse.value = float.Parse(args, CultureInfo.InvariantCulture.NumberFormat); + tse.numToSet = 0; + } + else + { + Debug.Log("unknown command: " + command); + continue; + } + + track.Add(tse); + } + + return track.Count > 0; + } +} \ No newline at end of file diff --git a/Scripts/TrackScript.cs.meta b/Scripts/TrackScript.cs.meta new file mode 100755 index 0000000..9236981 --- /dev/null +++ b/Scripts/TrackScript.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 32a9880970ec34508b0093f9033599e1 +timeCreated: 1484087050 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/TrainingManager.cs b/Scripts/TrainingManager.cs new file mode 100755 index 0000000..c18ec4a --- /dev/null +++ b/Scripts/TrainingManager.cs @@ -0,0 +1,134 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class TrainingManager : MonoBehaviour +{ + + public PIDController controller; + public GameObject carObj; + public ICar car; + public Logger logger; + public RoadBuilder roadBuilder; + + public Camera overheadCamera; + + public PathManager pathManager; + public CarSpawner carSpawner; + + public int numTrainingRuns = 1; + int iRun = 0; + + + void LinkObj() + { + car = carObj.GetComponent(); + if (car == null) + Debug.LogError("TrainingManager needs car object"); + + roadBuilder = GameObject.FindObjectOfType(); + pathManager = GameObject.FindObjectOfType(); + carSpawner = GameObject.FindObjectOfType(); + } + + // Use this for initialization + void Start() + { + LinkObj(); + controller.endOfPathCB += new PIDController.OnEndOfPathCB(OnPathDone); + } + + public void SetRoadStyle(int style) + { + iRun = style; + } + + void SwapRoadToNewTextureVariation() + { + if (roadBuilder == null) + return; + + roadBuilder.SetNewRoadVariation(iRun); + } + + void StartNewRun() + { + car.RestorePosRot(); + roadBuilder.DestroyRoad(); + SwapRoadToNewTextureVariation(); + pathManager.InitCarPath(); + controller.StartDriving(); + RepositionOverheadCamera(); + } + + public void RepositionOverheadCamera() + { + if (carSpawner == null) + return; + + if (GlobalState.overheadCamera) + { + GameObject OHCamGo = carSpawner.cameras[0]; + OverHeadCamera overheadCamera = OHCamGo.GetComponent(); + overheadCamera.Init(); + } + } + + + void OnLastRunCompleted() + { + car.RequestFootBrake(1.0f); + controller.StopDriving(); + logger.Shutdown(); + } + + public void OnMenuNextTrack() + { + iRun += 1; + + if (iRun >= numTrainingRuns) + iRun = 0; + + StartNewRun(); + car.RequestFootBrake(1); + } + + public void OnMenuRegenTrack() + { + StartNewRun(); + car.RequestFootBrake(1); + } + + void OnPathDone() + { + iRun += 1; + + if (iRun >= numTrainingRuns) + { + OnLastRunCompleted(); + } + else + { + StartNewRun(); + } + } + + void Update() + { + if (car == null) + return; + + //watch the car and if we fall off the road, reset things. + if (car.GetTransform().position.y < -1.0f) + { + OnPathDone(); + } + + if (logger.frameCounter + 1 % 1000 == 0) + { + //swap road texture left to right. or Y + roadBuilder.NegateYTiling(); + } + } + +} diff --git a/Scripts/TrainingManager.cs.meta b/Scripts/TrainingManager.cs.meta new file mode 100755 index 0000000..59e354c --- /dev/null +++ b/Scripts/TrainingManager.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: e23e5c302586a4fa68977f49c9010cf6 +timeCreated: 1482955047 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/UnityStandardCarAdapter.cs b/Scripts/UnityStandardCarAdapter.cs new file mode 100755 index 0000000..b5bf3ab --- /dev/null +++ b/Scripts/UnityStandardCarAdapter.cs @@ -0,0 +1,157 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityStandardAssets.CrossPlatformInput; + +public class UnityStandardCarAdapter : MonoBehaviour, ICar { + + public UnityStandardAssets.Vehicles.Car.CarController unityCar; + public float MaximumSteerAngle = 25.0f; //has to be kept in sync with the car, as that's a private var. + float steering = 0.0f; + float throttle = 0.0f; + float footBrake = 0.0f; + float handBrake = 0.0f; + Vector3 vel = Vector3.zero; + Vector3 accel = Vector3.zero; + Quaternion rotation = Quaternion.identity; + Vector3 gyro = Vector3.zero; + public string activity = "keep_lane"; + + Rigidbody rb; + + public Vector3 startPos; + public Quaternion startRot; + + void Awake() + { + rb = unityCar.GetComponent(); + SavePosRot(); + } + + //all inputs require 0-1 input except steering which is in degrees, where 0 is center. + public void RequestThrottle(float val) { throttle = val; } + + public void RequestSteering(float val) { steering = val; } + + public void RequestFootBrake(float val) { footBrake = val; } + + public void RequestHandBrake(float val) { handBrake = val; } + + + //query last input given. + public float GetSteering() { return steering; } + + public float GetThrottle() { return throttle; } + + public float GetFootBrake() { return footBrake; } + + public float GetHandBrake() { return handBrake; } + + + //query state. + public Transform GetTransform() { return this.transform; } + + public Vector3 GetVelocity() + { + return rb.velocity; + } + + public Vector3 GetAccel() { return accel; } + public Vector3 GetGyro() { return gyro; } + public void SetMaxSteering(float val) + { + MaximumSteerAngle = val; + } + + public float GetMaxSteering() + { + return MaximumSteerAngle; + } + + //Save and restore State + public void SavePosRot() + { + startPos = transform.position; + startRot = transform.rotation; + } + + public void RestorePosRot() + { + Set(startPos, startRot); + } + + public void Set(Vector3 pos, Quaternion rot) + { + rb.position = pos; + rb.rotation = rot; + + //just setting it once doesn't seem to work. Try setting it multiple times.. + StartCoroutine(KeepSetting(pos, rot, 10)); + } + + IEnumerator KeepSetting(Vector3 pos, Quaternion rot, int numIter) + { + while(numIter > 0) + { + rb.position = pos; + rb.rotation = rot; + transform.position = pos; + transform.rotation = rot; + rb.velocity = Vector3.zero; + rb.angularVelocity = Vector3.zero; + + numIter--; + yield return new WaitForFixedUpdate(); + } + } + + private void FixedUpdate() + { + accel = rb.velocity - vel; + vel = rb.velocity; + + unityCar.Move(steering / MaximumSteerAngle, throttle, footBrake, handBrake); + gyro = rb.angularVelocity; + rotation = rb.rotation; + } + + public string GetActivity() + { + return activity; + } + + public void SetActivity(string act) + { + activity = act; + } + + //get the name of the last object we collided with + public string GetLastCollision() + { + return last_collision; + } + + public void ClearLastCollision() + { + if (Time.time - last_collision_time > collision_hold_seconds) + { + last_collision = "none"; + } + } + + void OnCollisionEnter(Collision col) + { + last_collision = col.gameObject.name; + last_collision_time = Time.time; + } + + void OnCollisionStay(Collision col) + { + last_collision = col.gameObject.name; + last_collision_time = Time.time; + } + + string last_collision = "none"; + float last_collision_time = -999f; + const float collision_hold_seconds = 0.75f; +} diff --git a/Scripts/UnityStandardCarAdapter.cs.meta b/Scripts/UnityStandardCarAdapter.cs.meta new file mode 100755 index 0000000..ec1e4de --- /dev/null +++ b/Scripts/UnityStandardCarAdapter.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 28325c40c7dc04ee98b3b136abf1c3e6 +timeCreated: 1484597963 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/VersionCheck.cs b/Scripts/VersionCheck.cs new file mode 100755 index 0000000..b8fb389 --- /dev/null +++ b/Scripts/VersionCheck.cs @@ -0,0 +1,36 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine.Networking; +using UnityEngine; + +public class VersionCheck : MonoBehaviour +{ + + private string uri = "https://github.com/tawnkramer/gym-donkeycar/releases/latest"; + public string latest; + + void Awake() + { + StartCoroutine(GetRequest(uri)); + } + + + IEnumerator GetRequest(string _uri) + { + using (UnityWebRequest webRequest = UnityWebRequest.Get(_uri)) + { + yield return webRequest.SendWebRequest(); + uri = webRequest.uri.ToString(); + string[] split = uri.Split('/'); + latest = split[split.Length - 1]; + // Debug.Log(latest); + } + } + + + public void GetLatestVersion() + { + // open the URL in a web browser + Application.OpenURL(uri); + } +} diff --git a/Scripts/VersionCheck.cs.meta b/Scripts/VersionCheck.cs.meta new file mode 100755 index 0000000..6c83cf9 --- /dev/null +++ b/Scripts/VersionCheck.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 00af98866b2653642870117c27793fa2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/WheelPhys.cs b/Scripts/WheelPhys.cs new file mode 100755 index 0000000..cc204c0 --- /dev/null +++ b/Scripts/WheelPhys.cs @@ -0,0 +1,32 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class WheelPhys : MonoBehaviour +{ + WheelCollider wc; + float originalForwardStiffness; + float originalSidewaysStiffness; + + void Awake() + { + wc = gameObject.GetComponent(); + originalForwardStiffness = wc.forwardFriction.stiffness; + originalSidewaysStiffness = wc.sidewaysFriction.stiffness; + } + + void FixedUpdate() + { + WheelHit hit; + if (wc.GetGroundHit(out hit)) + { + WheelFrictionCurve fFriction = wc.forwardFriction; + fFriction.stiffness = hit.collider.material.staticFriction * originalForwardStiffness; + wc.forwardFriction = fFriction; + + WheelFrictionCurve sFriction = wc.sidewaysFriction; + sFriction.stiffness = hit.collider.material.staticFriction * originalSidewaysStiffness; + wc.sidewaysFriction = sFriction; + } + } +} diff --git a/Scripts/WheelPhys.cs.meta b/Scripts/WheelPhys.cs.meta new file mode 100755 index 0000000..9f65a5b --- /dev/null +++ b/Scripts/WheelPhys.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0620291b95526354dbb90fd1081e2937 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/camera.meta b/Scripts/camera.meta new file mode 100755 index 0000000..8aa20e9 --- /dev/null +++ b/Scripts/camera.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4270536b35ddfec42ae53529e41c8834 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/camera/CameraFollow.cs b/Scripts/camera/CameraFollow.cs new file mode 100755 index 0000000..f1c3b60 --- /dev/null +++ b/Scripts/camera/CameraFollow.cs @@ -0,0 +1,21 @@ +using UnityEngine; +using System.Collections; + +public class CameraFollow : MonoBehaviour +{ + + public Transform target; + + public float approachPosRate = 0.1f; + public float approachRotRate = 0.05f; + + void FixedUpdate() + { + if (target != null) + { + float fixedDeltaTimeRate = (Time.fixedDeltaTime / 0.02f); + transform.position = Vector3.Lerp(transform.position, target.position, approachPosRate * fixedDeltaTimeRate); + transform.rotation = Quaternion.Lerp(transform.rotation, target.rotation, approachRotRate * fixedDeltaTimeRate); + } + } +} diff --git a/Scripts/camera/CameraFollow.cs.meta b/Scripts/camera/CameraFollow.cs.meta new file mode 100755 index 0000000..c1cfc88 --- /dev/null +++ b/Scripts/camera/CameraFollow.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: c9645d1b9795a4eaa8ee90d635adbb16 +timeCreated: 1448833810 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/camera/CameraHelper.cs b/Scripts/camera/CameraHelper.cs new file mode 100755 index 0000000..f073a4f --- /dev/null +++ b/Scripts/camera/CameraHelper.cs @@ -0,0 +1 @@ +using UnityEngine; public static class CameraHelper { public static byte[] CaptureFrame(Camera camera) { RenderTexture targetTexture = camera.targetTexture; RenderTexture.active = targetTexture; Texture2D texture2D = new Texture2D(targetTexture.width, targetTexture.height, TextureFormat.RGB24, false); texture2D.ReadPixels(new Rect(0, 0, targetTexture.width, targetTexture.height), 0, 0); texture2D.Apply(); byte[] image = texture2D.EncodeToJPG(); //byte[] image = texture2D.EncodeToPNG(); //byte[] image = texture2D.GetRawTextureData(); Object.DestroyImmediate(texture2D); // Required to prevent leaking the texture return image; } } \ No newline at end of file diff --git a/Scripts/camera/CameraHelper.cs.meta b/Scripts/camera/CameraHelper.cs.meta new file mode 100755 index 0000000..5ba8d1c --- /dev/null +++ b/Scripts/camera/CameraHelper.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 12ae52b5c0b3dd94190b3032b691be15 +timeCreated: 1499903536 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/camera/CameraSensor.cs b/Scripts/camera/CameraSensor.cs new file mode 100755 index 0000000..b00045f --- /dev/null +++ b/Scripts/camera/CameraSensor.cs @@ -0,0 +1,95 @@ +using System.Collections; +using System.Collections.Generic; +using Unity.Collections; +using UnityEngine; + +public class CameraSensor : MonoBehaviour { + + public Camera sensorCam; + public int width = 256; + public int height = 256; + public int depth = 3; + public string img_enc = "JPG"; //accepts JPG, PNG, TGA + Texture2D tex; + RenderTexture ren; + Rect ImageRect; + + public void SetConfig(float fov, float offset_x, float offset_y, float offset_z, float rot_x, float rot_y, float rot_z, int img_w, int img_h, int img_d, string _img_enc) + { + if (img_d != 0) + { + depth = img_d; + } + + if (img_w != 0 && img_h != 0) + { + width = img_w; + height = img_h; + + Awake(); + } + + if(_img_enc.Length == 3) + img_enc = _img_enc; + + if(offset_x != 0.0f || offset_y != 0.0f || offset_z != 0.0f) + transform.localPosition = new Vector3(offset_x, offset_y, offset_z); + + if (rot_x != 0.0f || rot_y != 0.0f || rot_z != 0.0f) + { + transform.localEulerAngles = new Vector3(rot_x, rot_y, rot_z); + } + + if(fov != 0.0f) + sensorCam.fieldOfView = fov; + } + + void Awake() + { + tex = new Texture2D(width, height, TextureFormat.RGB24, false); + ren = new RenderTexture(width, height, 16, RenderTextureFormat.ARGB32); + sensorCam.targetTexture = ren; + } + + Texture2D RTImage() + { + var currentRT = RenderTexture.active; + RenderTexture.active = sensorCam.targetTexture; + + sensorCam.Render(); + + tex.ReadPixels(new Rect(0, 0, width, height), 0, 0); + + if(depth == 1) + { + //assumes TextureFormat.RGB24. Convert to grey scale image + NativeArray bytes = tex.GetRawTextureData(); + for (int i=0; i(); + boxCollider.isTrigger = true; + } + + public void setBoxCollider(Vector3 position, Quaternion rotation, Vector3 scale) + { + boxCollider.transform.position = position; + boxCollider.transform.localScale = scale; + boxCollider.transform.rotation = rotation; + } + + void OnTriggerEnter(Collider col) + { + if (col.gameObject.name != target) { return; } + if (raceCamera == null) { raceCamera = transform.GetComponentInParent(); } + raceCamera.CameraTriggered(col); + } +} \ No newline at end of file diff --git a/Scripts/camera/CameraTrigger.cs.meta b/Scripts/camera/CameraTrigger.cs.meta new file mode 100755 index 0000000..07ef679 --- /dev/null +++ b/Scripts/camera/CameraTrigger.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 647b45fe820c60146a25f9c73557fef5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/camera/OverHeadCamera.cs b/Scripts/camera/OverHeadCamera.cs new file mode 100755 index 0000000..aaf0c6c --- /dev/null +++ b/Scripts/camera/OverHeadCamera.cs @@ -0,0 +1,90 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class OverHeadCamera : MonoBehaviour +{ + public PathManager pathManager; + public float margin = 5; + public float height = 10; + Camera cam; + + float previousHeight = 0; + float previousWidth = 0; + + public void Init() + { + if (pathManager == null) { return; } + if (pathManager.carPath == null) { return; } + + cam = GetComponent(); + if (cam == null) { return; } + + cam.orthographic = true; // make sure the camera is orthographic + + + List points = new List(); + float xmin = float.MaxValue; + float xmax = float.MinValue; + float zmin = float.MaxValue; + float zmax = float.MinValue; + + Vector3 vxmin = new Vector3(); + Vector3 vzmin = new Vector3(); + + foreach (PathNode centerNode in pathManager.carPath.centerNodes) + { + Vector3 pos = centerNode.pos; + points.Add(pos); + if (pos.x < xmin) + { + xmin = pos.x; + vxmin = pos; + } + if (pos.z < zmin) + { + zmin = pos.z; + vzmin = pos; + } + if (pos.x > xmax) + { + xmax = pos.x; + } + if (pos.z > zmax) + { + zmax = pos.z; + } + } + + // place the camera in the center of the circuit + Vector3 center = new Vector3((xmin + xmax) / 2.0f, 0, (zmin + zmax) / 2.0f); + transform.position = center + Vector3.up * height; + transform.LookAt(center, Vector3.up); + + // try to best fit the camera to the screen + Vector3 vpxmin = cam.WorldToViewportPoint(vxmin); + Vector3 vpzmin = cam.WorldToViewportPoint(vzmin); + + float xdistanceToBorder = vpxmin.x; + float zdistanceToBorder = vpzmin.y; + + float zoomValue = 1 - Mathf.Min(xdistanceToBorder, zdistanceToBorder) * 2; + cam.orthographicSize = cam.orthographicSize * zoomValue + margin; + } + + public void Update() + { + float currentHeight = Display.main.renderingHeight; + float currentWidth = Display.main.renderingWidth; + + // if the resolution of the window changed, re-Init the camera + if (currentHeight != previousHeight || currentWidth != previousWidth) + { + previousHeight = currentHeight; + previousWidth = currentWidth; + + Init(); + } + } + +} diff --git a/Scripts/camera/OverHeadCamera.cs.meta b/Scripts/camera/OverHeadCamera.cs.meta new file mode 100755 index 0000000..bf7e85f --- /dev/null +++ b/Scripts/camera/OverHeadCamera.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bb1d44f16bbb15642b7f8df35aadaad1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/camera/RaceCamera.cs b/Scripts/camera/RaceCamera.cs new file mode 100755 index 0000000..4f61b41 --- /dev/null +++ b/Scripts/camera/RaceCamera.cs @@ -0,0 +1,42 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class RaceCamera : MonoBehaviour +{ + RaceCameras raceCameras; + public CameraTrigger cameraTrigger; + public Camera camera; + public int index; + + void Awake() + { + GameObject goCamChild = new GameObject(string.Format("Camera")); + goCamChild.transform.SetParent(transform); + camera = goCamChild.AddComponent(); + camera.enabled = false; + camera.fieldOfView = 90; + + GameObject goTriggerChild = new GameObject(string.Format("Cam Trigger")); + goTriggerChild.transform.SetParent(transform); + cameraTrigger = goTriggerChild.AddComponent(); + + raceCameras = transform.GetComponentInParent(); + } + + public void SetCameraTrigger(Vector3 position, Quaternion rotation, Vector3 scale) + { + cameraTrigger.setBoxCollider(position, rotation, scale); + } + public void SetCam(Vector3 position, Vector3 lookAt) + { + camera.transform.position = position; + camera.transform.rotation = Quaternion.LookRotation(lookAt - position, Vector3.up); + } + + public void CameraTriggered(Collider col) + { + raceCameras.CameraTriggered(col, camera, index); + } + +} diff --git a/Scripts/camera/RaceCamera.cs.meta b/Scripts/camera/RaceCamera.cs.meta new file mode 100755 index 0000000..0188ea9 --- /dev/null +++ b/Scripts/camera/RaceCamera.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 930287bc4c7b01e40b5edb33ece8a10c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/camera/RaceCameras.cs b/Scripts/camera/RaceCameras.cs new file mode 100755 index 0000000..b1ab277 --- /dev/null +++ b/Scripts/camera/RaceCameras.cs @@ -0,0 +1,193 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class RaceCameras : MonoBehaviour, IWaitCarPath +{ + public PathManager pathManager; + List raceCameras = new List(); + List nodeIndexes = new List(); + List deltaAngles = new List(); + public Dictionary carProgress = new Dictionary(); + + + public float distanceBetweenCameras = 30.0f; + public float roadWidth = 10.0f; + public float roadHeight = 1.0f; + public float cameraHeight = 10.0f; + public float laneXOffset = 0.0f; + public float startIndexOffset = 0.0f; + + public string[] layerMaskNames; + int cullMask = 0; + + float distance; + + public void Awake() + { + int v = 0; + foreach (string layerName in layerMaskNames) + { + int layer = LayerMask.NameToLayer(layerName); + v |= 1 << layer; + } + + cullMask |= ~v; + } + + public void Init() + { + if (!GlobalState.raceCameras) { return; } + + if (pathManager.carPath.centerNodes.Count < 1) { return; } + + // Detect where to put cameras + Vector3 prev_point = pathManager.carPath.centerNodes[0].pos; + nodeIndexes.Add(0); + deltaAngles.Add(0); + distance = startIndexOffset; + + for (int i = 1; i < pathManager.carPath.centerNodes.Count; i++) + { + + PathNode node = pathManager.carPath.centerNodes[i]; + PathNode nextNode = pathManager.carPath.centerNodes[(i + 1) % pathManager.carPath.centerNodes.Count]; + + float deltaAngle = Vector3.SignedAngle(nextNode.pos - node.pos, node.rotation * Vector3.forward, Vector3.up); + deltaAngles.Add(deltaAngle); + + distance = Vector3.Distance(node.pos, nextNode.pos) + distance; + if (distance > distanceBetweenCameras) + { + nodeIndexes.Add(i); + prev_point = node.pos; + distance = 0; + } + + } + + // Add cameras + for (int i = 0; i < nodeIndexes.Count; i++) + { + int nodeIndex; + if (i < nodeIndexes.Count - 1) + { + nodeIndex = ((nodeIndexes[i] + nodeIndexes[(i + 1)]) / 2) % pathManager.carPath.centerNodes.Count; + } + else + { + nodeIndex = ((nodeIndexes[nodeIndexes.Count - 1] + nodeIndexes[0] + pathManager.carPath.centerNodes.Count) / 2) % pathManager.carPath.centerNodes.Count; + } + float sign = Mathf.Sign(deltaAngles[nodeIndex]); + PathNode node = pathManager.carPath.centerNodes[nodeIndexes[i]]; + PathNode midNode = pathManager.carPath.centerNodes[nodeIndex]; + Vector3 nodepos = node.pos + node.rotation * (laneXOffset * Vector3.right); + Vector3 midNodepos = midNode.pos + midNode.rotation * (laneXOffset * Vector3.right); + + + GameObject goRaceCamChild = new GameObject(string.Format("RaceCamera {0}", i)); + goRaceCamChild.transform.SetParent(transform); + RaceCamera cmp = goRaceCamChild.AddComponent(); + cmp.SetCameraTrigger(nodepos, node.rotation * Quaternion.AngleAxis(90, Vector3.up), new Vector3(0.1f, roadHeight, roadWidth)); + cmp.SetCam(midNodepos + midNode.rotation * (6f * sign * Vector3.right) + (cameraHeight * Vector3.up), midNodepos); + cmp.index = i; + cmp.camera.cullingMask = cullMask; + raceCameras.Add(cmp); + } + + // Enable first camera + raceCameras[0].camera.enabled = true; + + float coverage = GetCoverage(raceCameras.ToArray(), pathManager.carPath.centerNodes.ToArray(), nodeIndexes.ToArray()); + Debug.Log(string.Format("Race cameras coverage: {0}%", coverage)); + + } + + public void EnableCameras(bool enabled) + { + foreach (RaceCamera raceCamera in raceCameras) + { + raceCamera.camera.enabled = enabled; + } + } + + public void CameraTriggered(Collider col, Camera camera, int index) + { + int carID = col.attachedRigidbody.GetInstanceID(); + + if (carProgress.ContainsKey(carID)) + { + carProgress[carID] = index; + + int furtherValue = int.MinValue; + int furtherID = 0; + foreach (int key in carProgress.Keys) + { + + if (carProgress[key] > furtherValue) + { + furtherValue = carProgress[key]; + furtherID = key; + } + + } + + if (furtherID != 0) + { + EnableCameras(false); + raceCameras[carProgress[furtherID]].camera.enabled = true; + } + } + else + { + carProgress.Add(carID, index); + } + + } + + public float GetCoverage(RaceCamera[] raceCams, PathNode[] nodes, int[] nodeIndexes) + { + if (raceCams.Length == nodeIndexes.Length) + { + int count = 0; + + for (int i = 0; i < nodeIndexes.Length; i++) + { + int from = nodeIndexes[i]; + int to = nodeIndexes[(i + 1) % nodeIndexes.Length]; + if (i + 1 >= nodeIndexes.Length) { to += nodes.Length; } + + + for (int j = from; j < to; j++) + { + PathNode node = nodes[j % nodes.Length]; + + bool isSeenByCamera = IsSeenByCamera(raceCams[i].camera, node.pos + node.rotation * (laneXOffset * Vector3.right)); + if (isSeenByCamera) { count++; } + } + + + } + + return ((float)count / (float)nodes.Length) * 100.0f; // get coverage percentage + } + + else + { + Debug.LogWarning("No race camera found"); // no cameras found + return 0f; + } + } + + bool IsSeenByCamera(Camera camera, Vector3 position) + { + // check whether the object is visible by the camera + Vector3 viewPos = camera.WorldToViewportPoint(position); + if (viewPos.x >= 0 && viewPos.x <= 1 && viewPos.y >= 0 && viewPos.y <= 1 && viewPos.z > 0) + { + return true; + } + else + return false; + } +} diff --git a/Scripts/camera/RaceCameras.cs.meta b/Scripts/camera/RaceCameras.cs.meta new file mode 100755 index 0000000..cf6ff2b --- /dev/null +++ b/Scripts/camera/RaceCameras.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a55b4ccad22db364ab233f3090ff4a90 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/challenges.meta b/Scripts/challenges.meta new file mode 100755 index 0000000..e3b1b28 --- /dev/null +++ b/Scripts/challenges.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 221522121b901644ea356e7f8d308a24 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/challenges/IWaitCarPath.cs b/Scripts/challenges/IWaitCarPath.cs new file mode 100755 index 0000000..82fc153 --- /dev/null +++ b/Scripts/challenges/IWaitCarPath.cs @@ -0,0 +1,8 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public interface IWaitCarPath // interface to create challenges +{ + void Init(); +} diff --git a/Scripts/challenges/IWaitCarPath.cs.meta b/Scripts/challenges/IWaitCarPath.cs.meta new file mode 100755 index 0000000..784e0c0 --- /dev/null +++ b/Scripts/challenges/IWaitCarPath.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 22ac3198a4c067e49b6001e4cba04dfc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/challenges/SpinningPlatform.cs b/Scripts/challenges/SpinningPlatform.cs new file mode 100755 index 0000000..b815584 --- /dev/null +++ b/Scripts/challenges/SpinningPlatform.cs @@ -0,0 +1,22 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +[RequireComponent(typeof(Rigidbody))] +public class SpinningPlatform : MonoBehaviour +{ + public float speed = 1; + + private Rigidbody rb; + + void Start() + { + rb = GetComponent(); + } + + public void Update() + { + rb.angularVelocity = Vector3.up * speed; + } + +} diff --git a/Scripts/challenges/SpinningPlatform.cs.meta b/Scripts/challenges/SpinningPlatform.cs.meta new file mode 100755 index 0000000..cc6986e --- /dev/null +++ b/Scripts/challenges/SpinningPlatform.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b72dc2276c5dce043adf50e9182c7de4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/challenges/cone.meta b/Scripts/challenges/cone.meta new file mode 100755 index 0000000..98fb62d --- /dev/null +++ b/Scripts/challenges/cone.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 184a50e09228b31419b3b6e0149c10ca +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/challenges/cone/ColCone.cs b/Scripts/challenges/cone/ColCone.cs new file mode 100755 index 0000000..9b04089 --- /dev/null +++ b/Scripts/challenges/cone/ColCone.cs @@ -0,0 +1,35 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class ColCone : MonoBehaviour +{ + public int index = 0; + string target = "body"; + public float penalty = 1; + PrivateAPI privateAPI; + + void Start() + { + privateAPI = GameObject.FindObjectOfType(); + } + + void OnTriggerEnter(Collider col) + { + if (col.gameObject.name != target || privateAPI == null) { return; } + + Transform parent = col.transform.parent.parent; + if (parent == null) { return; } + string carName = parent.name; + + if (privateAPI == null) { return; } + privateAPI.CollisionWithChallenge(carName, index, Time.fixedTime); + + Timer[] status = parent.gameObject.GetComponentsInChildren(); + foreach (Timer t in status) + { + Debug.Log("Collision with Challenge"); + t.OnCollideChallenge(penalty); + } + } +} diff --git a/Scripts/challenges/cone/ColCone.cs.meta b/Scripts/challenges/cone/ColCone.cs.meta new file mode 100755 index 0000000..6667742 --- /dev/null +++ b/Scripts/challenges/cone/ColCone.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cf3af54a8b8b43b4c889da938862e3e4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/challenges/cone/ConeChallenge.cs b/Scripts/challenges/cone/ConeChallenge.cs new file mode 100755 index 0000000..762247e --- /dev/null +++ b/Scripts/challenges/cone/ConeChallenge.cs @@ -0,0 +1,63 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class ConeChallenge : MonoBehaviour, IWaitCarPath +{ + + public PathManager pathManager; + public int numRandCone = 0; + public float coneHeightOffset = 0.0f; + public float coneOffset = 1.0f; + public int iConePrefab = 0; + public int nodesAfterStart = 10; + public GameObject[] conePrefabs; + private List createdObjects = new List(); + + public void Init() + { + if (!GlobalState.generateRandomCones) { return; } + foreach (GameObject createdObject in createdObjects) + { + GameObject.Destroy(createdObject); + } + createdObjects = new List(); + Generate(); + } + + public void Generate() + { + if (GlobalState.useSeed) { Random.InitState(GlobalState.seed); }; + for (int i = 0; i < numRandCone; i++) + { + RandomCone(i); + } + } + + public void ResetChallenge() + { + foreach (GameObject createdObject in createdObjects) + { + GameObject.Destroy(createdObject); + } + createdObjects = new List(); + Generate(); + } + + public void RandomCone(int index) + { + if (pathManager.carPath.centerNodes != null && pathManager.carPath.centerNodes.Count > 0) + { + + int random_index = Random.Range(nodesAfterStart, pathManager.carPath.centerNodes.Count - nodesAfterStart); + PathNode random_node = pathManager.carPath.centerNodes[random_index]; + + Vector3 rand_pos_offset = new Vector3(Random.Range(-coneOffset, coneOffset), 0, Random.Range(-coneOffset, coneOffset)); + Vector3 xz_coords = new Vector3(random_node.pos.x, random_node.pos.y + coneHeightOffset, random_node.pos.z); + GameObject go = Instantiate(conePrefabs[iConePrefab], xz_coords + rand_pos_offset, conePrefabs[iConePrefab].transform.rotation); + ColCone col = go.GetComponentInChildren(); + if (col != null) { col.index = index; } + createdObjects.Add(go); + } + } +} diff --git a/Scripts/challenges/cone/ConeChallenge.cs.meta b/Scripts/challenges/cone/ConeChallenge.cs.meta new file mode 100755 index 0000000..7a87de4 --- /dev/null +++ b/Scripts/challenges/cone/ConeChallenge.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 765b095c46c20214482d8bf7c87b3995 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/challenges/light.meta b/Scripts/challenges/light.meta new file mode 100755 index 0000000..57eb675 --- /dev/null +++ b/Scripts/challenges/light.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: eb5dd3edb6bdd1147969270248ca34a5 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/challenges/light/LightChallenge.cs b/Scripts/challenges/light/LightChallenge.cs new file mode 100755 index 0000000..6070737 --- /dev/null +++ b/Scripts/challenges/light/LightChallenge.cs @@ -0,0 +1,38 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class LightChallenge : MonoBehaviour, IWaitCarPath +{ + public GameObject lightSource; + public Color minLightColorRange; + public Color maxLightColorRange; + public float max_angle = 45; + + public void Init() + { + if (!GlobalState.randomLight) { return; } + Randomize(); + } + + public void ResetChallenge() + { + Init(); + } + + public void Randomize() + { + if (GlobalState.useSeed) { Random.InitState(GlobalState.seed); }; + if (lightSource != null) + { + Light lightComp = lightSource.GetComponent(); + if (lightComp != null) + { + // calculate the interpolation between the two colors for a random T + lightComp.color = Color.Lerp(minLightColorRange, maxLightColorRange, Random.Range(0.0f, 1.0f)); + } + + lightSource.transform.localRotation = Quaternion.Euler(90 + Random.Range(-max_angle, max_angle), Random.Range(0, 180), 0); + } + } +} diff --git a/Scripts/challenges/light/LightChallenge.cs.meta b/Scripts/challenges/light/LightChallenge.cs.meta new file mode 100755 index 0000000..230fcc6 --- /dev/null +++ b/Scripts/challenges/light/LightChallenge.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 26d745fc5c0fb634390448d50ad7e768 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/challenges/randassets.meta b/Scripts/challenges/randassets.meta new file mode 100755 index 0000000..4115fdf --- /dev/null +++ b/Scripts/challenges/randassets.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f72b10198e8c7af46ad62e609214cafe +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/challenges/randassets/RandAssetsChallenge.cs b/Scripts/challenges/randassets/RandAssetsChallenge.cs new file mode 100755 index 0000000..6d1e201 --- /dev/null +++ b/Scripts/challenges/randassets/RandAssetsChallenge.cs @@ -0,0 +1,118 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class RandAssetsChallenge : MonoBehaviour, IWaitCarPath +{ + public PathManager pathManager; + public float minRange = 5; + public float maxRange = 20; + public float heightOffset = 0; + public int numAssets = 10; + public GameObject[] prefabList; + public GameObject parentGameObject; + private List createdObjects = new List(); + + public void Init() + { + if (!GlobalState.generateTrees) { return; } + foreach (GameObject createdObject in createdObjects) + { + GameObject.Destroy(createdObject); + } + createdObjects = new List(); + Generate(); + } + + public void Generate() + { + if (GlobalState.useSeed) { Random.InitState(GlobalState.seed); }; + GameObject[] randomList = new GameObject[numAssets]; + for (int i = 0; i < numAssets; i++) // pick some items from the prefab list and add them to the randomList array + { + int index = Random.Range(0, prefabList.Length); + randomList[i] = prefabList[index]; + } + + PlaceAssets(randomList); + } + + public void ResetChallenge() + { + foreach (GameObject createdObject in createdObjects) + { + GameObject.Destroy(createdObject); + } + createdObjects = new List(); + Generate(); + } + + public void PlaceAssets(GameObject[] assetList) + { + if (pathManager.carPath.centerNodes != null && pathManager.carPath.centerNodes.Count > 0) + { + for (int i = 0; i < assetList.Length; i++) + { + GameObject asset = assetList[i]; + int random_index = Random.Range(0, pathManager.carPath.centerNodes.Count); + PathNode random_node = pathManager.carPath.centerNodes[random_index]; + + bool valid_pos = false; + int max_iter = 10; + while (!valid_pos && max_iter > 0) + { + + Vector3 rand_pos_offset = new Vector3(RandomMinMaxRange(minRange, maxRange), 0, RandomMinMaxRange(minRange, maxRange)); + Vector3 xyz_coords = new Vector3(random_node.pos.x, random_node.pos.y + heightOffset, random_node.pos.z); // height variation is not supported yet + Vector3 new_point = rand_pos_offset + xyz_coords + asset.transform.position; + + if (IsValid(pathManager.carPath, new_point)) + { + GameObject go = Instantiate(asset, new_point, asset.transform.rotation); + go.transform.RotateAround(go.transform.position, Vector3.up, Random.Range(0, 180)); + if (parentGameObject != null) + { + go.transform.parent = parentGameObject.transform; + } + go.isStatic = true; // set the object to static to save some performance + createdObjects.Add(go); + break; + } + + max_iter--; + } + } + + } + } + + public bool IsValid(CarPath path, Vector3 point) + { + int pathLength = path.centerNodes.Count; + float[] distances = new float[pathLength - 1]; + + for (int i = 0; i < pathLength; i++) + { + Vector3 pathPoint = path.centerNodes[i].pos; + float distance = Vector3.Distance(point, pathPoint); + + if (distance < minRange) + { + return false; + } + } + + return true; + } + + public float RandomMinMaxRange(float min, float max) + { + int sign = -1; + if (Random.value > 0.5) + { + sign = 1; + } + return Random.Range(max, min) * sign; + } + +} diff --git a/Scripts/challenges/randassets/RandAssetsChallenge.cs.meta b/Scripts/challenges/randassets/RandAssetsChallenge.cs.meta new file mode 100755 index 0000000..0db2e5a --- /dev/null +++ b/Scripts/challenges/randassets/RandAssetsChallenge.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b1ff43d487d977843bc457d32fc56325 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/startingLine.cs b/Scripts/startingLine.cs new file mode 100755 index 0000000..af8d2cb --- /dev/null +++ b/Scripts/startingLine.cs @@ -0,0 +1,33 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class startingLine : MonoBehaviour +{ + public int index = 0; + string target = "body"; + PrivateAPI privateAPI; + + void Start() + { + privateAPI = GameObject.FindObjectOfType(); + } + + void OnTriggerEnter(Collider col) + { + if (col.gameObject.name != target) { return; } + float time = Time.fixedTime; + + Transform parent = col.transform.parent.parent; + if (parent == null) { return; } + + string carName = parent.name; + tk.TcpCarHandler client = parent.GetComponentInChildren(); + + if (client != null) + UnityMainThreadDispatcher.Instance().Enqueue(client.SendCollisionWithStartingLine(index, time)); + + if (privateAPI != null) + privateAPI.CollisionWithStatingLine(carName, index, time); + } +} diff --git a/Scripts/startingLine.cs.meta b/Scripts/startingLine.cs.meta new file mode 100755 index 0000000..702eec3 --- /dev/null +++ b/Scripts/startingLine.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 99dd490a05b4e1149964cbad9755353d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/tcp.meta b/Scripts/tcp.meta new file mode 100755 index 0000000..5ce42b5 --- /dev/null +++ b/Scripts/tcp.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8faec0be33f4542878094f2275316037 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/tcp/Delegates.cs b/Scripts/tcp/Delegates.cs new file mode 100755 index 0000000..4d46fc8 --- /dev/null +++ b/Scripts/tcp/Delegates.cs @@ -0,0 +1,11 @@ + +namespace tk +{ + public class Delegates + { + + public delegate void OnMsgRecv(JSONObject data); + + public OnMsgRecv onMsgCb; + } +} \ No newline at end of file diff --git a/Scripts/tcp/Delegates.cs.meta b/Scripts/tcp/Delegates.cs.meta new file mode 100755 index 0000000..848195f --- /dev/null +++ b/Scripts/tcp/Delegates.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b07af06229ec34778b257f87dd8d4600 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/tcp/Dispatcher.cs b/Scripts/tcp/Dispatcher.cs new file mode 100755 index 0000000..1c4fc5a --- /dev/null +++ b/Scripts/tcp/Dispatcher.cs @@ -0,0 +1,62 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using System.Net; +using System.Net.Sockets; +using System; +using tk; + +namespace tk +{ + public class Dispatcher { + //Name to Message client handling. + + private Dictionary eventDictionary; + + public void Init () + { + if (eventDictionary == null) + { + eventDictionary = new Dictionary(); + } + } + + public void Reset() + { + eventDictionary.Clear(); + } + + public void Register(string msgType, Delegates.OnMsgRecv regCallback) + { + Delegates Delegates = null; + + if (eventDictionary.TryGetValue (msgType, out Delegates)) + { + Delegates.onMsgCb += regCallback; + } + else + { + Delegates newDel = new Delegates(); + + newDel.onMsgCb += regCallback; + + eventDictionary.Add(msgType, newDel); + } + } + + public void Dipatch(string msgType, JSONObject msgPayload) + { + Delegates delegates = null; + + if (eventDictionary.TryGetValue (msgType, out delegates)) + { + delegates.onMsgCb.Invoke(msgPayload); + } + else + { + Debug.Log("No delegates for msg type: " + msgType); + } + } + } + +} \ No newline at end of file diff --git a/Scripts/tcp/Dispatcher.cs.meta b/Scripts/tcp/Dispatcher.cs.meta new file mode 100755 index 0000000..6a4dd03 --- /dev/null +++ b/Scripts/tcp/Dispatcher.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a2ca3182879cb41569b7dccb9541141f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/tcp/JsonTcpClient.cs b/Scripts/tcp/JsonTcpClient.cs new file mode 100755 index 0000000..6b3b7da --- /dev/null +++ b/Scripts/tcp/JsonTcpClient.cs @@ -0,0 +1,176 @@ +using System.Collections.Generic; +using UnityEngine; +using System; + +namespace tk +{ + // Wrap a tk.TcpClient and dispatcher to handle network events over a tcp connection. + // Use Json for message contents. Assumes no newline characters in the json string contents, + // and end each packet with a newline for separation. + [RequireComponent(typeof(tk.TcpClient))] + public class JsonTcpClient : MonoBehaviour { + + // Our reference to the required 'base' component + private tk.TcpClient client; + + // This allows other objects to register for incoming json messages + public tk.Dispatcher dispatcher; + + // Some messages need to be handled in the main thread. Unity object creation, etc.. + public bool dispatchInMainThread = false; + + // A list of raw json strings received from network and waiting to dispatched locally. + private List recv_packets; + + // Make sure to protect our recv_packets from race conditions + readonly object _locker = new object(); + + //required for stream parsing where client may recv multiple messages at once. + const string packetTerminationChar = "\n"; + + + void Awake() + { + recv_packets = new List(); + dispatcher = new tk.Dispatcher(); + dispatcher.Init(); + client = GetComponent(); + + Initcallbacks(); + } + + // Interact with our base TcpClient to handle incoming data + void Initcallbacks() + { + client.onDataRecvCB += new TcpClient.OnDataRecv(OnDataRecv); + client.onConnectedCB += new TcpClient.OnConnected(OnConnected); + } + + public void OnConnected() + { + recv_packets.Add("{\"msg_type\" : \"connected\"}"); + } + + // Close our socket connection + public void Disconnect() + { + client.Disconnect(); + } + + + // Send a json packet over our TCP socket asynchronously. + public void SendMsg(JSONObject msg) + { + string packet = msg.ToString() + packetTerminationChar; + + byte[] bytes = System.Text.Encoding.UTF8.GetBytes(packet); + + client.SendData( bytes ); + } + + + // Our callback from the TCPClient to get data. + void OnDataRecv(byte[] bytes) + { + string str = System.Text.Encoding.UTF8.GetString(bytes); + + lock(_locker) + { + recv_packets.Add(str); + } + + if(!dispatchInMainThread) + { + Dispatch(); + } + } + + // Over simplified algorithm to extract json payload from TCP stream. It assumes that there are no nested JSON objects + List ExtractJsonFromStream() + { + List result = new List(); + string jsonBuffer = ""; + string[] jsonMessages; + + //Ignore request if TCP buffer list is empty + if (recv_packets.Count == 0) { + return result; + } + + // Concat all reveiced TCP buffers to have change to extract as much as possible JSON objects + foreach(string str in recv_packets) + { + jsonBuffer = String.Concat (jsonBuffer, str); + } + + recv_packets.Clear(); + + // Split data received on each json object delimitor + jsonMessages = jsonBuffer.Split("{"[0]); + + for (int i = 0; i < jsonMessages.Length; i++) { + // Ignore empty parts, this is likely side effect of Split + if (jsonMessages[i].Length == 0) + { + continue; + } + // Since split remove delimitor, add it back to keep JSON structure + string theMessage = jsonMessages[i].Insert(0, "{"); + // If JSON message is complete, add to list of complete JSON message + if (theMessage[0]=='{' && theMessage.Substring(theMessage.Length - 1)[0]=='}') + { + result.Add(theMessage); + } else { + if (i==(jsonMessages.Length-1)) { + //last message is a partial one, push back to recv_packets + recv_packets.Add (theMessage); + } else { + Debug.Log("Unexpected partial JSON object in the middle of the TCP buffer !, buffer = "+jsonBuffer); + } + } + + } + return result; + } + + // Send each queued json packet to the recipient which registered + // with our dispatcher. + void Dispatch() + { + lock(_locker) + { + List msgs = ExtractJsonFromStream(); + foreach (string msg in msgs) + { + try + { + //Only extract and propagate the last one to avoid to overload simulator in case of burst + JSONObject j = new JSONObject(msg); + + string msg_type = j["msg_type"].str; + + // Debug.Log("Got: " + msg_type); + + dispatcher.Dipatch(msg_type, j); + + } + catch(Exception e) + { + Debug.LogError(e.ToString()); + } + + } + } + } + + + // Optionally poll our dispatch queue in the main thread context + void Update() + { + if (dispatchInMainThread) + { + Dispatch(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/tcp/JsonTcpClient.cs.meta b/Scripts/tcp/JsonTcpClient.cs.meta new file mode 100755 index 0000000..53f4907 --- /dev/null +++ b/Scripts/tcp/JsonTcpClient.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d39224ddd3f514b7495096492daae1e7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/tcp/TcpCarHandler.cs b/Scripts/tcp/TcpCarHandler.cs new file mode 100755 index 0000000..75e8da7 --- /dev/null +++ b/Scripts/tcp/TcpCarHandler.cs @@ -0,0 +1,627 @@ +using System.Collections; +using UnityEngine; +using System; +using UnityEngine.UI; +using System.Globalization; +using UnityEngine.SceneManagement; +using UnityStandardAssets.ImageEffects; +using UnityEngine.AI; +using System.Collections.Generic; + +namespace tk +{ + + public class TcpCarHandler : MonoBehaviour + { + + public GameObject carObj; + public ICar car; + public CarSpawner carSpawner; + public PathManager pm; + public CarConfig conf; + + // Sensors + public CameraSensor camSensor; + public CameraSensor camSensorB; + public Lidar lidar; + public Odometry[] odom; + + private tk.JsonTcpClient client; + public Text ai_text; + string customOverlayText = ""; + MapOverlay mapOverlay; + + public float limitFPS = 20.0f; + float timeSinceLastCapture = 0.0f; + public float timeSinceLastMoved = 0.0f; + public Vector3 lastPos = Vector3.zero; + public float lastDistanceTraveled = 0.0f; + + float steer_to_angle = 16.0f; + + float ai_steering = 0.0f; + float ai_throttle = 0.0f; + float ai_brake = 0.0f; + + int iActiveSpan = 0; + + bool asynchronous = true; + float time_step = 0.1f; + bool bResetCar = false; + bool bExitScene = false; + + public enum State + { + UnConnected, + SendTelemetry + } + + public State state = State.UnConnected; + + void Awake() + { + car = carObj.GetComponent(); + conf = carObj.GetComponent(); + pm = GameObject.FindObjectOfType(); + carSpawner = GameObject.FindObjectOfType(); + Canvas canvas = GameObject.FindObjectOfType(); + GameObject go = CarSpawner.getChildGameObject(canvas.gameObject, "AISteering"); + if (go != null) + ai_text = go.GetComponent(); + + if (pm != null && carObj != null) + { + iActiveSpan = pm.carPath.GetClosestSpanIndex(carObj.transform.position); + } + } + + public void Init(tk.JsonTcpClient _client) + { + client = _client; + + if (client == null) + return; + + client.dispatchInMainThread = false; //too slow to wait. + client.dispatcher.Register("get_protocol_version", new tk.Delegates.OnMsgRecv(OnProtocolVersion)); + client.dispatcher.Register("control", new tk.Delegates.OnMsgRecv(OnControlsRecv)); + client.dispatcher.Register("exit_scene", new tk.Delegates.OnMsgRecv(OnExitSceneRecv)); + client.dispatcher.Register("reset_car", new tk.Delegates.OnMsgRecv(OnResetCarRecv)); + client.dispatcher.Register("step_mode", new tk.Delegates.OnMsgRecv(OnStepModeRecv)); + client.dispatcher.Register("quit_app", new tk.Delegates.OnMsgRecv(OnQuitApp)); + client.dispatcher.Register("regen_road", new tk.Delegates.OnMsgRecv(OnRegenRoad)); + client.dispatcher.Register("set_ai_text", new tk.Delegates.OnMsgRecv(OnSetAiText)); + client.dispatcher.Register("car_config", new tk.Delegates.OnMsgRecv(OnCarConfig)); + client.dispatcher.Register("cam_config", new tk.Delegates.OnMsgRecv(OnCamConfig)); + client.dispatcher.Register("cam_config_b", new tk.Delegates.OnMsgRecv(OnCamConfigB)); + client.dispatcher.Register("lidar_config", new tk.Delegates.OnMsgRecv(OnLidarConfig)); + client.dispatcher.Register("set_position", new tk.Delegates.OnMsgRecv(OnSetPosition)); + client.dispatcher.Register("node_position", new tk.Delegates.OnMsgRecv(OnNodePositionRecv)); + } + + public void Start() + { + mapOverlay = gameObject.AddComponent(); + SendCarLoaded(); + state = State.SendTelemetry; + } + + public tk.JsonTcpClient GetClient() + { + return client; + } + + public void OnDestroy() + { + if (client != null && client.dispatcher != null) + client.dispatcher.Reset(); + } + + void Disconnect() + { + client.Disconnect(); + } + + public void Boot() + { + if (carSpawner != null) + { + if (client != null) { Disconnect(); } + carSpawner.RemoveCar(client); + } + } + + void OnProtocolVersion(JSONObject msg) + { + JSONObject json = new JSONObject(JSONObject.Type.OBJECT); + json.AddField("msg_type", "protocol_version"); + json.AddField("version", "2"); + + client.SendMsg(json); + } + + void SendTelemetry() + { + if (client == null) + return; + + JSONObject json = new JSONObject(JSONObject.Type.OBJECT); + json.AddField("msg_type", "telemetry"); + + json.AddField("steering_angle", car.GetSteering() / steer_to_angle); + json.AddField("throttle", car.GetThrottle()); + + json.AddField("image", Convert.ToBase64String(camSensor.GetImageBytes())); + + if (camSensorB != null && camSensorB.gameObject.activeInHierarchy) + { + json.AddField("imageb", Convert.ToBase64String(camSensorB.GetImageBytes())); + } + + if (lidar != null && lidar.gameObject.activeInHierarchy) + { + json.AddField("lidar", lidar.GetOutputAsJson()); + } + + foreach (Odometry o in odom) + { + json.AddField(o.Label, o.GetNumberRotations()); + } + + + json.AddField("hit", car.GetLastCollision()); + car.ClearLastCollision(); + json.AddField("time", Time.timeSinceLevelLoad); + + Vector3 velocity = car.GetVelocity() / 8.0f; + json.AddField("speed", velocity.magnitude); + + Vector3 accel = car.GetAccel() / 8.0f; + json.AddField("accel_x", accel.x); + json.AddField("accel_y", accel.y); + json.AddField("accel_z", accel.z); + + Vector3 gyro = car.GetGyro(); + json.AddField("gyro_x", gyro.x); + json.AddField("gyro_y", gyro.y); + json.AddField("gyro_z", gyro.z); + + Transform tm = car.GetTransform(); + Vector3 eulerAngles = tm.rotation.eulerAngles; + json.AddField("pitch", eulerAngles.x); + json.AddField("yaw", eulerAngles.y); + json.AddField("roll", eulerAngles.z); + + if (pm != null) + { + float cte = 0.0f; + pm.carPath.GetCrossTrackErr(tm.position, ref iActiveSpan, ref cte); // get distance to closest node + if (GlobalState.extendedTelemetry) { json.AddField("cte", cte); } + + json.AddField("activeNode", iActiveSpan); + json.AddField("totalNodes", pm.carPath.nodes.Count); + } + + // not intended to use in races, just to train + if (GlobalState.extendedTelemetry) + { + Vector3 pos = tm.position / 8.0f; + json.AddField("pos_x", pos.x); + json.AddField("pos_y", pos.y); + json.AddField("pos_z", pos.z); + + json.AddField("vel_x", velocity.x); + json.AddField("vel_y", velocity.y); + json.AddField("vel_z", velocity.z); + } + client.SendMsg(json); + } + + void SendCarLoaded() + { + if (client == null) + return; + + JSONObject json = new JSONObject(JSONObject.Type.OBJECT); + json.AddField("msg_type", "car_loaded"); + client.SendMsg(json); + Debug.Log("car loaded."); + } + + float clamp(float val, float low, float high) + { + float ret = val; + if (val > high) + ret = high; + else if (val < low) + ret = low; + return ret; + } + + void OnControlsRecv(JSONObject json) + { + try + { + ai_steering = float.Parse(json["steering"].str, CultureInfo.InvariantCulture.NumberFormat); + ai_throttle = float.Parse(json["throttle"].str, CultureInfo.InvariantCulture.NumberFormat); + ai_brake = float.Parse(json["brake"].str, CultureInfo.InvariantCulture.NumberFormat); + + ai_steering = clamp(ai_steering, -1.0f, 1.0f); + ai_throttle = clamp(ai_throttle, -1.0f, 1.0f); + ai_brake = clamp(ai_brake, 0.0f, 1.0f); + + ai_steering *= steer_to_angle; + + car.RequestSteering(ai_steering); + car.RequestThrottle(ai_throttle); + car.RequestFootBrake(ai_brake); + } + catch (Exception e) + { + Debug.Log(e.ToString()); + } + } + + void OnExitSceneRecv(JSONObject json) + { + bExitScene = true; + } + + void ExitScene() + { + SceneManager.LoadSceneAsync(0); + } + + void OnResetCarRecv(JSONObject json) + { + bResetCar = true; + } + + void OnRegenRoad(JSONObject json) + { + //This causes the track to be regenerated with the given settings. + //This only works in scenes that have random track generation enabled. + //For now that is only in scene road_generator. + //An index into our track options. 5 in scene RoadGenerator. + int road_style = int.Parse(json.GetField("road_style").str); + int rand_seed = int.Parse(json.GetField("rand_seed").str); + float turn_increment = float.Parse(json.GetField("turn_increment").str, CultureInfo.InvariantCulture); + + //We get this callback in a worker thread, but need to make mainthread calls. + //so use this handy utility dispatcher from + // https://github.com/PimDeWitte/UnityMainThreadDispatcher + UnityMainThreadDispatcher.Instance().Enqueue(RegenRoad(road_style, rand_seed, turn_increment)); + } + + IEnumerator RegenRoad(int road_style, int rand_seed, float turn_increment) + { + TrainingManager train_mgr = GameObject.FindObjectOfType(); + PathManager path_mgr = GameObject.FindObjectOfType(); + RoadBuilder road_bld = GameObject.FindObjectOfType(); + + // Apply optional turn increment override before seeding + if (turn_increment != 0.0f && path_mgr != null) + path_mgr.turnInc = turn_increment; + + UnityEngine.Random.InitState(rand_seed); + + if (train_mgr != null) + { + // Scenes with TrainingManager (e.g. generated_track): use the full flow + train_mgr.SetRoadStyle(road_style); + train_mgr.OnMenuRegenTrack(); + } + else if (path_mgr != null) + { + // Scenes without TrainingManager (e.g. generated_road): drive regen directly. + // DestroyRoad removes old barriers/mesh; InitCarPath regenerates nodes + // and auto-notifies RoadBuilder via IWaitCarPath to rebuild geometry. + if (road_bld != null) + road_bld.DestroyRoad(); + path_mgr.InitCarPath(); + } + + yield return null; + } + + void OnSetAiText(JSONObject json) + { + customOverlayText = json.GetField("text").str; + } + + void OnCarConfig(JSONObject json) + { + Debug.Log("Got car config message"); + + string body_style = json.GetField("body_style").str; + int body_r = int.Parse(json.GetField("body_r").str); + int body_g = int.Parse(json.GetField("body_g").str); + int body_b = int.Parse(json.GetField("body_b").str); + string car_name = json.GetField("car_name").str; + int font_size = 100; + + if (json.GetField("font_size") != null) + font_size = int.Parse(json.GetField("font_size").str); + + if (carObj != null && car_name != "Racer Name") + UnityMainThreadDispatcher.Instance().Enqueue(SetCarConfig(body_style, body_r, body_g, body_b, car_name, font_size)); + } + + IEnumerator SetCarConfig(string body_style, int body_r, int body_g, int body_b, string car_name, int font_size) + { + CarConfig conf = carObj.GetComponent(); + + if (conf) + { + conf.SetStyle(body_style, body_r, body_g, body_b, car_name, font_size); + } + + yield return null; + } + + void OnCamConfig(JSONObject json) + { + ParseCamConfig(json, 0); + } + + void OnCamConfigB(JSONObject json) + { + ParseCamConfig(json, 1); + } + + void ParseCamConfig(JSONObject json, int iCamera) + { + float fov = float.Parse(json.GetField("fov").str, CultureInfo.InvariantCulture.NumberFormat); + float offset_x = float.Parse(json.GetField("offset_x").str, CultureInfo.InvariantCulture.NumberFormat); + float offset_y = float.Parse(json.GetField("offset_y").str, CultureInfo.InvariantCulture.NumberFormat); + float offset_z = float.Parse(json.GetField("offset_z").str, CultureInfo.InvariantCulture.NumberFormat); + float rot_x = float.Parse(json.GetField("rot_x").str, CultureInfo.InvariantCulture.NumberFormat); + float rot_y = 0f; + float rot_z = 0f; + if (json.HasField("rot_y")) { rot_y = float.Parse(json.GetField("rot_y").str, CultureInfo.InvariantCulture.NumberFormat); } + if (json.HasField("rot_z")) { rot_z = float.Parse(json.GetField("rot_z").str, CultureInfo.InvariantCulture.NumberFormat); } + float fish_eye_x = float.Parse(json.GetField("fish_eye_x").str, CultureInfo.InvariantCulture.NumberFormat); + float fish_eye_y = float.Parse(json.GetField("fish_eye_y").str, CultureInfo.InvariantCulture.NumberFormat); + int img_w = int.Parse(json.GetField("img_w").str); + int img_h = int.Parse(json.GetField("img_h").str); + int img_d = int.Parse(json.GetField("img_d").str); + string img_enc = json.GetField("img_enc").str; + + if (carObj != null) + UnityMainThreadDispatcher.Instance().Enqueue(SetCamConfig(iCamera, fov, offset_x, offset_y, offset_z, rot_x, rot_y, rot_z, img_w, img_h, img_d, img_enc, fish_eye_x, fish_eye_y)); + } + + IEnumerator SetCamConfig(int iCamera, float fov, float offset_x, float offset_y, float offset_z, float rot_x, + float rot_y, float rot_z, int img_w, int img_h, int img_d, string img_enc, float fish_eye_x, float fish_eye_y) + { + CameraSensor cam = null; + + if (iCamera == 0) + cam = camSensor; + else + { + cam = camSensorB; + + if (cam != null && !cam.gameObject.activeInHierarchy) + { + cam.gameObject.SetActive(true); + } + } + + if (cam) + { + cam.SetConfig(fov, offset_x, offset_y, offset_z, rot_x, rot_y, rot_z, img_w, img_h, img_d, img_enc); + + Fisheye fe = cam.gameObject.GetComponent(); + + if (fe != null && (fish_eye_x != 0.0f || fish_eye_y != 0.0f)) + { + fe.enabled = true; + fe.strengthX = fish_eye_x; + fe.strengthY = fish_eye_y; + } + } + + yield return null; + } + + void OnSetPosition(JSONObject json) + { + if (GlobalState.extendedTelemetry) + { + float pos_x = float.Parse(json.GetField("pos_x").str, CultureInfo.InvariantCulture.NumberFormat); + float pos_y = float.Parse(json.GetField("pos_y").str, CultureInfo.InvariantCulture.NumberFormat); + float pos_z = float.Parse(json.GetField("pos_z").str, CultureInfo.InvariantCulture.NumberFormat); + Quaternion rot = Quaternion.identity; + if (json.GetField("Qx") != null && json.GetField("Qy") != null && json.GetField("Qz") != null && json.GetField("Qw") != null) + { + float qx = float.Parse(json.GetField("Qx").str, CultureInfo.InvariantCulture.NumberFormat); + float qy = float.Parse(json.GetField("Qy").str, CultureInfo.InvariantCulture.NumberFormat); + float qz = float.Parse(json.GetField("Qz").str, CultureInfo.InvariantCulture.NumberFormat); + float qw = float.Parse(json.GetField("Qw").str, CultureInfo.InvariantCulture.NumberFormat); + + rot.x = qx; + rot.y = qy; + rot.z = qz; + rot.w = qw; + } + + UnityMainThreadDispatcher.Instance().Enqueue(setCarPosition(pos_x, pos_y, pos_z, rot)); + } + } + + IEnumerator setCarPosition(float pos_x, float pos_y, float pos_z, Quaternion rot) + { + carObj.transform.position = new Vector3(pos_x, pos_y, pos_z); + carObj.transform.rotation = rot; + yield return null; + } + + void OnLidarConfig(JSONObject json) + { + float offset_x = float.Parse(json.GetField("offset_x").str, CultureInfo.InvariantCulture.NumberFormat); + float offset_y = float.Parse(json.GetField("offset_y").str, CultureInfo.InvariantCulture.NumberFormat); + float offset_z = float.Parse(json.GetField("offset_z").str, CultureInfo.InvariantCulture.NumberFormat); + float rot_x = float.Parse(json.GetField("rot_x").str, CultureInfo.InvariantCulture.NumberFormat); + + float degPerSweepInc = float.Parse(json.GetField("degPerSweepInc").str, CultureInfo.InvariantCulture.NumberFormat); + float degAngDown = float.Parse(json.GetField("degAngDown").str, CultureInfo.InvariantCulture.NumberFormat); + float degAngDelta = float.Parse(json.GetField("degAngDelta").str, CultureInfo.InvariantCulture.NumberFormat); + float maxRange = float.Parse(json.GetField("maxRange").str, CultureInfo.InvariantCulture.NumberFormat); + float noise = float.Parse(json.GetField("noise").str, CultureInfo.InvariantCulture.NumberFormat); + int numSweepsLevels = int.Parse(json.GetField("numSweepsLevels").str); + + + if (carObj != null) + UnityMainThreadDispatcher.Instance().Enqueue(SetLidarConfig(offset_x, offset_y, offset_z, rot_x, degPerSweepInc, degAngDown, degAngDelta, maxRange, noise, numSweepsLevels)); + } + + IEnumerator SetLidarConfig(float offset_x, float offset_y, float offset_z, float rot_x, + float degPerSweepInc, float degAngDown, float degAngDelta, float maxRange, float noise, int numSweepsLevels) + { + if (lidar != null) + { + if (!lidar.gameObject.activeInHierarchy) + lidar.gameObject.SetActive(true); + + lidar.SetConfig(offset_x, offset_y, offset_z, rot_x, degPerSweepInc, degAngDown, degAngDelta, maxRange, noise, numSweepsLevels); + } + + yield return null; + } + + void OnStepModeRecv(JSONObject json) + { + string step_mode = json.GetField("step_mode").str; + float _time_step = float.Parse(json.GetField("time_step").str); + + Debug.Log("got settings"); + + if (step_mode == "synchronous") + { + Debug.Log("setting mode to synchronous"); + asynchronous = false; + this.time_step = _time_step; + Time.timeScale = 0.0f; + } + else + { + Debug.Log("setting mode to asynchronous"); + asynchronous = true; + } + } + + public IEnumerator SendCollisionWithStartingLine(int startingLineIndex, float timeStamp) + { + if (client != null) + { + JSONObject json = new JSONObject(JSONObject.Type.OBJECT); + json.AddField("msg_type", "collision_with_starting_line"); + json.AddField("starting_line_index", startingLineIndex); + json.AddField("timeStamp", timeStamp); + client.SendMsg(json); + } + + yield return null; + } + + public void OnNodePositionRecv(JSONObject json) + { + int index = int.Parse(json.GetField("index").str); + if (pm != null && index >= 0 && index < pm.carPath.nodes.Count) + { + PathNode node = pm.carPath.nodes[index]; + UnityMainThreadDispatcher.Instance().Enqueue(SendNodePosition(index, node)); + } + } + + public IEnumerator SendNodePosition(int index, PathNode node) + { + + JSONObject json = new JSONObject(JSONObject.Type.OBJECT); + json.AddField("msg_type", "node_position"); + json.AddField("index", index); + json.AddField("pos_x", node.pos.x); + json.AddField("pos_y", node.pos.y); + json.AddField("pos_z", node.pos.z); + json.AddField("Qx", node.rotation.x); + json.AddField("Qy", node.rotation.y); + json.AddField("Qz", node.rotation.z); + json.AddField("Qw", node.rotation.w); + client.SendMsg(json); + yield return null; + } + + void OnQuitApp(JSONObject json) + { + Application.Quit(); + } + + void FixedUpdate() + { + if (bExitScene) + { + bExitScene = false; + ExitScene(); + } + + if (state == State.SendTelemetry) + { + if (bResetCar) + { + car.RestorePosRot(); + + if (carObj != null) + { + //reset last controls + car.RequestSteering(0.0f); + car.RequestThrottle(0.0f); + car.RequestFootBrake(10.0f); + + // Reset closest point of car path + if (pm) + iActiveSpan = 0; + } + + bResetCar = false; + } + + timeSinceLastCapture += Time.fixedDeltaTime; + if (timeSinceLastCapture >= 1.0f / limitFPS) + { + timeSinceLastCapture -= (1.0f / limitFPS); + SendTelemetry(); + } + + if (ai_text != null) + { + string steerLine = string.Format("Steer:{0,7:F3} Thr:{1:F2}", ai_steering / steer_to_angle, ai_throttle); + ai_text.text = customOverlayText.Length > 0 ? steerLine + "\n" + customOverlayText : steerLine; + } + + Vector3 currentPos = car.GetTransform().position; + float distance = Vector3.Distance(currentPos, lastPos); + + if (distance < 1f) + { + timeSinceLastMoved += Time.fixedDeltaTime; + } + else + { + timeSinceLastMoved = 0.0f; + lastPos = currentPos; + } + if (timeSinceLastMoved >= GlobalState.timeOut && carSpawner != null) + { + Boot(); + } + } + } + + public bool IsGhostCar() + { + if (client == null) { return true; } + else { return false; } + } + } +} diff --git a/Scripts/tcp/TcpCarHandler.cs.meta b/Scripts/tcp/TcpCarHandler.cs.meta new file mode 100755 index 0000000..7e2d587 --- /dev/null +++ b/Scripts/tcp/TcpCarHandler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a9dd927a729e546ca959bfe84042f855 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/tcp/TcpClient.cs b/Scripts/tcp/TcpClient.cs new file mode 100755 index 0000000..e66a5fa --- /dev/null +++ b/Scripts/tcp/TcpClient.cs @@ -0,0 +1,233 @@ +using UnityEngine; +using System.Collections; +using System; +using System.Net; +using System.Net.Sockets; + +namespace tk +{ + public class TcpClient : MonoBehaviour + { + public bool debug = false; + + private Socket _clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + private byte[] _recieveBuffer = new byte[8142]; + private TcpServer _server = null; + + public delegate void OnDataRecv(byte[] data); + + public OnDataRecv onDataRecvCB; + + public delegate void OnConnected(); + + public OnConnected onConnectedCB; + + // Flag to let us know a connection has dropped. + private bool dropped = false; + public float time_check_dropped = 0.0f; + public float time_check_dropped_freq = 3.0f; + + /// + /// Connect will establish a new TCP socket connection to a remote ip, port. This method is the first method + /// called to start using this object. + /// + /// + /// + /// + public bool Connect(string ip, int port) + { + if (_clientSocket.Connected) + return false; + + try + { + IPAddress address = IPAddress.Parse(ip); + _clientSocket.Connect(new IPEndPoint(address, port)); + } + catch (SocketException ex) + { + Debug.Log(ex.Message); + return false; + } + + dropped = false; + _clientSocket.BeginReceive(_recieveBuffer, 0, _recieveBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), null); + + return true; + } + + /// + /// OnServerAccept is an alternate form of client initialization that occurs when a server has accepted a client + /// connection already and passes that socket in clientSock in a connected state. This also then passes a pointer + /// to the server which can be used by the clients to broadcast messages to all the peers. + /// + /// + /// + /// + public bool OnServerAccept(Socket clientSock, TcpServer server) + { + if (!clientSock.Connected) + return false; + + _clientSocket = clientSock; + _server = server; + dropped = false; + + _clientSocket.BeginReceive(_recieveBuffer, 0, _recieveBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), null); + + return true; + } + + public void ClientFinishedConnect() + { + if(onConnectedCB != null) + onConnectedCB.Invoke(); + } + + public void ReleaseServer() + { + _server = null; + } + + public void Disconnect() + { + try + { + if (_clientSocket.Connected) + { + _clientSocket.Shutdown(SocketShutdown.Both); + + if (!IsDropped()) + { + _clientSocket.Disconnect(true); + } + } + } + catch(SocketException e) + { + Debug.Log(e.ToString()); + } + finally + { + _clientSocket.Close(); + } + + if (_server != null) + _server.RemoveClient(this); + } + + void OnDestroy() + { + Disconnect(); + } + + public bool IsDropped() + { + return dropped; + } + + public void Update() + { + // Update our drop detection... + if(_clientSocket != null && !IsDropped()) + { + time_check_dropped += Time.deltaTime; + + if(!_clientSocket.Connected) + { + dropped = true; + } + else if(time_check_dropped > time_check_dropped_freq) + { + time_check_dropped = 0.0f; + + try + { + // this is the minimal form of message for a JsonTCPClient + string msg = "{}\n"; + System.Text.Encoding encoding = System.Text.Encoding.Default; + _clientSocket.Send(encoding.GetBytes(msg)); + } + catch(SocketException e) + { + Debug.LogWarning("connection dropped."); + dropped = true; + } + } + } + } + + private void ReceiveCallback(IAsyncResult AR) + { + //Check how much bytes are recieved and call EndRecieve to finalize handshake + int recieved = 0; + + try + { + recieved = _clientSocket.EndReceive(AR); + } + catch(SocketException e) + { + recieved = 0; + dropped = true; + Debug.LogWarning("Exception on recv. Connection dropped."); + } + + + if (recieved <= 0) + return; + + //Copy the recieved data into new buffer , to avoid null bytes + byte[] recData = new byte[recieved]; + Buffer.BlockCopy(_recieveBuffer, 0, recData, 0, recieved); + + if (debug) + { + Debug.Log("recv:" + System.Text.Encoding.Default.GetString(recData)); + } + + //Process data here the way you want , all your bytes will be stored in recData + if (onDataRecvCB != null) + onDataRecvCB.Invoke(recData); + + // Reset our drop connection test timer, since we just got data. + time_check_dropped = 0.0f; + + //Start receiving again + _clientSocket.BeginReceive(_recieveBuffer, 0, _recieveBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), null); + } + + public bool SendData(byte[] data) + { + if (!_clientSocket.Connected) + return false; + + SocketAsyncEventArgs socketAsyncData = new SocketAsyncEventArgs(); + socketAsyncData.SetBuffer(data, 0, data.Length); + _clientSocket.SendAsync(socketAsyncData); + + if (debug) + { + Debug.Log("sent:" + System.Text.Encoding.Default.GetString(data)); + } + + return true; + } + + public bool SendDataToPeers(byte[] data) + { + if (!_clientSocket.Connected || _server == null) + return false; + + _server.SendData(data, this); + return true; + } + + public void SetDebug(bool _debug) + { + debug = _debug; + } + + } + +} //end namepace tk diff --git a/Scripts/tcp/TcpClient.cs.meta b/Scripts/tcp/TcpClient.cs.meta new file mode 100755 index 0000000..4d09a59 --- /dev/null +++ b/Scripts/tcp/TcpClient.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f2066484c9a154edb98cdcba85ce8cee +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/tcp/TcpMenuHandler.cs b/Scripts/tcp/TcpMenuHandler.cs new file mode 100755 index 0000000..5867bd0 --- /dev/null +++ b/Scripts/tcp/TcpMenuHandler.cs @@ -0,0 +1,174 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; +using UnityEngine.SceneManagement; +using System.Net; +using System.Net.Sockets; +using System; + +namespace tk +{ + + public class TcpMenuHandler : MonoBehaviour + { + + public List scene_names = new List(); + public SceneLoader loader; + public GameObject ButtonGridLayout; + public GameObject ButtonPrefab; + private tk.JsonTcpClient client; + private string[] bundleAssetScenePaths; + + public void Init(tk.JsonTcpClient _client) + { + _client.dispatchInMainThread = true; + + client = _client; + client.dispatcher.Register("load_scene", new tk.Delegates.OnMsgRecv(OnLoadScene)); + client.dispatcher.Register("get_protocol_version", new tk.Delegates.OnMsgRecv(OnProtocolVersion)); + client.dispatcher.Register("get_scene_names", new tk.Delegates.OnMsgRecv(OnGetSceneNames)); + client.dispatcher.Register("quit_app", new tk.Delegates.OnMsgRecv(OnQuitApp)); + client.dispatcher.Register("connected", new tk.Delegates.OnMsgRecv(OnConnected)); + } + + public void Start() + { + if (GlobalState.sceneNames == null) + { + try + { + bundleAssetScenePaths = loader.LoadScenePathsFromFile(GlobalState.additionnalContentPath); + if (bundleAssetScenePaths != null) // Add those paths to the scene names + { + scene_names.AddRange(bundleAssetScenePaths); + } + + } + catch (Exception e) { Debug.LogError(e); } + + GlobalState.sceneNames = scene_names.ToArray(); + + } + + foreach (string scene_name in GlobalState.sceneNames) + { + AddButtonToMenu(scene_name); + } + } + + public void OnDestroy() + { + if (client) + client.dispatcher.Reset(); + } + + void Disconnect() + { + client.Disconnect(); + } + + void OnProtocolVersion(JSONObject msg) + { + JSONObject json = new JSONObject(JSONObject.Type.OBJECT); + json.AddField("msg_type", "protocol_version"); + json.AddField("version", "2"); + + client.SendMsg(json); + } + + void OnConnected(JSONObject msg) + { + SendFELoaded(); + } + + private void SendFELoaded() + { + JSONObject json = new JSONObject(JSONObject.Type.OBJECT); + json.AddField("msg_type", "scene_selection_ready"); + json.AddField("loaded", "1"); + + client.SendMsg(json); + } + + void OnGetSceneNames(JSONObject jsonObject) + { + SendSceneNames(); + } + + private void SendSceneNames() + { + JSONObject scenes = new JSONObject(JSONObject.Type.ARRAY); + + foreach (string scene_name in scene_names) + { + scenes.Add(scene_name); + } + + JSONObject json = new JSONObject(JSONObject.Type.OBJECT); + json.AddField("scene_names", scenes); + json.AddField("msg_type", "scene_names"); + + client.SendMsg(json); + } + + void OnLoadScene(JSONObject jsonObject) + { + GlobalState.bAutoHideSceneMenu = true; + + // since we know this is called only from a network client, + // we can also infer that we don't want to auto create + GlobalState.bCreateCarWithoutNetworkClient = false; + + string scene_name = jsonObject.GetField("scene_name").str; + LoadScene(scene_name); + } + + public void LoadScene(string scene_name) + { + // check wether the scene_name is in the scene_names list, if so, load it + if (Array.Exists(GlobalState.sceneNames, element => element == scene_name)) + { + loader.LoadScene(scene_name); + Debug.Log("loaded scene"); + } + } + + void OnQuitApp(JSONObject json) + { + Application.Quit(); + } + + void AddButtonToMenu(string scene_path) + { + string[] split_scene_name = scene_path.Split('/'); + split_scene_name = split_scene_name[split_scene_name.Length - 1].Split('.'); + string scene_name = split_scene_name[0]; // get the scene name (last part of the path and remove the .unity extension) + + // create a new button and add it to the grid layout + GameObject go = Instantiate(ButtonPrefab); + + go.name = scene_name; + go.transform.SetParent(ButtonGridLayout.transform); + go.transform.localScale = Vector3.one; + + // add a function to be called when the button is clicked + Button button = go.GetComponent