diff --git a/.yamato/com.unity.ml-agents-test.yml b/.yamato/com.unity.ml-agents-test.yml index c223ccf78d..903f5429de 100644 --- a/.yamato/com.unity.ml-agents-test.yml +++ b/.yamato/com.unity.ml-agents-test.yml @@ -39,7 +39,7 @@ packages: assembly: Unity.ML-Agents minCoveragePct: 72 - name: com.unity.ml-agents.extensions - assembly: Unity.ML-Agents.Extensions + assembly: Unity.ML-Agents.Extensions* minCoveragePct: 75 --- diff --git a/DevProject/DevProject.sln.DotSettings b/DevProject/DevProject.sln.DotSettings new file mode 100644 index 0000000000..9832e7c315 --- /dev/null +++ b/DevProject/DevProject.sln.DotSettings @@ -0,0 +1,2 @@ + + RL \ No newline at end of file diff --git a/DevProject/Packages/manifest.json b/DevProject/Packages/manifest.json index 831264513d..2ed0133f56 100644 --- a/DevProject/Packages/manifest.json +++ b/DevProject/Packages/manifest.json @@ -2,25 +2,26 @@ "dependencies": { "com.unity.2d.sprite": "1.0.0", "com.unity.2d.tilemap": "1.0.0", - "com.unity.ads": "3.4.9", + "com.unity.ads": "3.6.1", "com.unity.analytics": "3.3.5", "com.unity.coding": "0.1.0-preview.13", "com.unity.collab-proxy": "1.2.16", "com.unity.ide.rider": "1.1.4", - "com.unity.ide.vscode": "1.2.1", + "com.unity.ide.vscode": "1.2.3", + "com.unity.inputsystem": "1.1.0-preview.3", "com.unity.ml-agents": "file:../../com.unity.ml-agents", "com.unity.ml-agents.extensions": "file:../../com.unity.ml-agents.extensions", - "com.unity.multiplayer-hlapi": "1.0.6", + "com.unity.multiplayer-hlapi": "1.0.8", "com.unity.package-manager-doctools": "1.7.0-preview", "com.unity.package-validation-suite": "0.19.0-preview", - "com.unity.purchasing": "2.1.0", - "com.unity.test-framework": "1.1.16", + "com.unity.purchasing": "2.2.1", + "com.unity.test-framework": "1.1.20", "com.unity.test-framework.performance": "2.2.0-preview", "com.unity.testtools.codecoverage": "1.0.0-pre.3", "com.unity.textmeshpro": "2.0.1", "com.unity.timeline": "1.2.12", "com.unity.ugui": "1.0.0", - "com.unity.xr.legacyinputhelpers": "2.1.4", + "com.unity.xr.legacyinputhelpers": "2.1.7", "com.unity.modules.ai": "1.0.0", "com.unity.modules.androidjni": "1.0.0", "com.unity.modules.animation": "1.0.0", @@ -56,6 +57,7 @@ "registry": "https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-candidates", "testables": [ "com.unity.ml-agents", - "com.unity.ml-agents.extensions" + "com.unity.ml-agents.extensions", + "com.unity.inputsystem" ] } diff --git a/DevProject/Packages/packages-lock.json b/DevProject/Packages/packages-lock.json new file mode 100644 index 0000000000..3ceb180eeb --- /dev/null +++ b/DevProject/Packages/packages-lock.json @@ -0,0 +1,519 @@ +{ + "dependencies": { + "com.unity.2d.sprite": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.2d.tilemap": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.ads": { + "version": "3.6.1", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.ugui": "1.0.0" + }, + "url": "https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-candidates" + }, + "com.unity.analytics": { + "version": "3.3.5", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.ugui": "1.0.0" + }, + "url": "https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-candidates" + }, + "com.unity.barracuda": { + "version": "1.3.0-preview", + "depth": 1, + "source": "registry", + "dependencies": { + "com.unity.burst": "1.3.4", + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.imageconversion": "1.0.0" + }, + "url": "https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-candidates" + }, + "com.unity.burst": { + "version": "1.3.4", + "depth": 2, + "source": "registry", + "dependencies": { + "com.unity.mathematics": "1.2.1" + }, + "url": "https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-candidates" + }, + "com.unity.coding": { + "version": "0.1.0-preview.13", + "depth": 0, + "source": "registry", + "dependencies": { + "nuget.moq": "1.0.0" + }, + "url": "https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-candidates" + }, + "com.unity.collab-proxy": { + "version": "1.2.16", + "depth": 0, + "source": "registry", + "dependencies": {}, + "url": "https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-candidates" + }, + "com.unity.ext.nunit": { + "version": "1.0.6", + "depth": 1, + "source": "registry", + "dependencies": {}, + "url": "https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-candidates" + }, + "com.unity.ide.rider": { + "version": "1.1.4", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.test-framework": "1.1.1" + }, + "url": "https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-candidates" + }, + "com.unity.ide.vscode": { + "version": "1.2.3", + "depth": 0, + "source": "registry", + "dependencies": {}, + "url": "https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-candidates" + }, + "com.unity.inputsystem": { + "version": "1.1.0-preview.3", + "depth": 0, + "source": "registry", + "dependencies": {}, + "url": "https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-candidates" + }, + "com.unity.mathematics": { + "version": "1.2.1", + "depth": 3, + "source": "registry", + "dependencies": {}, + "url": "https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-candidates" + }, + "com.unity.ml-agents": { + "version": "file:../../com.unity.ml-agents", + "depth": 0, + "source": "local", + "dependencies": { + "com.unity.barracuda": "1.3.0-preview", + "com.unity.modules.imageconversion": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.physics2d": "1.0.0", + "com.unity.modules.unityanalytics": "1.0.0" + } + }, + "com.unity.ml-agents.extensions": { + "version": "file:../../com.unity.ml-agents.extensions", + "depth": 0, + "source": "local", + "dependencies": { + "com.unity.ml-agents": "1.7.2-preview" + } + }, + "com.unity.multiplayer-hlapi": { + "version": "1.0.8", + "depth": 0, + "source": "registry", + "dependencies": { + "nuget.mono-cecil": "0.1.6-preview" + }, + "url": "https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-candidates" + }, + "com.unity.nuget.mono-cecil": { + "version": "0.1.6-preview.2", + "depth": 1, + "source": "registry", + "dependencies": { + "nuget.mono-cecil": "0.1.6-preview" + }, + "url": "https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-candidates" + }, + "com.unity.nuget.newtonsoft-json": { + "version": "2.0.0-preview", + "depth": 1, + "source": "registry", + "dependencies": {}, + "url": "https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-candidates" + }, + "com.unity.package-manager-doctools": { + "version": "1.7.0-preview", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.package-validation-suite": "0.10.0-preview", + "com.unity.nuget.newtonsoft-json": "2.0.0-preview" + }, + "url": "https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-candidates" + }, + "com.unity.package-validation-suite": { + "version": "0.19.0-preview", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.nuget.mono-cecil": "0.1.6-preview.2" + }, + "url": "https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-candidates" + }, + "com.unity.purchasing": { + "version": "2.2.1", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.ugui": "1.0.0" + }, + "url": "https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-candidates" + }, + "com.unity.settings-manager": { + "version": "1.0.1", + "depth": 1, + "source": "registry", + "dependencies": {}, + "url": "https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-candidates" + }, + "com.unity.test-framework": { + "version": "1.1.20", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.ext.nunit": "1.0.6", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0" + }, + "url": "https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-candidates" + }, + "com.unity.test-framework.performance": { + "version": "2.2.0-preview", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.test-framework": "1.1.0", + "com.unity.nuget.newtonsoft-json": "2.0.0-preview" + }, + "url": "https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-candidates" + }, + "com.unity.testtools.codecoverage": { + "version": "1.0.0-pre.3", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.test-framework": "1.0.16", + "com.unity.settings-manager": "1.0.1" + }, + "url": "https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-candidates" + }, + "com.unity.textmeshpro": { + "version": "2.0.1", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.ugui": "1.0.0" + }, + "url": "https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-candidates" + }, + "com.unity.timeline": { + "version": "1.2.12", + "depth": 0, + "source": "registry", + "dependencies": {}, + "url": "https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-candidates" + }, + "com.unity.ugui": { + "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.xr.legacyinputhelpers": { + "version": "2.1.7", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.modules.vr": "1.0.0", + "com.unity.modules.xr": "1.0.0" + }, + "url": "https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-candidates" + }, + "nuget.castle-core": { + "version": "1.0.1", + "depth": 2, + "source": "registry", + "dependencies": {}, + "url": "https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-candidates" + }, + "nuget.mono-cecil": { + "version": "0.1.6-preview", + "depth": 1, + "source": "registry", + "dependencies": {}, + "url": "https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-candidates" + }, + "nuget.moq": { + "version": "1.0.0", + "depth": 1, + "source": "registry", + "dependencies": { + "nuget.castle-core": "1.0.1" + }, + "url": "https://artifactory.prd.cds.internal.unity3d.com/artifactory/api/npm/upm-candidates" + }, + "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.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.imgui": "1.0.0", + "com.unity.modules.jsonserialize": "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.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/DevProject/ProjectSettings/Packages/com.unity.testtools.codecoverage/Settings.json b/DevProject/ProjectSettings/Packages/com.unity.testtools.codecoverage/Settings.json new file mode 100644 index 0000000000..ad11087f42 --- /dev/null +++ b/DevProject/ProjectSettings/Packages/com.unity.testtools.codecoverage/Settings.json @@ -0,0 +1,7 @@ +{ + "m_Name": "Settings", + "m_Path": "ProjectSettings/Packages/com.unity.testtools.codecoverage/Settings.json", + "m_Dictionary": { + "m_DictionaryValues": [] + } +} \ No newline at end of file diff --git a/DevProject/ProjectSettings/ProjectSettings.asset b/DevProject/ProjectSettings/ProjectSettings.asset index 86c7b211b5..9370d8272f 100644 --- a/DevProject/ProjectSettings/ProjectSettings.asset +++ b/DevProject/ProjectSettings/ProjectSettings.asset @@ -68,7 +68,7 @@ PlayerSettings: androidBlitType: 0 defaultIsNativeResolution: 1 macRetinaSupport: 1 - runInBackground: 0 + runInBackground: 1 captureSingleScreen: 0 muteOtherAudioSources: 0 Prepare IOS For Recording: 0 @@ -103,6 +103,7 @@ PlayerSettings: xboxOneMonoLoggingLevel: 0 xboxOneLoggingLevel: 1 xboxOneDisableEsram: 0 + xboxOneEnableTypeOptimization: 0 xboxOnePresentImmediateThreshold: 0 switchQueueCommandMemory: 1048576 switchQueueControlMemory: 16384 @@ -110,8 +111,13 @@ PlayerSettings: switchNVNShaderPoolsGranularity: 33554432 switchNVNDefaultPoolsGranularity: 16777216 switchNVNOtherPoolsGranularity: 16777216 + switchNVNMaxPublicTextureIDCount: 0 + switchNVNMaxPublicSamplerIDCount: 0 + stadiaPresentMode: 0 + stadiaTargetFramerate: 0 vulkanNumSwapchainBuffers: 3 vulkanEnableSetSRGBWrite: 0 + vulkanEnableLateAcquireNextImage: 0 m_SupportedAspectRatios: 4:3: 1 5:4: 1 @@ -157,7 +163,7 @@ PlayerSettings: useHDRDisplay: 0 D3DHDRBitDepth: 0 m_ColorGamuts: 00000000 - targetPixelDensity: 0 + targetPixelDensity: 30 resolutionScalingMode: 0 androidSupportedAspectRatio: 1 androidMaxAspectRatio: 2.1 @@ -179,32 +185,16 @@ PlayerSettings: StripUnusedMeshComponents: 0 VertexChannelCompressionMask: 4054 iPhoneSdkVersion: 988 - iOSTargetOSVersionString: + iOSTargetOSVersionString: 10.0 tvOSSdkVersion: 0 tvOSRequireExtendedGameController: 0 - tvOSTargetOSVersionString: + tvOSTargetOSVersionString: 10.0 uIPrerenderedIcon: 0 uIRequiresPersistentWiFi: 0 uIRequiresFullScreen: 1 uIStatusBarHidden: 1 uIExitOnSuspend: 0 uIStatusBarStyle: 0 - iPhoneSplashScreen: {fileID: 0} - iPhoneHighResSplashScreen: {fileID: 0} - iPhoneTallHighResSplashScreen: {fileID: 0} - iPhone47inSplashScreen: {fileID: 0} - iPhone55inPortraitSplashScreen: {fileID: 0} - iPhone55inLandscapeSplashScreen: {fileID: 0} - iPhone58inPortraitSplashScreen: {fileID: 0} - iPhone58inLandscapeSplashScreen: {fileID: 0} - iPadPortraitSplashScreen: {fileID: 0} - iPadHighResPortraitSplashScreen: {fileID: 0} - iPadLandscapeSplashScreen: {fileID: 0} - iPadHighResLandscapeSplashScreen: {fileID: 0} - iPhone65inPortraitSplashScreen: {fileID: 0} - iPhone65inLandscapeSplashScreen: {fileID: 0} - iPhone61inPortraitSplashScreen: {fileID: 0} - iPhone61inLandscapeSplashScreen: {fileID: 0} appleTVSplashScreen: {fileID: 0} appleTVSplashScreen2x: {fileID: 0} tvOSSmallIconLayers: [] @@ -241,6 +231,7 @@ PlayerSettings: metalEditorSupport: 1 metalAPIValidation: 1 iOSRenderExtraFrameOnPause: 0 + iosCopyPluginsCodeInsteadOfSymlink: 0 appleDeveloperTeamID: iOSManualSigningProvisioningProfileID: tvOSManualSigningProvisioningProfileID: @@ -454,6 +445,7 @@ PlayerSettings: ps4ShareFilePath: ps4ShareOverlayImagePath: ps4PrivacyGuardImagePath: + ps4ExtraSceSysFile: ps4NPtitleDatPath: ps4RemotePlayKeyAssignment: -1 ps4RemotePlayKeyMappingDir: @@ -479,6 +471,7 @@ PlayerSettings: ps4UseResolutionFallback: 0 ps4ReprojectionSupport: 0 ps4UseAudio3dBackend: 0 + ps4UseLowGarlicFragmentationMode: 1 ps4SocialScreenEnabled: 0 ps4ScriptOptimizationLevel: 2 ps4Audio3dVirtualSpeakerCount: 14 @@ -495,6 +488,8 @@ PlayerSettings: ps4disableAutoHideSplash: 0 ps4videoRecordingFeaturesUsed: 0 ps4contentSearchFeaturesUsed: 0 + ps4CompatibilityPS5: 0 + ps4GPU800MHz: 1 ps4attribEyeToEyeDistanceSettingVR: 0 ps4IncludedModules: [] ps4attribVROutputEnabled: 0 @@ -527,6 +522,7 @@ PlayerSettings: additionalIl2CppArgs: scriptingRuntimeVersion: 1 gcIncremental: 0 + assemblyVersionValidation: 1 gcWBarrierValidation: 0 apiCompatibilityLevelPerPlatform: {} m_RenderingPath: 1 @@ -576,6 +572,7 @@ PlayerSettings: XboxOneCapability: [] XboxOneGameRating: {} XboxOneIsContentPackage: 0 + XboxOneEnhancedXboxCompatibilityMode: 0 XboxOneEnableGPUVariability: 1 XboxOneSockets: {} XboxOneSplashScreen: {fileID: 0} @@ -583,6 +580,7 @@ PlayerSettings: XboxOnePersistentLocalStorageSize: 0 XboxOneXTitleMemory: 8 XboxOneOverrideIdentityName: + XboxOneOverrideIdentityPublisher: vrEditorSettings: daydream: daydreamIconForeground: {fileID: 0} @@ -605,6 +603,6 @@ PlayerSettings: projectName: organizationId: cloudEnabled: 0 - enableNativePlatformBackendsForNewInputSystem: 0 - disableOldInputManagerSupport: 0 + enableNativePlatformBackendsForNewInputSystem: 1 + disableOldInputManagerSupport: 1 legacyClampBlendShapeWeights: 0 diff --git a/DevProject/ProjectSettings/ProjectVersion.txt b/DevProject/ProjectSettings/ProjectVersion.txt index 9eeca0e22f..acbe3fd398 100644 --- a/DevProject/ProjectSettings/ProjectVersion.txt +++ b/DevProject/ProjectSettings/ProjectVersion.txt @@ -1,2 +1,2 @@ -m_EditorVersion: 2019.4.7f1 -m_EditorVersionWithRevision: 2019.4.7f1 (e992b1a16e65) +m_EditorVersion: 2019.4.19f1 +m_EditorVersionWithRevision: 2019.4.19f1 (ca5b14067cec) diff --git a/com.unity.ml-agents.extensions/Documentation~/InputActuatorComponent.md b/com.unity.ml-agents.extensions/Documentation~/InputActuatorComponent.md new file mode 100644 index 0000000000..b72fb3fd8c --- /dev/null +++ b/com.unity.ml-agents.extensions/Documentation~/InputActuatorComponent.md @@ -0,0 +1,57 @@ +# Integration of the Input System Package with ML-Agents + +## Overview +One area we are always trying to improve is getting developers up and running with ML-Agents. With this in mind, +we have implemented an `InputActuatorComponent`. This component integrates with the +[Input System Package](https://docs.unity3d.com/Packages/com.unity.inputsystem@1.1/manual/QuickStartGuide.html) +to set up an action space for your `Agent` based on an `InputActionAsset` that is referenced by the +`IInputActionAssetProvider` interface, or the `PlayerInput` component that may be living on your player controlled +`Agent`. This means that if you have code outside of your agent that handles input, you will not need to implement +the Heuristic function in agent as well. The `InputActuatorComponent` will handle this for you. You can now train and +run inference on `Agents` with an action space defined by an `InputActionAsset`. + +This implementation includes: + +* C# `InputActuatorComponent` you can attach to your Agent. +* Implement the `IInputActionAssetProvider` in the `Componenet` where you handle player input. +* An example environment where the input handling code is not in the Heuristic function of the Agent subclass. + +### Feedback +We have only implemented a small subset of `InputControl` types that we thought would cover a large portion of what +most developers would use. Please let us know if you want more control types implemented by posting in the [ML-Agents +forum.](https://forum.unity.com/forums/ml-agents.453/) + +We would also like your feedback on the workflow of integrating this into your games. If you run +into workflow issues please let us know in the ML-Agents forums, or if you've discovered a bug, +please file a bug on our GitHub page. + +## Getting started +The C# code for the `InputActuatorComponent` exists inside of the extensions package (com.unity.ml-agents.extensions). A good first step would be to familiarize with the extensions package by reading the document [here](com.unity.ml-agents.extensions.md). The second step would be to take a look at how we have implemented the C# code in the example Input Integration scene (located under ML-Agents-Input-Example/Assets/ML-Agents/Examples/PushBlock/). Once you have some familiarity, then the next step would be to add the InputActuatorComponent to your player Agent. The example we have implemented uses C# Events to send information from the Input System. + +Additionally, see below for additional technical specifications on the C# code for the InputActuatorComponent. + +## Technical specifications for the InputActuatorComponent + +### `IInputActionsAssetProvider` Interface +The `InputActuatorComponent` searches for a `Component` that implements +`IInputActionAssetProvider` on the `GameObject` they both are attached to. It is important to note +that if multiple `Components` on your `GameObject` need to access an `InputActionAsset` to handle events, +they will need to share the same instance of the `InputActionAsset` that is returned from the +`IInputActionAssetProvider`. + +### `InputActuatorComponent` class +The `InputActuatorComponent` is the bridge between ML-Agents and the Input System.. It allows ML-Agents to +* create an `ActionSpec` for your Agent based on an `InputActionAsset` that comes from an +`IInputActionAssetProvider`. +* send simulated input from a training process or a neural network +* let developers keep their input handling code in one place + +This is accomplished by adding the `InputActuatorComponenet` to an Agent which already has the PlayerInput component attached. + +### Setting up a scene using the `InputActuatorComponent` +1. Add the `com.unity.inputsystem` version 1.1.0-preview.3 or later to your project via the Package Manager window. +2. If you have already setup an InputActionAsset skip to Step 3, otherwise follow these sub steps: + 1. Create an InputActionAsset to allow your Agent to be controlled by the Input System. + 2. Handle the events from the Input System where you normally would (i.e. a script external to your Agent class). +3. Add the InputSystemActuatorComponent to the GameObject that has the `PlayerInput` and `Agent` components attached. + diff --git a/com.unity.ml-agents.extensions/Documentation~/com.unity.ml-agents.extensions.md b/com.unity.ml-agents.extensions/Documentation~/com.unity.ml-agents.extensions.md index d732cbe5e4..44f9f8f953 100644 --- a/com.unity.ml-agents.extensions/Documentation~/com.unity.ml-agents.extensions.md +++ b/com.unity.ml-agents.extensions/Documentation~/com.unity.ml-agents.extensions.md @@ -22,6 +22,7 @@ The Runtime directory currently contains three features: * [Match-3 sensor and actuator](Match3.md) * [Grid-based sensor](Grid-Sensor.md) * Physics-based sensors + * [Input System Package Integration](InputActuatorComponent.md) ## Installation The ML-Agents Extensions package is not currently available in the Package Manager. There are two @@ -54,10 +55,15 @@ See [Git dependencies](https://docs.unity3d.com/Manual/upm-git.html#subfolder) f This version of the Unity ML-Agents Extensions package is compatible with the following versions of the Unity Editor: -- 2018.4 and later +- If using the `InputActuatorComponent` + - 2019.4 or later + - install the `com.unity.inputsystem` package version `1.1.0-preview.3` or later. +- Else 2018.4 and later ## Known Limitations -none +- For the `InputActuatorComponent` + - Limited implementation of `InputControls` + - No way to customize the action space of the `InputActuatorComponent` ## Need Help? The main [README](https://github.com/Unity-Technologies/ml-agents/tree/release_12_docs/README.md) contains links for contacting the team or getting support. diff --git a/com.unity.ml-agents.extensions/Editor/Input.meta b/com.unity.ml-agents.extensions/Editor/Input.meta new file mode 100644 index 0000000000..2b437a39f8 --- /dev/null +++ b/com.unity.ml-agents.extensions/Editor/Input.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1f773a20e85042999e87f5d3c7b55281 +timeCreated: 1613637190 \ No newline at end of file diff --git a/com.unity.ml-agents.extensions/Editor/Input/InputActuatorComponentEditor.cs b/com.unity.ml-agents.extensions/Editor/Input/InputActuatorComponentEditor.cs new file mode 100644 index 0000000000..c06ae75d1b --- /dev/null +++ b/com.unity.ml-agents.extensions/Editor/Input/InputActuatorComponentEditor.cs @@ -0,0 +1,26 @@ +#if MLA_INPUT_SYSTEM && UNITY_2019_4_OR_NEWER +using Unity.MLAgents.Extensions.Input; +using UnityEditor; + +namespace Unity.MLAgents.Extensions.Editor.Input +{ + [CustomEditor(typeof(InputActuatorComponent))] + internal class InputActuatorComponentEditor : UnityEditor.Editor + { + const string k_ActionSpecName = "m_ActionSpec"; + + public override void OnInspectorGUI() + { + var so = serializedObject; + so.Update(); + InputActuatorComponent o = so.targetObject as InputActuatorComponent; + _ = o.ActionSpec; + EditorGUI.indentLevel++; + EditorGUI.BeginDisabledGroup(true); + EditorGUILayout.PropertyField(so.FindProperty(k_ActionSpecName)); + EditorGUI.EndDisabledGroup(); + EditorGUI.indentLevel--; + } + } +} +#endif // MLA_INPUT_SYSTEM && UNITY_2019_OR_NEWER diff --git a/com.unity.ml-agents.extensions/Editor/Input/InputActuatorComponentEditor.cs.meta b/com.unity.ml-agents.extensions/Editor/Input/InputActuatorComponentEditor.cs.meta new file mode 100644 index 0000000000..7fe63a5420 --- /dev/null +++ b/com.unity.ml-agents.extensions/Editor/Input/InputActuatorComponentEditor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3435eeef4d1645be8c0f770b68f2ba19 +timeCreated: 1613637202 \ No newline at end of file diff --git a/com.unity.ml-agents.extensions/Editor/Input/Unity.ML-Agents.Extensions.Editor.Input.asmdef b/com.unity.ml-agents.extensions/Editor/Input/Unity.ML-Agents.Extensions.Editor.Input.asmdef new file mode 100644 index 0000000000..162bac2252 --- /dev/null +++ b/com.unity.ml-agents.extensions/Editor/Input/Unity.ML-Agents.Extensions.Editor.Input.asmdef @@ -0,0 +1,26 @@ +{ + "name": "Unity.ML-Agents.Extensions.Editor.Input", + "references": [ + "Unity.ML-Agents", + "Unity.ML-Agents.Extensions.Input", + "Unity.ML-Agents.Editor", + "Unity.InputSystem" + ], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [ + { + "name": "com.unity.inputsystem", + "expression": "1.1.0-preview", + "define": "MLA_INPUT_SYSTEM" + } + ], + "noEngineReferences": false +} diff --git a/com.unity.ml-agents.extensions/Editor/Input/Unity.ML-Agents.Extensions.Editor.Input.asmdef.meta b/com.unity.ml-agents.extensions/Editor/Input/Unity.ML-Agents.Extensions.Editor.Input.asmdef.meta new file mode 100644 index 0000000000..0d252ad175 --- /dev/null +++ b/com.unity.ml-agents.extensions/Editor/Input/Unity.ML-Agents.Extensions.Editor.Input.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 4851f2d02f9f1423a8593f60b1a9cd7e +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.ml-agents.extensions/Runtime/Input.meta b/com.unity.ml-agents.extensions/Runtime/Input.meta new file mode 100644 index 0000000000..6d9bda962b --- /dev/null +++ b/com.unity.ml-agents.extensions/Runtime/Input.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1694e881b9ec420ba1c201f0612392d6 +timeCreated: 1610754907 \ No newline at end of file diff --git a/com.unity.ml-agents.extensions/Runtime/Input/Adaptors.meta b/com.unity.ml-agents.extensions/Runtime/Input/Adaptors.meta new file mode 100644 index 0000000000..f75266d5b7 --- /dev/null +++ b/com.unity.ml-agents.extensions/Runtime/Input/Adaptors.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: de3fc3f4fd664e3ab579a102f7fabc88 +timeCreated: 1612204931 \ No newline at end of file diff --git a/com.unity.ml-agents.extensions/Runtime/Input/Adaptors/ButtonInputActionAdaptor.cs b/com.unity.ml-agents.extensions/Runtime/Input/Adaptors/ButtonInputActionAdaptor.cs new file mode 100644 index 0000000000..46cc297ae7 --- /dev/null +++ b/com.unity.ml-agents.extensions/Runtime/Input/Adaptors/ButtonInputActionAdaptor.cs @@ -0,0 +1,45 @@ +#if MLA_INPUT_SYSTEM && UNITY_2019_4_OR_NEWER +using Unity.MLAgents.Actuators; +using UnityEngine; +using UnityEngine.InputSystem; +using UnityEngine.InputSystem.LowLevel; + +namespace Unity.MLAgents.Extensions.Input +{ + /// + /// Class that translates data between the a and + /// the ML-Agents object. + /// + public class ButtonInputActionAdaptor : IRLActionInputAdaptor + { + /// + /// TODO this method needs to be more nuanced depending the types of controls that can back it. i.e. TriggerControls + /// are continuous buttons, etc. + /// Currently returns an with 1 branch of size 2. One value for not pressed, and one + /// for pressed. + /// + /// The action associated with this adaptor to help determine the action space. + /// + public ActionSpec GetActionSpecForInputAction(InputAction action) + { + return ActionSpec.MakeDiscrete(2); + } + + /// TODO again this might need to be more nuanced for things like continuous buttons. + /// + public void QueueInputEventForAction(InputAction action, InputControl control, ActionSpec actionSpec, in ActionBuffers actionBuffers) + { + var val = actionBuffers.DiscreteActions[0]; + InputSystem.QueueDeltaStateEvent(control, (byte)val); + } + + /// > + public void WriteToHeuristic(InputAction action, in ActionBuffers actionBuffers) + { + var discreteActions = actionBuffers.DiscreteActions; + var val = action.ReadValue(); + discreteActions[0] = (int)val; + } + } +} +#endif // MLA_INPUT_SYSTEM && UNITY_2019_4_OR_NEWER diff --git a/com.unity.ml-agents.extensions/Runtime/Input/Adaptors/ButtonInputActionAdaptor.cs.meta b/com.unity.ml-agents.extensions/Runtime/Input/Adaptors/ButtonInputActionAdaptor.cs.meta new file mode 100644 index 0000000000..68f92bc212 --- /dev/null +++ b/com.unity.ml-agents.extensions/Runtime/Input/Adaptors/ButtonInputActionAdaptor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f3a0fe3f0bd446958c729d6f71e8d00b +timeCreated: 1612373241 \ No newline at end of file diff --git a/com.unity.ml-agents.extensions/Runtime/Input/Adaptors/DoubleInputActionAdaptor.cs b/com.unity.ml-agents.extensions/Runtime/Input/Adaptors/DoubleInputActionAdaptor.cs new file mode 100644 index 0000000000..9b2f58e74a --- /dev/null +++ b/com.unity.ml-agents.extensions/Runtime/Input/Adaptors/DoubleInputActionAdaptor.cs @@ -0,0 +1,35 @@ +#if MLA_INPUT_SYSTEM && UNITY_2019_4_OR_NEWER +using Unity.MLAgents.Actuators; +using UnityEngine.InputSystem; +using UnityEngine.InputSystem.LowLevel; + +namespace Unity.MLAgents.Extensions.Input +{ + /// + /// Translates data from a . + /// + public class DoubleInputActionAdaptor : IRLActionInputAdaptor + { + /// + public ActionSpec GetActionSpecForInputAction(InputAction action) + { + return ActionSpec.MakeContinuous(1); + } + + /// + public void QueueInputEventForAction(InputAction action, InputControl control, ActionSpec actionSpec, in ActionBuffers actionBuffers) + { + var val = actionBuffers.ContinuousActions[0]; + InputSystem.QueueDeltaStateEvent(control,(double)val); + } + + /// + public void WriteToHeuristic(InputAction action, in ActionBuffers actionBuffers) + { + var actions = actionBuffers.ContinuousActions; + var val = (float)action.ReadValue(); + actions[0] = val; + } + } +} +#endif // MLA_INPUT_SYSTEM && UNITY_2019_4_OR_NEWER diff --git a/com.unity.ml-agents.extensions/Runtime/Input/Adaptors/DoubleInputActionAdaptor.cs.meta b/com.unity.ml-agents.extensions/Runtime/Input/Adaptors/DoubleInputActionAdaptor.cs.meta new file mode 100644 index 0000000000..ced0139369 --- /dev/null +++ b/com.unity.ml-agents.extensions/Runtime/Input/Adaptors/DoubleInputActionAdaptor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 14ec2823be0a4b3bbee5490ed4840e9c +timeCreated: 1612574422 \ No newline at end of file diff --git a/com.unity.ml-agents.extensions/Runtime/Input/Adaptors/FloatInputActionAdaptor.cs b/com.unity.ml-agents.extensions/Runtime/Input/Adaptors/FloatInputActionAdaptor.cs new file mode 100644 index 0000000000..c39960cb12 --- /dev/null +++ b/com.unity.ml-agents.extensions/Runtime/Input/Adaptors/FloatInputActionAdaptor.cs @@ -0,0 +1,35 @@ +#if MLA_INPUT_SYSTEM && UNITY_2019_4_OR_NEWER +using Unity.MLAgents.Actuators; +using UnityEngine.InputSystem; +using UnityEngine.InputSystem.LowLevel; + +namespace Unity.MLAgents.Extensions.Input +{ + /// + /// Translates data from any control that extends from . + /// + public class FloatInputActionAdaptor : IRLActionInputAdaptor + { + /// + public ActionSpec GetActionSpecForInputAction(InputAction action) + { + return ActionSpec.MakeContinuous(1); + } + + /// + public void QueueInputEventForAction(InputAction action, InputControl control, ActionSpec actionSpec, in ActionBuffers actionBuffers) + { + var val = actionBuffers.ContinuousActions[0]; + InputSystem.QueueDeltaStateEvent(control, val); + } + + /// + public void WriteToHeuristic(InputAction action, in ActionBuffers actionBuffers) + { + var actions = actionBuffers.ContinuousActions; + var val = action.ReadValue(); + actions[0] = val; + } + } +} +#endif // MLA_INPUT_SYSTEM && UNITY_2019_4_OR_NEWER diff --git a/com.unity.ml-agents.extensions/Runtime/Input/Adaptors/FloatInputActionAdaptor.cs.meta b/com.unity.ml-agents.extensions/Runtime/Input/Adaptors/FloatInputActionAdaptor.cs.meta new file mode 100644 index 0000000000..9e12bca95f --- /dev/null +++ b/com.unity.ml-agents.extensions/Runtime/Input/Adaptors/FloatInputActionAdaptor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6c98cc3fdaec4664aae128a05cfe6560 +timeCreated: 1612573580 \ No newline at end of file diff --git a/com.unity.ml-agents.extensions/Runtime/Input/Adaptors/IntegerInputActionAdaptor.cs b/com.unity.ml-agents.extensions/Runtime/Input/Adaptors/IntegerInputActionAdaptor.cs new file mode 100644 index 0000000000..a7501aa987 --- /dev/null +++ b/com.unity.ml-agents.extensions/Runtime/Input/Adaptors/IntegerInputActionAdaptor.cs @@ -0,0 +1,36 @@ +#if MLA_INPUT_SYSTEM && UNITY_2019_4_OR_NEWER +using Unity.MLAgents.Actuators; +using UnityEngine.InputSystem; +using UnityEngine.InputSystem.LowLevel; + +namespace Unity.MLAgents.Extensions.Input +{ + /// + /// Translates data from a . + /// + public class IntegerInputActionAdaptor : IRLActionInputAdaptor + { + // TODO need to figure out how we can infer the branch size from here. + /// + public ActionSpec GetActionSpecForInputAction(InputAction action) + { + return ActionSpec.MakeDiscrete(2); + } + + /// + public void QueueInputEventForAction(InputAction action, InputControl control, ActionSpec actionSpec, in ActionBuffers actionBuffers) + { + var val = actionBuffers.DiscreteActions[0]; + InputSystem.QueueDeltaStateEvent(control, val); + } + + /// + public void WriteToHeuristic(InputAction action, in ActionBuffers actionBuffers) + { + var actions = actionBuffers.DiscreteActions; + var val = action.ReadValue(); + actions[0] = val; + } + } +} +#endif // MLA_INPUT_SYSTEM && UNITY_2019_4_OR_NEWER diff --git a/com.unity.ml-agents.extensions/Runtime/Input/Adaptors/IntegerInputActionAdaptor.cs.meta b/com.unity.ml-agents.extensions/Runtime/Input/Adaptors/IntegerInputActionAdaptor.cs.meta new file mode 100644 index 0000000000..3db5316f10 --- /dev/null +++ b/com.unity.ml-agents.extensions/Runtime/Input/Adaptors/IntegerInputActionAdaptor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 592012f9a30847a29c618f0cf8addfdf +timeCreated: 1612572952 \ No newline at end of file diff --git a/com.unity.ml-agents.extensions/Runtime/Input/Adaptors/Vector2InputActionAdaptor.cs b/com.unity.ml-agents.extensions/Runtime/Input/Adaptors/Vector2InputActionAdaptor.cs new file mode 100644 index 0000000000..9c3e4b2620 --- /dev/null +++ b/com.unity.ml-agents.extensions/Runtime/Input/Adaptors/Vector2InputActionAdaptor.cs @@ -0,0 +1,44 @@ +#if MLA_INPUT_SYSTEM && UNITY_2019_4_OR_NEWER +using System; +using Unity.MLAgents.Actuators; +using UnityEngine; +using UnityEngine.InputSystem; +using UnityEngine.InputSystem.LowLevel; + +namespace Unity.MLAgents.Extensions.Input +{ + /// + /// Translates data from any control that extends from . + /// + public class Vector2InputActionAdaptor : IRLActionInputAdaptor + { + /// + public ActionSpec GetActionSpecForInputAction(InputAction action) + { + // TODO create the action spec based on what controls back the action + return ActionSpec.MakeContinuous(2); + } + + /// + public void QueueInputEventForAction(InputAction action, + InputControl control, + ActionSpec actionSpec, + in ActionBuffers actionBuffers) + { + var x = actionBuffers.ContinuousActions[0]; + var y = actionBuffers.ContinuousActions[1]; + InputSystem.QueueDeltaStateEvent(control, new Vector2(x, y)); + } + + /// + public void WriteToHeuristic(InputAction action, in ActionBuffers actionBuffers) + { + var value = action.ReadValue(); + var continuousActions = actionBuffers.ContinuousActions; + continuousActions[0] = value.x; + continuousActions[1] = value.y; + } + + } +} +#endif // MLA_INPUT_SYSTEM && UNITY_2019_4_OR_NEWER diff --git a/com.unity.ml-agents.extensions/Runtime/Input/Adaptors/Vector2InputActionAdaptor.cs.meta b/com.unity.ml-agents.extensions/Runtime/Input/Adaptors/Vector2InputActionAdaptor.cs.meta new file mode 100644 index 0000000000..7f87c8fefd --- /dev/null +++ b/com.unity.ml-agents.extensions/Runtime/Input/Adaptors/Vector2InputActionAdaptor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: aa311fde3dac44f3b7998bb7fee77225 +timeCreated: 1611356491 \ No newline at end of file diff --git a/com.unity.ml-agents.extensions/Runtime/Input/AssemblyInfo.cs b/com.unity.ml-agents.extensions/Runtime/Input/AssemblyInfo.cs new file mode 100644 index 0000000000..ffd66a0ad7 --- /dev/null +++ b/com.unity.ml-agents.extensions/Runtime/Input/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Unity.ML-Agents.Extensions.Input.Tests.Runtime")] diff --git a/com.unity.ml-agents.extensions/Runtime/Input/AssemblyInfo.cs.meta b/com.unity.ml-agents.extensions/Runtime/Input/AssemblyInfo.cs.meta new file mode 100644 index 0000000000..93ccdcd833 --- /dev/null +++ b/com.unity.ml-agents.extensions/Runtime/Input/AssemblyInfo.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 989e62db4b694586bf2c832ce13e2d50 +timeCreated: 1612916938 \ No newline at end of file diff --git a/com.unity.ml-agents.extensions/Runtime/Input/IInputActionAssetProvider.cs b/com.unity.ml-agents.extensions/Runtime/Input/IInputActionAssetProvider.cs new file mode 100644 index 0000000000..0717d614b6 --- /dev/null +++ b/com.unity.ml-agents.extensions/Runtime/Input/IInputActionAssetProvider.cs @@ -0,0 +1,27 @@ +#if MLA_INPUT_SYSTEM && UNITY_2019_4_OR_NEWER +using UnityEngine.InputSystem; + +namespace Unity.MLAgents.Extensions.Input +{ + /// + /// Implement this interface if you are listening to C# events from the generated C# class from the + /// . This interface works with the in order + /// to allow ML-Agents to simulate input actions based on the instance of the + /// used to listen to events. If you implement this interface the will use + /// what is returned from as the asset to base it's simulated input for. + /// Otherwise, the will look for the component + /// and use the asset from there. If you have multiple components handling PlayerInput on the same GameObject + /// they will need to share the same instance of the in order to get the simulated + /// input. + /// + public interface IInputActionAssetProvider + { + /// + /// Returns the instance being from the generated C# class of the + /// in order to correctly fire events when simulating input from ML-Agents. + /// + /// The instance of the you are listening for events on. + (InputActionAsset, IInputActionCollection2) GetInputActionAsset(); + } +} +#endif // MLA_INPUT_SYSTEM && UNITY_2019_4_OR_NEWER diff --git a/com.unity.ml-agents.extensions/Runtime/Input/IInputActionAssetProvider.cs.meta b/com.unity.ml-agents.extensions/Runtime/Input/IInputActionAssetProvider.cs.meta new file mode 100644 index 0000000000..8b441de4dc --- /dev/null +++ b/com.unity.ml-agents.extensions/Runtime/Input/IInputActionAssetProvider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3ce1e3218e46e48a4ab64f6f631553a4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.ml-agents.extensions/Runtime/Input/IRLActionInputAdaptor.cs b/com.unity.ml-agents.extensions/Runtime/Input/IRLActionInputAdaptor.cs new file mode 100644 index 0000000000..173db482ae --- /dev/null +++ b/com.unity.ml-agents.extensions/Runtime/Input/IRLActionInputAdaptor.cs @@ -0,0 +1,39 @@ +#if MLA_INPUT_SYSTEM && UNITY_2019_4_OR_NEWER +using System; +using Unity.MLAgents.Actuators; +using UnityEngine.InputSystem; + +namespace Unity.MLAgents.Extensions.Input +{ + /// + /// Implement this interface in order to customize how information is translated s + /// and . + /// + public interface IRLActionInputAdaptor + { + /// + /// Generate an for a given action which determines how data is translated between + /// the and ML-Agents. + /// + /// The to based the from. + /// An instance based off the information in the . + ActionSpec GetActionSpecForInputAction(InputAction action); + + /// + /// Translates data from the object to the . + /// + /// The action associated with this adaptor. + /// The control which will write the event to the . + /// The associated with this action and adaptor pair. + /// The object to read from. + void QueueInputEventForAction(InputAction action, InputControl control, ActionSpec actionSpec, in ActionBuffers actionBuffers); + + /// + /// Writes data from the to the . + /// + /// The to read data from. + /// The object to write data to. + void WriteToHeuristic(InputAction action, in ActionBuffers actionBuffers); + } +} +#endif // MLA_INPUT_SYSTEM && UNITY_2019_4_OR_NEWER diff --git a/com.unity.ml-agents.extensions/Runtime/Input/IRLActionInputAdaptor.cs.meta b/com.unity.ml-agents.extensions/Runtime/Input/IRLActionInputAdaptor.cs.meta new file mode 100644 index 0000000000..f59800ce36 --- /dev/null +++ b/com.unity.ml-agents.extensions/Runtime/Input/IRLActionInputAdaptor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 010af7dd365b48849a9d498b84a4be94 +timeCreated: 1611350962 \ No newline at end of file diff --git a/com.unity.ml-agents.extensions/Runtime/Input/InputActionActuator.cs b/com.unity.ml-agents.extensions/Runtime/Input/InputActionActuator.cs new file mode 100644 index 0000000000..317f77e138 --- /dev/null +++ b/com.unity.ml-agents.extensions/Runtime/Input/InputActionActuator.cs @@ -0,0 +1,89 @@ +#if MLA_INPUT_SYSTEM && UNITY_2019_4_OR_NEWER + +using Unity.MLAgents.Actuators; +using Unity.MLAgents.Policies; +using UnityEngine; +using UnityEngine.InputSystem; +using UnityEngine.Profiling; + +namespace Unity.MLAgents.Extensions.Input +{ + /// + /// This implementation of will send events from the ML-Agents training process, or from + /// neural networks to the via the interface. If an + /// 's indicate that the Agent is running in Heuristic Mode, + /// this Actuator will write actions from the to the object. + /// + public class InputActionActuator : IActuator, IHeuristicProvider + { + readonly BehaviorParameters m_BehaviorParameters; + readonly InputAction m_Action; + readonly IRLActionInputAdaptor m_InputAdaptor; + InputDevice m_Device; + InputControl m_Control; + + /// + /// Construct an with the of the + /// component, the relevant , and the relevant + /// to convert between ml-agents <--> . + /// + /// The input device this action is bound to. + /// Used to determine if the is running in + /// heuristic mode. + /// The this we read/write data to/from + /// via the . + /// The that will convert data between ML-Agents + /// and the . + public InputActionActuator(InputDevice inputDevice, BehaviorParameters behaviorParameters, + InputAction action, + IRLActionInputAdaptor adaptor) + { + m_BehaviorParameters = behaviorParameters; + Name = $"InputActionActuator-{action.name}"; + m_Action = action; + m_InputAdaptor = adaptor; + ActionSpec = adaptor.GetActionSpecForInputAction(m_Action); + m_Device = inputDevice; + m_Control = m_Device?.GetChildControl(m_Action.name); + } + + /// + public void OnActionReceived(ActionBuffers actionBuffers) + { + Profiler.BeginSample("InputActionActuator.OnActionReceived"); + if (!m_BehaviorParameters.IsInHeuristicMode()) + { + m_InputAdaptor.QueueInputEventForAction(m_Action, m_Control, ActionSpec, actionBuffers); + } + Profiler.EndSample(); + } + + /// + public void WriteDiscreteActionMask(IDiscreteActionMask actionMask) + { + // TODO configure mask from editor UI? + } + + /// + public ActionSpec ActionSpec { get; } + + /// + public string Name { get; } + + /// + public void ResetData() + { + // do nothing for now + } + + /// + public void Heuristic(in ActionBuffers actionBuffersOut) + { + Profiler.BeginSample("InputActionActuator.Heuristic"); + m_InputAdaptor.WriteToHeuristic(m_Action, actionBuffersOut); + Profiler.EndSample(); + } + } +} + +#endif // MLA_INPUT_SYSTEM && UNITY_2019_4_OR_NEWER diff --git a/com.unity.ml-agents.extensions/Runtime/Input/InputActionActuator.cs.meta b/com.unity.ml-agents.extensions/Runtime/Input/InputActionActuator.cs.meta new file mode 100644 index 0000000000..158711cbab --- /dev/null +++ b/com.unity.ml-agents.extensions/Runtime/Input/InputActionActuator.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d4dfb5125cc2461088f18e27b6c21842 +timeCreated: 1612305510 \ No newline at end of file diff --git a/com.unity.ml-agents.extensions/Runtime/Input/InputActuatorComponent.cs b/com.unity.ml-agents.extensions/Runtime/Input/InputActuatorComponent.cs new file mode 100644 index 0000000000..fc7d2f4cf0 --- /dev/null +++ b/com.unity.ml-agents.extensions/Runtime/Input/InputActuatorComponent.cs @@ -0,0 +1,331 @@ +#if MLA_INPUT_SYSTEM && UNITY_2019_4_OR_NEWER +using System; +using System.Collections.Generic; +using Unity.MLAgents.Actuators; +using Unity.MLAgents.Policies; +using UnityEngine; +using UnityEngine.Assertions; +using UnityEngine.InputSystem; +using UnityEngine.InputSystem.Controls; +using UnityEngine.InputSystem.Layouts; +using UnityEngine.InputSystem.Utilities; +#if UNITY_EDITOR +using UnityEditor; +#endif + +namespace Unity.MLAgents.Extensions.Input +{ + /// + /// Component class that handles the parsing of the and translates that into + /// s. + /// + [RequireComponent(typeof(PlayerInput), typeof(IInputActionAssetProvider))] + public class InputActuatorComponent : ActuatorComponent + { + InputActionAsset m_InputAsset; + IInputActionCollection2 m_AssetCollection; + PlayerInput m_PlayerInput; + BehaviorParameters m_BehaviorParameters; + IActuator[] m_Actuators; + InputDevice m_Device; + + /// + /// Mapping of types to types of concrete classes. + /// + public static Dictionary controlTypeToAdaptorType = new Dictionary + { + { typeof(Vector2Control), typeof(Vector2InputActionAdaptor) }, + { typeof(ButtonControl), typeof(ButtonInputActionAdaptor) }, + { typeof(IntegerControl), typeof(IntegerInputActionAdaptor) }, + { typeof(AxisControl), typeof(FloatInputActionAdaptor) }, + { typeof(DoubleControl), typeof(DoubleInputActionAdaptor) } + }; + + string m_LayoutName; + [SerializeField] + ActionSpec m_ActionSpec; + InputControlScheme m_ControlScheme; + + public const string mlAgentsLayoutFormat = "MLAT"; + public const string mlAgentsLayoutName = "MLAgentsLayout"; + public const string mlAgentsControlSchemeName = "ml-agents"; + + /// + public override ActionSpec ActionSpec + { + get + { +#if UNITY_EDITOR + FindNeededComponents(); + var actuators = CreateActuatorsFromMap(m_InputAsset.FindActionMap(m_PlayerInput.defaultActionMap), m_BehaviorParameters, null); + m_ActionSpec = CombineActuatorActionSpecs(actuators); +#endif + return m_ActionSpec; + } + } + + void OnDisable() + { + CleanupActionAsset(); + } + + /// + /// This method is where the gets parsed and translated into + /// s that communicate with the via a + /// virtual . + /// + /// The flow of this method is as follows: + /// + /// + /// Ensure that our custom s are registered with + /// the InputSystem. + /// + /// + /// Look for the components that are needed by this class in order to retrieve the + /// . It first looks for , if that + /// is not found, it will get the asset from the component. + /// + /// + /// Create the list s, one for each action in the default + /// as set by the component. Within the method + /// where the actuators are being created, an is also being built based + /// on the number and types of s. This will be used to create a virtual + /// with a that is specific to the + /// specified by + /// + /// + /// Create our device based on the layout that was generated and registered during + /// actuator creation. + /// + /// + /// Create an ml-agents control scheme and add it to the so + /// our virtual devices can be used. + /// + /// + /// Add our virtual to the input system. + /// + /// + /// + /// + /// A list of + public override IActuator[] CreateActuators() + { + FindNeededComponents(); + var collection = m_AssetCollection ?? m_InputAsset; + collection.Disable(); + var inputActionMap = m_InputAsset.FindActionMap(m_PlayerInput.defaultActionMap); + + RegisterLayoutBuilder(inputActionMap, m_LayoutName); + m_Device = InputSystem.AddDevice(m_LayoutName); + + m_Actuators = CreateActuatorsFromMap(inputActionMap, m_BehaviorParameters, m_Device); + + UpdateDeviceBinding(m_BehaviorParameters.IsInHeuristicMode()); + inputActionMap.Enable(); + + m_ActionSpec = CombineActuatorActionSpecs(m_Actuators); + collection.Enable(); + return m_Actuators; + } + + static ActionSpec CombineActuatorActionSpecs(IActuator[] actuators) + { + var specs = new ActionSpec[actuators.Length]; + for (var i = 0; i < actuators.Length; i++) + { + specs[i] = actuators[i].ActionSpec; + } + return ActionSpec.Combine(specs); + } + + internal static IActuator[] CreateActuatorsFromMap(InputActionMap inputActionMap, + BehaviorParameters behaviorParameters, + InputDevice inputDevice) + { + var actuators = new IActuator[inputActionMap.actions.Count]; + for (var i = 0; i < inputActionMap.actions.Count; i++) + { + var action = inputActionMap.actions[i]; + var actionLayout = InputSystem.LoadLayout(action.expectedControlType); + var adaptor = (IRLActionInputAdaptor)Activator.CreateInstance(controlTypeToAdaptorType[actionLayout.type]); + actuators[i] = new InputActionActuator(inputDevice, behaviorParameters, action, adaptor); + + // Reasonably, the input system starts adding numbers after the first none numbered name + // is added. So for device ID of 0, we use the empty string in the path. + var path = $"{inputDevice?.path}{InputControlPath.Separator}{action.name}"; + action.AddBinding(path, + action.interactions, + action.processors, + mlAgentsControlSchemeName); + } + return actuators; + } + + /// + /// Set up bindings based on whether or not the BehaviorParameters are working in Heuristic mode or not. + /// If we are working in Heuristic mode, we want the input system to handle everything. If not, we + /// want the neural network to send input from virtual devices. + /// + /// true if the Agent connected to this GameObject is working in + /// Heuristic mode. + /// + internal void UpdateDeviceBinding(bool isInHeuristicMode) + { + if (ReferenceEquals(m_Device, null)) + { + return; + } + var collection = m_AssetCollection ?? m_InputAsset; + m_ControlScheme = CreateControlScheme(m_Device, isInHeuristicMode, m_InputAsset); + if (m_InputAsset.FindControlSchemeIndex(m_ControlScheme.name) != -1) + { + m_InputAsset.RemoveControlScheme(m_ControlScheme.name); + } + + if (!isInHeuristicMode) + { + var inputActionMap = m_InputAsset.FindActionMap(m_PlayerInput.defaultActionMap); + m_InputAsset.AddControlScheme(m_ControlScheme); + collection.bindingMask = InputBinding.MaskByGroup(m_ControlScheme.bindingGroup); + collection.devices = new ReadOnlyArray(new[] { m_Device }); + inputActionMap.bindingMask = collection.bindingMask; + inputActionMap.devices = collection.devices; + } + else + { + var inputActionMap = m_InputAsset.FindActionMap(m_PlayerInput.defaultActionMap); + collection.bindingMask = null; + collection.devices = InputSystem.devices; + inputActionMap.devices = InputSystem.devices; + inputActionMap.bindingMask = null; + } + collection.Enable(); + } + + /// + /// This method creates a control scheme and adds it to the passed in so + /// we can add our device to in order for it to be discovered by the . + /// + /// The virtual device to add to our custom control scheme. + /// if we are in heuristic mode, we need to add other other device requirements. + /// The InputActionAsset to get the device requirements from + internal static InputControlScheme CreateControlScheme(InputControl device, + bool isInHeuristicMode, + InputActionAsset asset) + { + var deviceRequirements = new List + { + new InputControlScheme.DeviceRequirement + { + controlPath = InputBinding.Separator + mlAgentsLayoutName + } + }; + + if (isInHeuristicMode) + { + for (var i = 0; i < asset.controlSchemes.Count; i++) + { + var scheme = asset.controlSchemes[i]; + for (var ii = 0; ii < scheme.deviceRequirements.Count; ii++) + { + deviceRequirements.Add(scheme.deviceRequirements[ii]); + } + } + } + + var inputControlScheme = new InputControlScheme( + mlAgentsControlSchemeName, + deviceRequirements); + + return inputControlScheme; + } + +#pragma warning disable 672 + /// + public override IActuator CreateActuator() { return null; } +#pragma warning restore 672 + + /// + /// + /// + /// + /// + /// + internal static void RegisterLayoutBuilder(InputActionMap defaultMap, string layoutName) + { + if (InputSystem.LoadLayout(layoutName) == null) + { + InputSystem.RegisterLayoutBuilder(() => + { + // TODO does this need to change based on the action map we use? + var builder = new InputControlLayout.Builder() + .WithName(layoutName) + .WithFormat(mlAgentsLayoutFormat); + for(var i = 0; i < defaultMap.actions.Count; i++) + { + var action = defaultMap.actions[i]; + builder.AddControl(action.name) + .WithLayout(action.expectedControlType); + } + return builder.Build(); + + }, layoutName); + } + } + + internal void FindNeededComponents() + { + if (m_InputAsset == null) + { + var assetProvider = GetComponent(); + Assert.IsNotNull(assetProvider); + (m_InputAsset, m_AssetCollection) = assetProvider.GetInputActionAsset(); + Assert.IsNotNull(m_InputAsset, "An InputActionAsset could not be found on IInputActionAssetProvider or PlayerInput."); + } + if (m_PlayerInput == null) + { + m_PlayerInput = GetComponent(); + Assert.IsNotNull(m_PlayerInput, "PlayerInput component could not be found on this GameObject."); + } + + if (m_BehaviorParameters == null) + { + m_BehaviorParameters = GetComponent(); + Assert.IsNotNull(m_BehaviorParameters, "BehaviorParameters were not on the current GameObject."); + m_BehaviorParameters.OnPolicyUpdated += UpdateDeviceBinding; + m_LayoutName = mlAgentsLayoutName + m_BehaviorParameters.BehaviorName; + } + } + + internal void CleanupActionAsset() + { + InputSystem.RemoveLayout(mlAgentsLayoutName); + if (!ReferenceEquals(m_Device, null)) + { + InputSystem.RemoveDevice(m_Device); + } + + if (!ReferenceEquals(m_InputAsset, null) + && m_InputAsset.FindControlSchemeIndex(mlAgentsControlSchemeName) != -1) + { + m_InputAsset.RemoveControlScheme(mlAgentsControlSchemeName); + } + + if (m_Actuators != null) + { + Array.Clear(m_Actuators, 0, m_Actuators.Length); + } + + if (!ReferenceEquals(m_BehaviorParameters, null)) + { + m_BehaviorParameters.OnPolicyUpdated -= UpdateDeviceBinding; + } + + m_InputAsset = null; + m_PlayerInput = null; + m_BehaviorParameters = null; + m_Device = null; + } + } +} +#endif // MLA_INPUT_SYSTEM && UNITY_2019_4_OR_NEWER diff --git a/com.unity.ml-agents.extensions/Runtime/Input/InputActuatorComponent.cs.meta b/com.unity.ml-agents.extensions/Runtime/Input/InputActuatorComponent.cs.meta new file mode 100644 index 0000000000..ee3aa3d024 --- /dev/null +++ b/com.unity.ml-agents.extensions/Runtime/Input/InputActuatorComponent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 33005b124d7f841a191249baf2bacb2a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.ml-agents.extensions/Runtime/Input/Unity.ML-Agents.Extensions.Input.asmdef b/com.unity.ml-agents.extensions/Runtime/Input/Unity.ML-Agents.Extensions.Input.asmdef new file mode 100644 index 0000000000..c1cdc58563 --- /dev/null +++ b/com.unity.ml-agents.extensions/Runtime/Input/Unity.ML-Agents.Extensions.Input.asmdef @@ -0,0 +1,23 @@ +{ + "name": "Unity.ML-Agents.Extensions.Input", + "references": [ + "Unity.ML-Agents", + "Unity.Barracuda", + "Unity.InputSystem" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [ + { + "name": "com.unity.inputsystem", + "expression": "1.1.0-preview", + "define": "MLA_INPUT_SYSTEM" + } + ], + "noEngineReferences": false +} diff --git a/com.unity.ml-agents.extensions/Runtime/Input/Unity.ML-Agents.Extensions.Input.asmdef.meta b/com.unity.ml-agents.extensions/Runtime/Input/Unity.ML-Agents.Extensions.Input.asmdef.meta new file mode 100644 index 0000000000..5ad61670a7 --- /dev/null +++ b/com.unity.ml-agents.extensions/Runtime/Input/Unity.ML-Agents.Extensions.Input.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: a7b0d999fb2a7493a85c4c7017412530 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.ml-agents.extensions/Runtime/Unity.ML-Agents.Extensions.asmdef b/com.unity.ml-agents.extensions/Runtime/Unity.ML-Agents.Extensions.asmdef index 214fbea537..4fffde3916 100644 --- a/com.unity.ml-agents.extensions/Runtime/Unity.ML-Agents.Extensions.asmdef +++ b/com.unity.ml-agents.extensions/Runtime/Unity.ML-Agents.Extensions.asmdef @@ -2,7 +2,8 @@ "name": "Unity.ML-Agents.Extensions", "references": [ "Unity.Barracuda", - "Unity.ML-Agents" + "Unity.ML-Agents", + "Unity.ML-Agents.Extensions.Input" ], "includePlatforms": [], "excludePlatforms": [] diff --git a/com.unity.ml-agents.extensions/Tests/Runtime/Input.meta b/com.unity.ml-agents.extensions/Tests/Runtime/Input.meta new file mode 100644 index 0000000000..ad97c88194 --- /dev/null +++ b/com.unity.ml-agents.extensions/Tests/Runtime/Input.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 27f8e1ce37d7485f814ce50a37101203 +timeCreated: 1612908869 \ No newline at end of file diff --git a/com.unity.ml-agents.extensions/Tests/Runtime/Input/Adaptors.meta b/com.unity.ml-agents.extensions/Tests/Runtime/Input/Adaptors.meta new file mode 100644 index 0000000000..13aedd73ff --- /dev/null +++ b/com.unity.ml-agents.extensions/Tests/Runtime/Input/Adaptors.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9b59c16f3e30469ca26156b96351164a +timeCreated: 1613081382 \ No newline at end of file diff --git a/com.unity.ml-agents.extensions/Tests/Runtime/Input/Adaptors/ButtonInputActionAdaptorTests.cs b/com.unity.ml-agents.extensions/Tests/Runtime/Input/Adaptors/ButtonInputActionAdaptorTests.cs new file mode 100644 index 0000000000..d2a613f389 --- /dev/null +++ b/com.unity.ml-agents.extensions/Tests/Runtime/Input/Adaptors/ButtonInputActionAdaptorTests.cs @@ -0,0 +1,72 @@ +#if MLA_INPUT_TESTS && UNITY_2019_4_OR_NEWER +using NUnit.Framework; +using Unity.MLAgents.Actuators; +using Unity.MLAgents.Extensions.Input; +using UnityEngine; +using UnityEngine.InputSystem; + +namespace Unity.MLAgents.Extensions.Tests.Runtime.Input +{ + public class ButtonInputActionAdaptorTests : InputTestFixture + { + ButtonInputActionAdaptor m_Adaptor; + InputDevice m_Device; + InputControl m_Control; + InputAction m_Action; + + public override void Setup() + { + base.Setup(); + const string kLayout = @" + { + ""name"" : ""TestDevice"", + ""extend"" : ""HID"", + ""controls"" : [ + { ""name"" : ""button"", ""layout"" : ""Button"" } + ] + }"; + InputSystem.RegisterLayout(kLayout); + m_Device = InputSystem.AddDevice("TestDevice"); + m_Control = (InputControl)m_Device["button"]; + m_Action = new InputAction("action", InputActionType.Button, "/TestDevice/button", null, null, "Button"); + m_Action.Enable(); + m_Adaptor = new ButtonInputActionAdaptor(); + } + + public override void TearDown() + { + base.TearDown(); + m_Adaptor = null; + } + + [Test] + public void TestGenerateActionSpec() + { + var actionSpec = m_Adaptor.GetActionSpecForInputAction(new InputAction()); + Assert.IsTrue(actionSpec.NumDiscreteActions == 1); + Assert.IsTrue(actionSpec.BranchSizes[0] == 2); + } + + [Test] + public void TestQueueEvent() + { + var actionBuffers = new ActionBuffers(ActionSegment.Empty, new ActionSegment(new[] { 1 })); + m_Adaptor.QueueInputEventForAction(m_Action, m_Control, new ActionSpec(), actionBuffers); + InputSystem.Update(); + var val = m_Action.ReadValue(); + Assert.IsTrue(Mathf.Approximately(1f, val)); + } + + [Test] + public void TestWriteToHeuristic() + { + var actionBuffers = new ActionBuffers(ActionSegment.Empty, new ActionSegment(new[] { 1 })); + m_Adaptor.QueueInputEventForAction(m_Action, m_Control, new ActionSpec(), actionBuffers); + InputSystem.Update(); + var buffer = new ActionBuffers(ActionSegment.Empty, new ActionSegment(new[] { 1 })); + m_Adaptor.WriteToHeuristic(m_Action, buffer); + Assert.IsTrue(buffer.DiscreteActions[0] == 1); + } + } +} +#endif // MLA_INPUT_TESTS && UNITY_2019_4_OR_NEWER diff --git a/com.unity.ml-agents.extensions/Tests/Runtime/Input/Adaptors/ButtonInputActionAdaptorTests.cs.meta b/com.unity.ml-agents.extensions/Tests/Runtime/Input/Adaptors/ButtonInputActionAdaptorTests.cs.meta new file mode 100644 index 0000000000..b8047c76fb --- /dev/null +++ b/com.unity.ml-agents.extensions/Tests/Runtime/Input/Adaptors/ButtonInputActionAdaptorTests.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f53bd7ea7b154c9a992b48c9fbc34e47 +timeCreated: 1613081399 \ No newline at end of file diff --git a/com.unity.ml-agents.extensions/Tests/Runtime/Input/Adaptors/DoubleInputActionAdaptorTests.cs b/com.unity.ml-agents.extensions/Tests/Runtime/Input/Adaptors/DoubleInputActionAdaptorTests.cs new file mode 100644 index 0000000000..4f52a075d3 --- /dev/null +++ b/com.unity.ml-agents.extensions/Tests/Runtime/Input/Adaptors/DoubleInputActionAdaptorTests.cs @@ -0,0 +1,70 @@ +#if MLA_INPUT_TESTS && UNITY_2019_4_OR_NEWER +using NUnit.Framework; +using Unity.MLAgents.Actuators; +using Unity.MLAgents.Extensions.Input; +using UnityEngine; +using UnityEngine.InputSystem; + +namespace Unity.MLAgents.Extensions.Tests.Runtime.Input +{ + public class DoubleInputActionAdaptorTests : InputTestFixture + { + DoubleInputActionAdaptor m_Adaptor; + InputDevice m_Device; + InputControl m_Control; + InputAction m_Action; + + public override void Setup() + { + base.Setup(); + const string kLayout = @" + { + ""name"" : ""TestDevice"", + ""extend"" : ""HID"", + ""controls"" : [ + { ""name"" : ""button"", ""layout"" : ""Double"" } + ] + }"; + InputSystem.RegisterLayout(kLayout); + m_Device = InputSystem.AddDevice("TestDevice"); + m_Control = (InputControl)m_Device["button"]; + m_Action = new InputAction("action", InputActionType.Value, "/TestDevice/button", null, null, "double"); + m_Action.Enable(); + m_Adaptor = new DoubleInputActionAdaptor(); + } + + public override void TearDown() + { + base.TearDown(); + m_Adaptor = null; + } + + [Test] + public void TestGenerateActionSpec() + { + var actionSpec = m_Adaptor.GetActionSpecForInputAction(new InputAction()); + Assert.IsTrue(actionSpec.NumContinuousActions == 1); + } + + [Test] + public void TestQueueEvent() + { + var actionBuffers = new ActionBuffers(new ActionSegment(new[] { 1f }), ActionSegment.Empty); + m_Adaptor.QueueInputEventForAction(m_Action, m_Control, new ActionSpec(), actionBuffers); + InputSystem.Update(); + Assert.IsTrue(Mathf.Approximately(1f, (float)m_Action.ReadValue())); + } + + [Test] + public void TestWriteToHeuristic() + { + var actionBuffers = new ActionBuffers(new ActionSegment(new[] { 1f }), ActionSegment.Empty); + m_Adaptor.QueueInputEventForAction(m_Action, m_Control, new ActionSpec(), actionBuffers); + InputSystem.Update(); + var buffer = new ActionBuffers(new ActionSegment(new[] { 1f }), ActionSegment.Empty); + m_Adaptor.WriteToHeuristic(m_Action, buffer); + Assert.IsTrue(Mathf.Approximately(buffer.ContinuousActions[0], 1f)); + } + } +} +#endif // MLA_INPUT_TESTS && UNITY_2019_4_OR_NEWER diff --git a/com.unity.ml-agents.extensions/Tests/Runtime/Input/Adaptors/DoubleInputActionAdaptorTests.cs.meta b/com.unity.ml-agents.extensions/Tests/Runtime/Input/Adaptors/DoubleInputActionAdaptorTests.cs.meta new file mode 100644 index 0000000000..b5779ccaaa --- /dev/null +++ b/com.unity.ml-agents.extensions/Tests/Runtime/Input/Adaptors/DoubleInputActionAdaptorTests.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ae2012c098aa4b57ab09dbf72bcd4efe +timeCreated: 1613074622 \ No newline at end of file diff --git a/com.unity.ml-agents.extensions/Tests/Runtime/Input/Adaptors/FloatInputActionAdapatorTests.cs b/com.unity.ml-agents.extensions/Tests/Runtime/Input/Adaptors/FloatInputActionAdapatorTests.cs new file mode 100644 index 0000000000..04ef1ea09d --- /dev/null +++ b/com.unity.ml-agents.extensions/Tests/Runtime/Input/Adaptors/FloatInputActionAdapatorTests.cs @@ -0,0 +1,71 @@ +#if MLA_INPUT_TESTS && UNITY_2019_4_OR_NEWER +using NUnit.Framework; +using Unity.MLAgents.Actuators; +using Unity.MLAgents.Extensions.Input; +using UnityEngine; +using UnityEngine.InputSystem; + +namespace Unity.MLAgents.Extensions.Tests.Runtime.Input +{ + public class FloatInputActionAdaptorTests : InputTestFixture + { + FloatInputActionAdaptor m_Adaptor; + InputDevice m_Device; + InputControl m_Control; + InputAction m_Action; + + public override void Setup() + { + base.Setup(); + const string kLayout = @" + { + ""name"" : ""TestDevice"", + ""extend"" : ""HID"", + ""controls"" : [ + { ""name"" : ""button"", ""layout"" : ""Axis"" } + ] + }"; + InputSystem.RegisterLayout(kLayout); + m_Device = InputSystem.AddDevice("TestDevice"); + m_Control = (InputControl)m_Device["button"]; + m_Action = new InputAction("action", InputActionType.Value, "/TestDevice/button", null, null, "Axis"); + m_Action.Enable(); + m_Adaptor = new FloatInputActionAdaptor(); + } + + public override void TearDown() + { + base.TearDown(); + m_Adaptor = null; + } + + [Test] + public void TestGenerateActionSpec() + { + var actionSpec = m_Adaptor.GetActionSpecForInputAction(new InputAction()); + Assert.IsTrue(actionSpec.NumContinuousActions == 1); + } + + [Test] + public void TestQueueEvent() + { + var actionBuffers = new ActionBuffers(new ActionSegment(new[] { 1f }), ActionSegment.Empty); + m_Adaptor.QueueInputEventForAction(m_Action, m_Control, new ActionSpec(), actionBuffers); + InputSystem.Update(); + var val = m_Action.ReadValue(); + Assert.IsTrue(Mathf.Approximately(1f, val)); + } + + [Test] + public void TestWriteToHeuristic() + { + var actionBuffers = new ActionBuffers(new ActionSegment(new[] { 1f }), ActionSegment.Empty); + m_Adaptor.QueueInputEventForAction(m_Action, m_Control, new ActionSpec(), actionBuffers); + InputSystem.Update(); + var buffer = new ActionBuffers(new ActionSegment(new[] { 1f }), ActionSegment.Empty); + m_Adaptor.WriteToHeuristic(m_Action, buffer); + Assert.IsTrue(Mathf.Approximately(1f, buffer.ContinuousActions[0])); + } + } +} +#endif // MLA_INPUT_TESTS && UNITY_2019_4_OR_NEWER diff --git a/com.unity.ml-agents.extensions/Tests/Runtime/Input/Adaptors/FloatInputActionAdapatorTests.cs.meta b/com.unity.ml-agents.extensions/Tests/Runtime/Input/Adaptors/FloatInputActionAdapatorTests.cs.meta new file mode 100644 index 0000000000..f8abad1d70 --- /dev/null +++ b/com.unity.ml-agents.extensions/Tests/Runtime/Input/Adaptors/FloatInputActionAdapatorTests.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a85fbb8b3e154eccadbdab826c3c5cae +timeCreated: 1613083958 \ No newline at end of file diff --git a/com.unity.ml-agents.extensions/Tests/Runtime/Input/Adaptors/IntegerInputActionAdaptorTests.cs b/com.unity.ml-agents.extensions/Tests/Runtime/Input/Adaptors/IntegerInputActionAdaptorTests.cs new file mode 100644 index 0000000000..3655bc31d0 --- /dev/null +++ b/com.unity.ml-agents.extensions/Tests/Runtime/Input/Adaptors/IntegerInputActionAdaptorTests.cs @@ -0,0 +1,73 @@ +#if MLA_INPUT_TESTS && UNITY_2019_4_OR_NEWER +using System; +using NUnit.Framework; +using Unity.MLAgents.Actuators; +using Unity.MLAgents.Extensions.Input; +using UnityEngine; +using UnityEngine.InputSystem; + +namespace Unity.MLAgents.Extensions.Tests.Runtime.Input +{ + public class IntegerInputActionAdaptorTests : InputTestFixture + { + IntegerInputActionAdaptor m_Adaptor; + InputDevice m_Device; + InputControl m_Control; + InputAction m_Action; + + public override void Setup() + { + base.Setup(); + const string kLayout = @" + { + ""name"" : ""TestDevice"", + ""extend"" : ""HID"", + ""controls"" : [ + { ""name"" : ""button"", ""layout"" : ""integer"" } + ] + }"; + InputSystem.RegisterLayout(kLayout); + m_Device = InputSystem.AddDevice("TestDevice"); + m_Control = (InputControl)m_Device["button"]; + m_Action = new InputAction("action", InputActionType.Value, "/TestDevice/button", null, null, "int"); + m_Action.Enable(); + m_Adaptor = new IntegerInputActionAdaptor(); + } + + public override void TearDown() + { + base.TearDown(); + m_Adaptor = null; + } + + [Test] + public void TestGenerateActionSpec() + { + var actionSpec = m_Adaptor.GetActionSpecForInputAction(new InputAction()); + Assert.IsTrue(actionSpec.NumDiscreteActions == 1); + Assert.IsTrue(actionSpec.SumOfDiscreteBranchSizes == 2); + } + + [Test] + public void TestQueueEvent() + { + var actionBuffers = new ActionBuffers(ActionSegment.Empty, new ActionSegment(new[] { 1 })); + m_Adaptor.QueueInputEventForAction(m_Action, m_Control, new ActionSpec(), actionBuffers); + InputSystem.Update(); + var val = m_Action.ReadValue(); + Assert.IsTrue(val == 1); + } + + [Test] + public void TestWriteToHeuristic() + { + var actionBuffers = new ActionBuffers(ActionSegment.Empty, new ActionSegment(new[] { 1 })); + m_Adaptor.QueueInputEventForAction(m_Action, m_Control, new ActionSpec(), actionBuffers); + InputSystem.Update(); + var buffer = new ActionBuffers(ActionSegment.Empty, new ActionSegment(new int[1])); + m_Adaptor.WriteToHeuristic(m_Action, buffer); + Assert.IsTrue(buffer.DiscreteActions[0] == 1); + } + } +} +#endif // MLA_INPUT_TESTS && UNITY_2019_4_OR_NEWER diff --git a/com.unity.ml-agents.extensions/Tests/Runtime/Input/Adaptors/IntegerInputActionAdaptorTests.cs.meta b/com.unity.ml-agents.extensions/Tests/Runtime/Input/Adaptors/IntegerInputActionAdaptorTests.cs.meta new file mode 100644 index 0000000000..4af1d87081 --- /dev/null +++ b/com.unity.ml-agents.extensions/Tests/Runtime/Input/Adaptors/IntegerInputActionAdaptorTests.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a8631ede70c049a7836284217ddfdc94 +timeCreated: 1613082414 \ No newline at end of file diff --git a/com.unity.ml-agents.extensions/Tests/Runtime/Input/Adaptors/Vector2InputActionAdaptorTests.cs b/com.unity.ml-agents.extensions/Tests/Runtime/Input/Adaptors/Vector2InputActionAdaptorTests.cs new file mode 100644 index 0000000000..e699c2da96 --- /dev/null +++ b/com.unity.ml-agents.extensions/Tests/Runtime/Input/Adaptors/Vector2InputActionAdaptorTests.cs @@ -0,0 +1,73 @@ +#if MLA_INPUT_TESTS && UNITY_2019_4_OR_NEWER +using NUnit.Framework; +using Unity.MLAgents.Actuators; +using Unity.MLAgents.Extensions.Input; +using UnityEngine; +using UnityEngine.InputSystem; + +namespace Unity.MLAgents.Extensions.Tests.Runtime.Input +{ + public class Vector2InputActionAdaptorTests : InputTestFixture + { + Vector2InputActionAdaptor m_Adaptor; + InputDevice m_Device; + InputControl m_Control; + InputAction m_Action; + + public override void Setup() + { + base.Setup(); + const string kLayout = @" + { + ""name"" : ""TestDevice"", + ""extend"" : ""HID"", + ""controls"" : [ + { ""name"" : ""button"", ""layout"" : ""Vector2"" } + ] + }"; + InputSystem.RegisterLayout(kLayout); + m_Device = InputSystem.AddDevice("TestDevice"); + m_Control = (InputControl)m_Device["button"]; + m_Action = new InputAction("action", InputActionType.Value, "/TestDevice/button", null, null, "Vector2"); + m_Action.Enable(); + m_Adaptor = new Vector2InputActionAdaptor(); + } + + public override void TearDown() + { + base.TearDown(); + m_Adaptor = null; + } + + [Test] + public void TestGenerateActionSpec() + { + var actionSpec = m_Adaptor.GetActionSpecForInputAction(new InputAction()); + Assert.IsTrue(actionSpec.NumContinuousActions == 2); + } + + [Test] + public void TestQueueEvent() + { + var actionBuffers = new ActionBuffers(new ActionSegment(new[] { 0f, 1f }), ActionSegment.Empty); + m_Adaptor.QueueInputEventForAction(m_Action, m_Control, new ActionSpec(), actionBuffers); + InputSystem.Update(); + var val = m_Action.ReadValue(); + Assert.IsTrue(Mathf.Approximately(0f, val.x)); + Assert.IsTrue(Mathf.Approximately(1f, val.y)); + } + + [Test] + public void TestWriteToHeuristic() + { + var actionBuffers = new ActionBuffers(new ActionSegment(new[] { 0f, 1f }), ActionSegment.Empty); + m_Adaptor.QueueInputEventForAction(m_Action, m_Control, new ActionSpec(), actionBuffers); + InputSystem.Update(); + var buffer = new ActionBuffers(new ActionSegment(new float[2]), ActionSegment.Empty); + m_Adaptor.WriteToHeuristic(m_Action, buffer); + Assert.IsTrue(Mathf.Approximately(buffer.ContinuousActions[0], 0f)); + Assert.IsTrue(Mathf.Approximately(buffer.ContinuousActions[1], 1f)); + } + } +} +#endif // MLA_INPUT_TESTS && UNITY_2019_4_OR_NEWER diff --git a/com.unity.ml-agents.extensions/Tests/Runtime/Input/Adaptors/Vector2InputActionAdaptorTests.cs.meta b/com.unity.ml-agents.extensions/Tests/Runtime/Input/Adaptors/Vector2InputActionAdaptorTests.cs.meta new file mode 100644 index 0000000000..cfadda3314 --- /dev/null +++ b/com.unity.ml-agents.extensions/Tests/Runtime/Input/Adaptors/Vector2InputActionAdaptorTests.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 135c4a0f33174ea090da18b0d67bd169 +timeCreated: 1613082080 \ No newline at end of file diff --git a/com.unity.ml-agents.extensions/Tests/Runtime/Input/InputActionActuatorTests.cs b/com.unity.ml-agents.extensions/Tests/Runtime/Input/InputActionActuatorTests.cs new file mode 100644 index 0000000000..d5f2f15ce4 --- /dev/null +++ b/com.unity.ml-agents.extensions/Tests/Runtime/Input/InputActionActuatorTests.cs @@ -0,0 +1,82 @@ +#if MLA_INPUT_TESTS && UNITY_2019_4_OR_NEWER +using NUnit.Framework; +using Unity.Barracuda; +using Unity.MLAgents.Actuators; +using Unity.MLAgents.Extensions.Input; +using Unity.MLAgents.Policies; +using UnityEngine; +using UnityEngine.InputSystem; + +namespace Unity.MLAgents.Extensions.Tests.Runtime.Input +{ + class TestAdaptor : IRLActionInputAdaptor + { + public bool eventQueued; + public bool writtenToHeuristic; + + public ActionSpec GetActionSpecForInputAction(InputAction action) + { + return ActionSpec.MakeContinuous(1); + } + + public void QueueInputEventForAction(InputAction action, InputControl control, ActionSpec actionSpec, in ActionBuffers actionBuffers) + { + eventQueued = true; + } + + public void WriteToHeuristic(InputAction action, in ActionBuffers actionBuffers) + { + writtenToHeuristic = true; + } + + public void Reset() + { + eventQueued = false; + writtenToHeuristic = false; + } + } + + [TestFixture] + public class InputActionActuatorTests + { + BehaviorParameters m_BehaviorParameters; + InputActionActuator m_Actuator; + TestAdaptor m_Adaptor; + + [SetUp] + public void Setup() + { + var go = new GameObject(); + m_BehaviorParameters = go.AddComponent(); + var action = new InputAction("action"); + m_Adaptor = new TestAdaptor(); + m_Actuator = new InputActionActuator(null, m_BehaviorParameters, action, m_Adaptor); + } + + [Test] + public void TestOnActionReceived() + { + m_BehaviorParameters.BehaviorType = BehaviorType.HeuristicOnly; + m_Actuator.OnActionReceived(new ActionBuffers()); + m_Actuator.Heuristic(new ActionBuffers()); + Assert.IsFalse(m_Adaptor.eventQueued); + Assert.IsTrue(m_Adaptor.writtenToHeuristic); + m_Adaptor.Reset(); + + m_BehaviorParameters.BehaviorType = BehaviorType.Default; + m_Actuator.OnActionReceived(new ActionBuffers()); + Assert.IsFalse(m_Adaptor.eventQueued); + m_Adaptor.Reset(); + + m_BehaviorParameters.Model = ScriptableObject.CreateInstance(); + m_Actuator.OnActionReceived(new ActionBuffers()); + Assert.IsTrue(m_Adaptor.eventQueued); + m_Adaptor.Reset(); + + Assert.AreEqual(m_Actuator.Name, "InputActionActuator-action"); + m_Actuator.ResetData(); + m_Actuator.WriteDiscreteActionMask(null); + } + } +} +#endif // MLA_INPUT_TESTS && UNITY_2019_4_OR_NEWER diff --git a/com.unity.ml-agents.extensions/Tests/Runtime/Input/InputActionActuatorTests.cs.meta b/com.unity.ml-agents.extensions/Tests/Runtime/Input/InputActionActuatorTests.cs.meta new file mode 100644 index 0000000000..decea5110f --- /dev/null +++ b/com.unity.ml-agents.extensions/Tests/Runtime/Input/InputActionActuatorTests.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c5adebe0f22e48938e7730469f034c70 +timeCreated: 1613070186 \ No newline at end of file diff --git a/com.unity.ml-agents.extensions/Tests/Runtime/Input/InputActuatorComponentTests.cs b/com.unity.ml-agents.extensions/Tests/Runtime/Input/InputActuatorComponentTests.cs new file mode 100644 index 0000000000..459a4ba61c --- /dev/null +++ b/com.unity.ml-agents.extensions/Tests/Runtime/Input/InputActuatorComponentTests.cs @@ -0,0 +1,112 @@ +#if MLA_INPUT_TESTS +using System; +using System.Linq; +using NUnit.Framework; +using Unity.MLAgents.Actuators; +using Unity.MLAgents.Extensions.Input; +using Unity.MLAgents.Policies; +using UnityEngine; +using UnityEngine.InputSystem; + +namespace Unity.MLAgents.Extensions.Tests.Runtime.Input +{ + class TestProvider : MonoBehaviour, IInputActionAssetProvider + { + public InputActionAsset asset; + public IInputActionCollection2 collection; + + public (InputActionAsset, IInputActionCollection2) GetInputActionAsset() + { + return (asset, collection); + } + } + public class InputActuatorComponentTests : InputTestFixture + { + InputActionAsset m_Asset; + GameObject m_GameObject; + PlayerInput m_PlayerInput; + BehaviorParameters m_BehaviorParameters; + InputActuatorComponent m_ActuatorComponent; + TestPushBlockActions m_Actions; + TestProvider m_Provider; + + public override void Setup() + { + base.Setup(); + m_Actions = new TestPushBlockActions(); + m_Asset = m_Actions.asset; + m_GameObject = new GameObject(); + m_PlayerInput = m_GameObject.AddComponent(); + m_Provider = m_GameObject.AddComponent(); + m_Provider.asset = m_Asset; + m_Provider.collection = m_Actions; + m_ActuatorComponent = m_GameObject.AddComponent(); + m_BehaviorParameters = m_GameObject.AddComponent(); + m_BehaviorParameters.BehaviorName = "InputTest"; + m_BehaviorParameters.BehaviorType = BehaviorType.Default; + } + + public override void TearDown() + { + m_ActuatorComponent.CleanupActionAsset(); + base.TearDown(); + } + + [Test] + public void InputActuatorComponentTestCreateActuators() + { + // Use the Assert class to test conditions. + m_PlayerInput.actions = m_Asset; + m_PlayerInput.defaultActionMap = m_Asset.actionMaps[0].name; + var actuators = m_ActuatorComponent.CreateActuators(); + Assert.IsTrue(actuators.Length == 2); + Assert.IsTrue(actuators[0].ActionSpec.Equals(ActionSpec.MakeContinuous(2))); + Assert.IsTrue(actuators[1].ActionSpec.NumDiscreteActions == 1); + + var actuatorComponentActionSpec = m_ActuatorComponent.ActionSpec; + Assert.IsTrue(actuatorComponentActionSpec.BranchSizes.SequenceEqual(new[] {2})); + Assert.IsTrue(actuatorComponentActionSpec.NumContinuousActions == 2); + } + + [Test] + public void InputActuatorComponentTestGenerateActuatorsFromAsset() + { + // Use the Assert class to test conditions. + m_PlayerInput.actions = m_Asset; + m_PlayerInput.defaultActionMap = m_Asset.actionMaps[0].name; + var inputActionMap = m_Asset.FindActionMap(m_PlayerInput.defaultActionMap); + InputActuatorComponent.RegisterLayoutBuilder( + inputActionMap, + "TestLayout"); + + var device = InputSystem.AddDevice("TestLayout"); + + var actuators = InputActuatorComponent.CreateActuatorsFromMap(inputActionMap, m_BehaviorParameters, device); + Assert.IsTrue(actuators.Length == 2); + Assert.IsTrue(actuators[0].ActionSpec.Equals(ActionSpec.MakeContinuous(2))); + Assert.IsTrue(actuators[1].ActionSpec.NumDiscreteActions == 1); + } + + [Test] + public void InputActuatorComponentTestCreateDevice() + { + // Use the Assert class to test conditions. + m_PlayerInput.actions = m_Asset; + m_PlayerInput.defaultActionMap = m_Asset.actionMaps[0].name; + + // need to call this to load the layout in the input system + InputActuatorComponent.RegisterLayoutBuilder( + m_Asset.FindActionMap(m_PlayerInput.defaultActionMap), + "TestLayout"); + + InputSystem.LoadLayout("TestLayout"); + var device = InputSystem.AddDevice("TestLayout"); + Assert.AreEqual("TestLayout", device.layout); + Assert.IsTrue(device.children.Count == 2); + Assert.AreEqual(device.children[0].path, $"{device.path}{InputControlPath.Separator}movement"); + Assert.AreEqual(device.children[1].path, $"{device.path}{InputControlPath.Separator}jump"); + Assert.NotNull(InputSystem.LoadLayout("TestLayout")); + } + } +} +#endif // MLA_INPUT_TESTS diff --git a/com.unity.ml-agents.extensions/Tests/Runtime/Input/InputActuatorComponentTests.cs.meta b/com.unity.ml-agents.extensions/Tests/Runtime/Input/InputActuatorComponentTests.cs.meta new file mode 100644 index 0000000000..a506e544f5 --- /dev/null +++ b/com.unity.ml-agents.extensions/Tests/Runtime/Input/InputActuatorComponentTests.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1894abc7110a4ab39719618db7eb55a9 +timeCreated: 1612910162 \ No newline at end of file diff --git a/com.unity.ml-agents.extensions/Tests/Runtime/Input/TestPushBlockActions.cs b/com.unity.ml-agents.extensions/Tests/Runtime/Input/TestPushBlockActions.cs new file mode 100644 index 0000000000..61ec3325da --- /dev/null +++ b/com.unity.ml-agents.extensions/Tests/Runtime/Input/TestPushBlockActions.cs @@ -0,0 +1,318 @@ +#if MLA_INPUT_TESTS && UNITY_2019_4_OR_NEWER +//------------------------------------------------------------------------------ +// +// This code was auto-generated by com.unity.inputsystem:InputActionCodeGenerator +// version 1.1.0 +// from Assets/ML-Agents/Examples/PushBlock/TestPushBlockActions.inputactions +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine.InputSystem; +using UnityEngine.InputSystem.Utilities; + +public partial class TestPushBlockActions : IInputActionCollection2, IDisposable +{ + public InputActionAsset asset { get; } + public TestPushBlockActions() + { + asset = InputActionAsset.FromJson(@"{ + ""name"": ""TestPushBlockActions"", + ""maps"": [ + { + ""name"": ""Movement"", + ""id"": ""03a2e5d4-ae81-47f1-a575-0779fb7da538"", + ""actions"": [ + { + ""name"": ""movement"", + ""type"": ""Value"", + ""id"": ""5f47cbc6-de46-4d33-93e2-2b1af4f5996d"", + ""expectedControlType"": ""Vector2"", + ""processors"": """", + ""interactions"": """" + }, + { + ""name"": ""jump"", + ""type"": ""Value"", + ""id"": ""ca5eb833-5dfb-4b7c-880d-6118bd5d1378"", + ""expectedControlType"": ""Integer"", + ""processors"": """", + ""interactions"": """" + } + ], + ""bindings"": [ + { + ""name"": ""gamepad_move"", + ""id"": ""477500ef-6d32-4b84-b9f8-158f18bcb906"", + ""path"": ""2DVector"", + ""interactions"": """", + ""processors"": """", + ""groups"": """", + ""action"": ""movement"", + ""isComposite"": true, + ""isPartOfComposite"": false + }, + { + ""name"": ""up"", + ""id"": ""6d2537b8-2266-4a50-8575-fb0fe310daa5"", + ""path"": ""/dpad/up"", + ""interactions"": """", + ""processors"": """", + ""groups"": ""Keyboard"", + ""action"": ""movement"", + ""isComposite"": false, + ""isPartOfComposite"": true + }, + { + ""name"": ""down"", + ""id"": ""50584c83-beb6-4e90-a453-a635c03a761e"", + ""path"": ""/dpad/down"", + ""interactions"": """", + ""processors"": """", + ""groups"": ""Keyboard"", + ""action"": ""movement"", + ""isComposite"": false, + ""isPartOfComposite"": true + }, + { + ""name"": ""left"", + ""id"": ""44408b8f-27e7-4c6d-b078-7536ba020d1a"", + ""path"": ""/dpad/left"", + ""interactions"": """", + ""processors"": """", + ""groups"": ""Keyboard"", + ""action"": ""movement"", + ""isComposite"": false, + ""isPartOfComposite"": true + }, + { + ""name"": ""right"", + ""id"": ""f5681423-d3e3-41a5-b85e-0a7642c774aa"", + ""path"": ""/dpad/right"", + ""interactions"": """", + ""processors"": """", + ""groups"": ""Keyboard"", + ""action"": ""movement"", + ""isComposite"": false, + ""isPartOfComposite"": true + }, + { + ""name"": ""keyboard_move"", + ""id"": ""6bcba4bf-5ce0-4005-9e6a-0de2487211b0"", + ""path"": ""2DVector"", + ""interactions"": """", + ""processors"": """", + ""groups"": """", + ""action"": ""movement"", + ""isComposite"": true, + ""isPartOfComposite"": false + }, + { + ""name"": ""up"", + ""id"": ""63da699e-b354-4e63-b0f8-26fb92abea41"", + ""path"": ""/w"", + ""interactions"": """", + ""processors"": """", + ""groups"": ""Keyboard"", + ""action"": ""movement"", + ""isComposite"": false, + ""isPartOfComposite"": true + }, + { + ""name"": ""down"", + ""id"": ""39409748-9002-4aff-9a09-cdc05b9708ad"", + ""path"": ""/s"", + ""interactions"": """", + ""processors"": """", + ""groups"": ""Keyboard"", + ""action"": ""movement"", + ""isComposite"": false, + ""isPartOfComposite"": true + }, + { + ""name"": ""left"", + ""id"": ""0afe45fc-dc45-4310-9c73-7dc3c503addf"", + ""path"": ""/a"", + ""interactions"": """", + ""processors"": """", + ""groups"": ""Keyboard"", + ""action"": ""movement"", + ""isComposite"": false, + ""isPartOfComposite"": true + }, + { + ""name"": ""right"", + ""id"": ""69fe0335-9e0c-495d-a90d-4b0fcbfd2b34"", + ""path"": ""/d"", + ""interactions"": """", + ""processors"": """", + ""groups"": ""Keyboard"", + ""action"": ""movement"", + ""isComposite"": false, + ""isPartOfComposite"": true + }, + { + ""name"": """", + ""id"": ""ab696218-63cd-4eb8-9fe1-48a68e32e92f"", + ""path"": ""/space"", + ""interactions"": """", + ""processors"": """", + ""groups"": ""Keyboard"", + ""action"": ""jump"", + ""isComposite"": false, + ""isPartOfComposite"": false + }, + { + ""name"": """", + ""id"": ""7adcb138-5175-4cc4-addc-d2b02cb5f0de"", + ""path"": ""/buttonSouth"", + ""interactions"": """", + ""processors"": """", + ""groups"": ""Keyboard"", + ""action"": ""jump"", + ""isComposite"": false, + ""isPartOfComposite"": false + } + ] + } + ], + ""controlSchemes"": [ + { + ""name"": ""Keyboard"", + ""bindingGroup"": ""Keyboard"", + ""devices"": [ + { + ""devicePath"": """", + ""isOptional"": true, + ""isOR"": false + }, + { + ""devicePath"": """", + ""isOptional"": true, + ""isOR"": false + } + ] + } + ] +}"); + // Movement + m_Movement = asset.FindActionMap("Movement", throwIfNotFound: true); + m_Movement_movement = m_Movement.FindAction("movement", throwIfNotFound: true); + m_Movement_jump = m_Movement.FindAction("jump", throwIfNotFound: true); + } + + public void Dispose() + { + UnityEngine.Object.Destroy(asset); + } + + public InputBinding? bindingMask + { + get => asset.bindingMask; + set => asset.bindingMask = value; + } + + public ReadOnlyArray? devices + { + get => asset.devices; + set => asset.devices = value; + } + + public ReadOnlyArray controlSchemes => asset.controlSchemes; + + public bool Contains(InputAction action) + { + return asset.Contains(action); + } + + public IEnumerator GetEnumerator() + { + return asset.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public void Enable() + { + asset.Enable(); + } + + public void Disable() + { + asset.Disable(); + } + public IEnumerable bindings => asset.bindings; + + public InputAction FindAction(string actionNameOrId, bool throwIfNotFound = false) + { + return asset.FindAction(actionNameOrId, throwIfNotFound); + } + public int FindBinding(InputBinding bindingMask, out InputAction action) + { + return asset.FindBinding(bindingMask, out action); + } + + // Movement + private readonly InputActionMap m_Movement; + private IMovementActions m_MovementActionsCallbackInterface; + private readonly InputAction m_Movement_movement; + private readonly InputAction m_Movement_jump; + public struct MovementActions + { + private TestPushBlockActions m_Wrapper; + public MovementActions(TestPushBlockActions wrapper) { m_Wrapper = wrapper; } + public InputAction @movement => m_Wrapper.m_Movement_movement; + public InputAction @jump => m_Wrapper.m_Movement_jump; + public InputActionMap Get() { return m_Wrapper.m_Movement; } + public void Enable() { Get().Enable(); } + public void Disable() { Get().Disable(); } + public bool enabled => Get().enabled; + public static implicit operator InputActionMap(MovementActions set) { return set.Get(); } + public void SetCallbacks(IMovementActions instance) + { + if (m_Wrapper.m_MovementActionsCallbackInterface != null) + { + @movement.started -= m_Wrapper.m_MovementActionsCallbackInterface.OnMovement; + @movement.performed -= m_Wrapper.m_MovementActionsCallbackInterface.OnMovement; + @movement.canceled -= m_Wrapper.m_MovementActionsCallbackInterface.OnMovement; + @jump.started -= m_Wrapper.m_MovementActionsCallbackInterface.OnJump; + @jump.performed -= m_Wrapper.m_MovementActionsCallbackInterface.OnJump; + @jump.canceled -= m_Wrapper.m_MovementActionsCallbackInterface.OnJump; + } + m_Wrapper.m_MovementActionsCallbackInterface = instance; + if (instance != null) + { + @movement.started += instance.OnMovement; + @movement.performed += instance.OnMovement; + @movement.canceled += instance.OnMovement; + @jump.started += instance.OnJump; + @jump.performed += instance.OnJump; + @jump.canceled += instance.OnJump; + } + } + } + public MovementActions @Movement => new MovementActions(this); + private int m_KeyboardSchemeIndex = -1; + public InputControlScheme KeyboardScheme + { + get + { + if (m_KeyboardSchemeIndex == -1) m_KeyboardSchemeIndex = asset.FindControlSchemeIndex("Keyboard"); + return asset.controlSchemes[m_KeyboardSchemeIndex]; + } + } + public interface IMovementActions + { + void OnMovement(InputAction.CallbackContext context); + void OnJump(InputAction.CallbackContext context); + } +} +#endif // MLA_INPUT_TESTS && UNITY_2019_4_OR_NEWER diff --git a/com.unity.ml-agents.extensions/Tests/Runtime/Input/TestPushBlockActions.cs.meta b/com.unity.ml-agents.extensions/Tests/Runtime/Input/TestPushBlockActions.cs.meta new file mode 100644 index 0000000000..18f5eb644a --- /dev/null +++ b/com.unity.ml-agents.extensions/Tests/Runtime/Input/TestPushBlockActions.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 87c358a50faa48c1bba3e5f48a5cb75d +timeCreated: 1613172830 \ No newline at end of file diff --git a/com.unity.ml-agents.extensions/Tests/Runtime/Input/Unity.ML-Agents.Extensions.Input.Tests.Runtime.asmdef b/com.unity.ml-agents.extensions/Tests/Runtime/Input/Unity.ML-Agents.Extensions.Input.Tests.Runtime.asmdef new file mode 100644 index 0000000000..4f54b2d4b4 --- /dev/null +++ b/com.unity.ml-agents.extensions/Tests/Runtime/Input/Unity.ML-Agents.Extensions.Input.Tests.Runtime.asmdef @@ -0,0 +1,31 @@ +{ + "name": "Unity.ML-Agents.Extensions.Input.Tests.Runtime", + "references": [ + "Unity.ML-Agents", + "Unity.ML-Agents.Extensions.Input", + "Unity.InputSystem.TestFramework", + "UnityEngine.TestRunner", + "UnityEditor.TestRunner", + "Unity.InputSystem", + "Unity.Barracuda" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": true, + "precompiledReferences": [ + "nunit.framework.dll" + ], + "autoReferenced": true, + "defineConstraints": [ + "UNITY_INCLUDE_TESTS" + ], + "versionDefines": [ + { + "name": "com.unity.inputsystem", + "expression": "1.1.0-preview", + "define": "MLA_INPUT_TESTS" + } + ], + "noEngineReferences": false +} \ No newline at end of file diff --git a/com.unity.ml-agents.extensions/Tests/Runtime/Input/Unity.ML-Agents.Extensions.Input.Tests.Runtime.asmdef.meta b/com.unity.ml-agents.extensions/Tests/Runtime/Input/Unity.ML-Agents.Extensions.Input.Tests.Runtime.asmdef.meta new file mode 100644 index 0000000000..874e8ded0e --- /dev/null +++ b/com.unity.ml-agents.extensions/Tests/Runtime/Input/Unity.ML-Agents.Extensions.Input.Tests.Runtime.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: ca257bdcba9544f71baf0c291c36b05a +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.ml-agents/CHANGELOG.md b/com.unity.ml-agents/CHANGELOG.md index e97c9d3346..fe0c7a0270 100755 --- a/com.unity.ml-agents/CHANGELOG.md +++ b/com.unity.ml-agents/CHANGELOG.md @@ -52,6 +52,10 @@ and this project adheres to - `InferenceDevice.Burst` was added, indicating that Agent's model will be run using Barracuda's Burst backend. This is the default for new Agents, but existing ones that use `InferenceDevice.CPU` should update to `InferenceDevice.Burst`. (#4925) +- Add an InputActuatorComponent to allow the generation of Agent action spaces from an InputActionAsset. + Projects wanting to use this feature will need to add the + [Input System Package](https://docs.unity3d.com/Packages/com.unity.inputsystem@1.1/manual/index.html) + at version 1.1.0-preview.3 or later. (#4881) #### ml-agents / ml-agents-envs / gym-unity (Python) - Tensorboard now logs the Environment Reward as both a scalar and a histogram. (#4878) diff --git a/com.unity.ml-agents/Runtime/Actuators/ActionSpec.cs b/com.unity.ml-agents/Runtime/Actuators/ActionSpec.cs index 1ea63f51ab..c5aee00cf4 100644 --- a/com.unity.ml-agents/Runtime/Actuators/ActionSpec.cs +++ b/com.unity.ml-agents/Runtime/Actuators/ActionSpec.cs @@ -1,6 +1,6 @@ using System; +using System.Collections.Generic; using System.Linq; -using Unity.MLAgents.Policies; using UnityEngine; namespace Unity.MLAgents.Actuators @@ -76,7 +76,7 @@ public static ActionSpec MakeDiscrete(params int[] branchSizes) public ActionSpec(int numContinuousActions = 0, int[] discreteBranchSizes = null) { m_NumContinuousActions = numContinuousActions; - BranchSizes = discreteBranchSizes; + BranchSizes = discreteBranchSizes ?? Array.Empty(); } /// @@ -94,5 +94,46 @@ internal void CheckAllContinuousOrDiscrete() ); } } + + /// + /// Combines a list of actions specs and allocates a new array of branch sizes if needed. + /// + /// The list of action specs to combine. + /// An ActionSpec which represents the aggregate of the ActionSpecs passed in. + public static ActionSpec Combine(params ActionSpec[] specs) + { + var numContinuous = 0; + var numDiscrete = 0; + for (var i = 0; i < specs.Length; i++) + { + var spec = specs[i]; + numContinuous += spec.NumContinuousActions; + numDiscrete += spec.NumDiscreteActions; + } + + if (numDiscrete <= 0) + { + return MakeContinuous(numContinuous); + } + + var branchSizes = new int[numDiscrete]; + var offset = 0; + for (var i = 0; i < specs.Length; i++) + { + var spec = specs[i]; + if (spec.BranchSizes.Length == 0) + { + continue; + } + var branchSizesLength = spec.BranchSizes.Length; + Array.Copy(spec.BranchSizes, + 0, + branchSizes, + offset, + branchSizesLength); + offset += branchSizesLength; + } + return new ActionSpec(numContinuous, branchSizes); + } } } diff --git a/com.unity.ml-agents/Runtime/Actuators/ActuatorManager.cs b/com.unity.ml-agents/Runtime/Actuators/ActuatorManager.cs index 76e9368916..fb7c3724d9 100644 --- a/com.unity.ml-agents/Runtime/Actuators/ActuatorManager.cs +++ b/com.unity.ml-agents/Runtime/Actuators/ActuatorManager.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using UnityEngine; +using UnityEngine.Profiling; namespace Unity.MLAgents.Actuators { @@ -157,9 +158,11 @@ public ActionSpec GetCombinedActionSpec() /// actions for the IActuators in this list. public void UpdateActions(ActionBuffers actions) { + Profiler.BeginSample("ActuatorManager.UpdateActions"); ReadyActuatorsForExecution(); UpdateActionArray(actions.ContinuousActions, StoredActions.ContinuousActions); UpdateActionArray(actions.DiscreteActions, StoredActions.DiscreteActions); + Profiler.EndSample(); } static void UpdateActionArray(ActionSegment sourceActionBuffer, ActionSegment destination) @@ -212,6 +215,7 @@ public void WriteActionMask() /// public void ApplyHeuristic(in ActionBuffers actionBuffersOut) { + Profiler.BeginSample("ActuatorManager.ApplyHeuristic"); var continuousStart = 0; var discreteStart = 0; for (var i = 0; i < m_Actuators.Count; i++) @@ -246,6 +250,7 @@ public void ApplyHeuristic(in ActionBuffers actionBuffersOut) continuousStart += numContinuousActions; discreteStart += numDiscreteActions; } + Profiler.EndSample(); } /// @@ -255,6 +260,7 @@ public void ApplyHeuristic(in ActionBuffers actionBuffersOut) /// public void ExecuteActions() { + Profiler.BeginSample("ActuatorManager.ExecuteActions"); ReadyActuatorsForExecution(); var continuousStart = 0; var discreteStart = 0; @@ -264,6 +270,11 @@ public void ExecuteActions() var numContinuousActions = actuator.ActionSpec.NumContinuousActions; var numDiscreteActions = actuator.ActionSpec.NumDiscreteActions; + if (numContinuousActions == 0 && numDiscreteActions == 0) + { + continue; + } + var continuousActions = ActionSegment.Empty; if (numContinuousActions > 0) { @@ -284,6 +295,7 @@ public void ExecuteActions() continuousStart += numContinuousActions; discreteStart += numDiscreteActions; } + Profiler.EndSample(); } /// diff --git a/com.unity.ml-agents/Runtime/Actuators/VectorActuator.cs b/com.unity.ml-agents/Runtime/Actuators/VectorActuator.cs index 1b18300f56..b00ed3bf28 100644 --- a/com.unity.ml-agents/Runtime/Actuators/VectorActuator.cs +++ b/com.unity.ml-agents/Runtime/Actuators/VectorActuator.cs @@ -1,3 +1,5 @@ +using UnityEngine.Profiling; + namespace Unity.MLAgents.Actuators { /// @@ -69,13 +71,17 @@ public void ResetData() /// public void OnActionReceived(ActionBuffers actionBuffers) { - ActionBuffers = actionBuffers; - m_ActionReceiver.OnActionReceived(ActionBuffers); + Profiler.BeginSample("VectorActuator.OnActionReceived"); + m_ActionBuffers = actionBuffers; + m_ActionReceiver.OnActionReceived(m_ActionBuffers); + Profiler.EndSample(); } public void Heuristic(in ActionBuffers actionBuffersOut) { + Profiler.BeginSample("VectorActuator.Heuristic"); m_HeuristicProvider?.Heuristic(actionBuffersOut); + Profiler.EndSample(); } /// diff --git a/com.unity.ml-agents/Runtime/Agent.deprecated.cs b/com.unity.ml-agents/Runtime/Agent.deprecated.cs index 9c8a8d5efb..fd9d6a1a88 100644 --- a/com.unity.ml-agents/Runtime/Agent.deprecated.cs +++ b/com.unity.ml-agents/Runtime/Agent.deprecated.cs @@ -1,5 +1,6 @@ using System; using UnityEngine; +using UnityEngine.Profiling; namespace Unity.MLAgents { @@ -42,6 +43,7 @@ public virtual void OnActionReceived(float[] vectorAction) { } [Obsolete("GetAction has been deprecated, please use GetStoredActionBuffers instead.")] public float[] GetAction() { + Profiler.BeginSample("Agent.GetAction.Deprecated"); var actionSpec = m_PolicyFactory.BrainParameters.ActionSpec; // For continuous and discrete actions together, this shouldn't be called because we can only return one. if (actionSpec.NumContinuousActions > 0 && actionSpec.NumDiscreteActions > 0) @@ -54,10 +56,8 @@ public float[] GetAction() { return storedAction.ContinuousActions.Array; } - else - { - return Array.ConvertAll(storedAction.DiscreteActions.Array, x => (float)x); - } + Profiler.EndSample(); + return Array.ConvertAll(storedAction.DiscreteActions.Array, x => (float)x); } } } diff --git a/com.unity.ml-agents/Runtime/AssemblyInfo.cs b/com.unity.ml-agents/Runtime/AssemblyInfo.cs index 4bc7a8bbb0..38b5953d5b 100644 --- a/com.unity.ml-agents/Runtime/AssemblyInfo.cs +++ b/com.unity.ml-agents/Runtime/AssemblyInfo.cs @@ -3,3 +3,4 @@ [assembly: InternalsVisibleTo("Unity.ML-Agents.Editor.Tests")] [assembly: InternalsVisibleTo("Unity.ML-Agents.Editor")] [assembly: InternalsVisibleTo("Unity.ML-Agents.Extensions")] +[assembly: InternalsVisibleTo("Unity.ML-Agents.Extensions.Input")] diff --git a/com.unity.ml-agents/Runtime/Policies/BehaviorParameters.cs b/com.unity.ml-agents/Runtime/Policies/BehaviorParameters.cs index cf82437357..09789eb35d 100644 --- a/com.unity.ml-agents/Runtime/Policies/BehaviorParameters.cs +++ b/com.unity.ml-agents/Runtime/Policies/BehaviorParameters.cs @@ -74,6 +74,10 @@ public class BehaviorParameters : MonoBehaviour [HideInInspector, SerializeField] BrainParameters m_BrainParameters = new BrainParameters(); + public delegate void PolicyUpdated(bool isInHeuristicMode); + + internal event PolicyUpdated OnPolicyUpdated; + /// /// The associated for this behavior. /// @@ -196,6 +200,11 @@ public string FullyQualifiedBehaviorName get { return m_BehaviorName + "?team=" + TeamId; } } + void Awake() + { + OnPolicyUpdated += mode => { }; + } + internal IPolicy GeneratePolicy(ActionSpec actionSpec, ActuatorManager actuatorManager) { switch (m_BehaviorType) @@ -232,6 +241,24 @@ internal IPolicy GeneratePolicy(ActionSpec actionSpec, ActuatorManager actuatorM } } + /// + /// Query the behavior parameters in order to see if the Agent is running in Heuristic Mode. + /// + /// true if the Agent is running in Heuristic mode. + public bool IsInHeuristicMode() + { + if (BehaviorType == BehaviorType.HeuristicOnly) + { + return true; + } + + return BehaviorType == BehaviorType.Default && + ReferenceEquals(Model, null) && + (!Academy.IsInitialized || + Academy.IsInitialized && + !Academy.Instance.IsCommunicatorOn); + } + internal void UpdateAgentPolicy() { var agent = GetComponent(); @@ -240,6 +267,7 @@ internal void UpdateAgentPolicy() return; } agent.ReloadPolicy(); + OnPolicyUpdated?.Invoke(IsInHeuristicMode()); } } } diff --git a/com.unity.ml-agents/Tests/Editor/Actuators/ActionSpecTests.cs b/com.unity.ml-agents/Tests/Editor/Actuators/ActionSpecTests.cs new file mode 100644 index 0000000000..78c4fbeff8 --- /dev/null +++ b/com.unity.ml-agents/Tests/Editor/Actuators/ActionSpecTests.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using Unity.MLAgents.Actuators; +using UnityEngine.TestTools.Constraints; +using Is = UnityEngine.TestTools.Constraints.Is; + +namespace Unity.MLAgents.Tests.Actuators +{ + [TestFixture] + public class ActionSpecTests + { + [Test] + public void ActionSpecCombineTest() + { + var as0 = new ActionSpec(3, new[] { 3, 2, 1 }); + var as1 = new ActionSpec(1, new[] { 35, 122, 1, 3, 8, 3 }); + + var as0NumCon = 3; + var as0NumDis = as0.NumDiscreteActions; + var as1NumCon = 1; + var as1NumDis = as1.NumDiscreteActions; + var branchSizes = new List(); + branchSizes.AddRange(as0.BranchSizes); + branchSizes.AddRange(as1.BranchSizes); + + var asc = ActionSpec.Combine(as0, as1); + + Assert.AreEqual(as0NumCon + as1NumCon, asc.NumContinuousActions); + Assert.AreEqual(as0NumDis + as1NumDis, asc.NumDiscreteActions); + Assert.IsTrue(branchSizes.ToArray().SequenceEqual(asc.BranchSizes)); + + as0 = new ActionSpec(3); + as1 = new ActionSpec(1); + asc = ActionSpec.Combine(as0, as1); + Assert.IsEmpty(asc.BranchSizes); + } + } +} diff --git a/com.unity.ml-agents/Tests/Editor/Actuators/ActionSpecTests.cs.meta b/com.unity.ml-agents/Tests/Editor/Actuators/ActionSpecTests.cs.meta new file mode 100644 index 0000000000..18ebcbb881 --- /dev/null +++ b/com.unity.ml-agents/Tests/Editor/Actuators/ActionSpecTests.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 99d76ec04c944b75bc6b85abfff4ac4e +timeCreated: 1613680505 \ No newline at end of file diff --git a/com.unity.ml-agents/Tests/Editor/BehaviorParameterTests.cs b/com.unity.ml-agents/Tests/Editor/BehaviorParameterTests.cs index 3d79558935..7667eb7b9e 100644 --- a/com.unity.ml-agents/Tests/Editor/BehaviorParameterTests.cs +++ b/com.unity.ml-agents/Tests/Editor/BehaviorParameterTests.cs @@ -1,13 +1,17 @@ using NUnit.Framework; +using Unity.Barracuda; using Unity.MLAgents.Actuators; using UnityEngine; using Unity.MLAgents.Policies; +using UnityEditor; +using UnityEngine.TestTools; namespace Unity.MLAgents.Tests { [TestFixture] public class BehaviorParameterTests : IHeuristicProvider { + const string k_continuousONNXPath = "Packages/com.unity.ml-agents/Tests/Editor/TestModels/continuous2vis8vec2action.onnx"; public void Heuristic(in ActionBuffers actionsOut) { // No-op @@ -26,5 +30,47 @@ public void TestNoModelInferenceOnlyThrows() bp.GeneratePolicy(actionSpec, new ActuatorManager()); }); } + + [Test] + public void TestIsInHeuristicMode() + { + var gameObj = new GameObject(); + var bp = gameObj.AddComponent(); + bp.Model = null; + gameObj.AddComponent(); + bp.BehaviorType = BehaviorType.HeuristicOnly; + Assert.IsTrue(bp.IsInHeuristicMode()); + + bp.BehaviorType = BehaviorType.Default; + Assert.IsTrue(bp.IsInHeuristicMode()); + + bp.Model = ScriptableObject.CreateInstance(); + Assert.IsFalse(bp.IsInHeuristicMode()); + } + + [Test] + public void TestPolicyUpdateEventFired() + { + var gameObj = new GameObject(); + var bp = gameObj.AddComponent(); + gameObj.AddComponent().LazyInitialize(); + bp.OnPolicyUpdated += delegate (bool isInHeuristicMode) { Debug.Log($"OnPolicyChanged:{isInHeuristicMode}"); }; + bp.BehaviorType = BehaviorType.HeuristicOnly; + LogAssert.Expect(LogType.Log, $"OnPolicyChanged:{true}"); + + bp.BehaviorType = BehaviorType.Default; + LogAssert.Expect(LogType.Log, $"OnPolicyChanged:{true}"); + + Assert.Throws(() => + { + bp.BehaviorType = BehaviorType.InferenceOnly; + }); + + bp.Model = AssetDatabase.LoadAssetAtPath(k_continuousONNXPath); + LogAssert.Expect(LogType.Log, $"OnPolicyChanged:{false}"); + + bp.BehaviorType = BehaviorType.HeuristicOnly; + LogAssert.Expect(LogType.Log, $"OnPolicyChanged:{true}"); + } } }