浏览代码

Merge pull request #202 from Unity-Technologies/json-config-update

Json config update
/main
GitHub 4 年前
当前提交
09540832
共有 32 个文件被更改,包括 1303 次插入700 次删除
  1. 14
      com.unity.perception/CHANGELOG.md
  2. 1
      com.unity.perception/Editor/Randomization/Editors/PerceptionEditorAnalytics.cs
  3. 280
      com.unity.perception/Editor/Randomization/Editors/RunInUnitySimulationWindow.cs
  4. 54
      com.unity.perception/Editor/Randomization/Editors/ScenarioBaseEditor.cs
  5. 48
      com.unity.perception/Editor/Randomization/Uxml/RunInUnitySimulationWindow.uxml
  6. 7
      com.unity.perception/Editor/Randomization/Uxml/ScenarioBaseElement.uxml
  7. 5
      com.unity.perception/Editor/Randomization/VisualElements/Randomizer/AddRandomizerMenu.cs
  8. 8
      com.unity.perception/Editor/Randomization/VisualElements/Randomizer/RandomizerList.cs
  9. 205
      com.unity.perception/Runtime/Randomization/Scenarios/Scenario.cs
  10. 337
      com.unity.perception/Runtime/Randomization/Scenarios/ScenarioBase.cs
  11. 24
      com.unity.perception/Runtime/Randomization/Scenarios/UnitySimulationScenario.cs
  12. 2
      com.unity.perception/Tests/Editor/RandomizerEditorTests.cs
  13. 10
      com.unity.perception/Tests/Runtime/Randomization/RandomizerTests/RandomizerTests.cs
  14. 18
      com.unity.perception/Editor/Randomization/Uss/RunInUnitySimulationWindowStyles.uss
  15. 3
      com.unity.perception/Editor/Randomization/Uss/RunInUnitySimulationWindowStyles.uss.meta
  16. 3
      com.unity.perception/Runtime/Randomization/Scenarios/Serialization.meta
  17. 3
      com.unity.perception/Tests/Runtime/Randomization/ScenarioTests.meta
  18. 188
      com.unity.perception/Runtime/Randomization/Scenarios/Serialization/JsonConverters.cs
  19. 3
      com.unity.perception/Runtime/Randomization/Scenarios/Serialization/JsonConverters.cs.meta
  20. 3
      com.unity.perception/Runtime/Randomization/Scenarios/Serialization/SerializationStructures.cs.meta
  21. 3
      com.unity.perception/Runtime/Randomization/Scenarios/Serialization/ScenarioSerializer.cs.meta
  22. 280
      com.unity.perception/Runtime/Randomization/Scenarios/Serialization/ScenarioSerializer.cs
  23. 95
      com.unity.perception/Runtime/Randomization/Scenarios/Serialization/SerializationStructures.cs
  24. 3
      com.unity.perception/Tests/Runtime/Randomization/ScenarioTests/Resources.meta
  25. 67
      com.unity.perception/Tests/Runtime/Randomization/ScenarioTests/Resources/SampleScenarioConfiguration.json
  26. 3
      com.unity.perception/Tests/Runtime/Randomization/ScenarioTests/Resources/SampleScenarioConfiguration.json.meta
  27. 159
      com.unity.perception/Tests/Runtime/Randomization/ScenarioTests/ScenarioTests.cs
  28. 177
      com.unity.perception/Tests/Runtime/Randomization/ScenarioTests.cs
  29. 0
      /com.unity.perception/Tests/Runtime/Randomization/ScenarioTests/ScenarioTests.cs.meta

14
com.unity.perception/CHANGELOG.md


### Added
Scenario serialization has been updated to include scalar values on randomizers and parameters.
Added new ScenarioBase virtual lifecycle hooks: OnAwake, OnStart, OnComplete, and OnIdle.
ScenarioBase's Awake, Start, and Update methods are now private. The newly added virtual lifecycle hooks are to be used as replacements.
Improved Run Unity Simulation window UI.
The Run Unity Simulation window now accepts option scenario JSON configurations to override existing scenario editor settings.
ScenarioBase's Get and Create randomizer methods have been augmented or replaced with more generic list index style accessors.
The scenario inspector buttons serialize and deserialize have been refactored to open a file explorer generate and import JSON configurations.
### Deprecated

1
com.unity.perception/Editor/Randomization/Editors/PerceptionEditorAnalytics.cs


using System;
using JetBrains.Annotations;
using UnityEngine;
using UnityEngine.Analytics;
namespace UnityEditor.Perception.Randomization

280
com.unity.perception/Editor/Randomization/Editors/RunInUnitySimulationWindow.cs


using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;

class RunInUnitySimulationWindow : EditorWindow
{
string m_BuildDirectory;
SysParamDefinition[] m_SysParamDefinitions;
ObjectField m_MainSceneField;
Button m_RunButton;
ObjectField m_ScenarioField;
SysParamDefinition m_SysParam;
ToolbarMenu m_SysParamMenu;
int m_SysParamIndex;
ObjectField m_ScenarioConfigField;
Button m_RunButton;
Label m_PrevRunNameLabel;
Label m_ProjectIdLabel;
Label m_PrevExecutionIdLabel;
RunParameters m_RunParameters;
[MenuItem("Window/Run in Unity Simulation")]
static void ShowWindow()

}
}
/// <summary>
/// Enables a visual element to remember values between editor sessions
/// </summary>
/// <param name="element">The visual element to enable view data for</param>
static void SetViewDataKey(VisualElement element)
{
element.viewDataKey = $"RunInUnitySimulation_{element.name}";
}
void CreateRunInUnitySimulationUI()
{
var root = rootVisualElement;

m_RunNameField = root.Q<TextField>("run-name");
SetViewDataKey(m_RunNameField);
SetViewDataKey(m_TotalIterationsField);
SetViewDataKey(m_InstanceCountField);
m_MainSceneField = root.Q<ObjectField>("main-scene");
m_MainSceneField.objectType = typeof(SceneAsset);
if (SceneManager.sceneCount > 0)
m_SysParamDefinitions = API.GetSysParams();
m_SysParamMenu = root.Q<ToolbarMenu>("sys-param");
for (var i = 0; i < m_SysParamDefinitions.Length; i++)
var path = SceneManager.GetSceneAt(0).path;
var asset = AssetDatabase.LoadAssetAtPath<SceneAsset>(path);
m_MainSceneField.value = asset;
}
m_ScenarioField = root.Q<ObjectField>("scenario");
m_ScenarioField.objectType = typeof(ScenarioBase);
m_ScenarioField.value = FindObjectOfType<ScenarioBase>();
var sysParamDefinitions = API.GetSysParams();
var sysParamMenu = root.Q<ToolbarMenu>("sys-param");
foreach (var definition in sysParamDefinitions)
sysParamMenu.menu.AppendAction(
definition.description,
var index = i;
var param = m_SysParamDefinitions[i];
m_SysParamMenu.menu.AppendAction(
param.description,
m_SysParam = definition;
sysParamMenu.text = definition.description;
m_SysParamIndex = index;
m_SysParamMenu.text = param.description;
}
sysParamMenu.text = sysParamDefinitions[0].description;
m_SysParam = sysParamDefinitions[0];
m_ScenarioConfigField = root.Q<ObjectField>("scenario-config");
m_ScenarioConfigField.objectType = typeof(TextAsset);
var configPath = PlayerPrefs.GetString("SimWindow/scenarioConfig");
if (configPath != string.Empty)
m_ScenarioConfigField.value = AssetDatabase.LoadAssetAtPath<TextAsset>(configPath);
m_PrevRunNameLabel = root.Q<Label>("prev-run-name");
m_ProjectIdLabel = root.Q<Label>("project-id");
m_PrevExecutionIdLabel = root.Q<Label>("execution-id");
var copyExecutionIdButton = root.Q<Button>("copy-execution-id");
copyExecutionIdButton.clicked += () =>
EditorGUIUtility.systemCopyBuffer = PlayerPrefs.GetString("SimWindow/prevExecutionId");
var copyProjectIdButton = root.Q<Button>("copy-project-id");
copyProjectIdButton.clicked += () =>
EditorGUIUtility.systemCopyBuffer = CloudProjectSettings.projectId;
SetFieldsFromPlayerPreferences();
}
void SetFieldsFromPlayerPreferences()
{
m_RunNameField.value = IncrementRunName(PlayerPrefs.GetString("SimWindow/runName"));
m_TotalIterationsField.value = PlayerPrefs.GetInt("SimWindow/totalIterations");
m_InstanceCountField.value = PlayerPrefs.GetInt("SimWindow/instanceCount");
m_SysParamIndex = PlayerPrefs.GetInt("SimWindow/sysParamIndex");
m_SysParamMenu.text = m_SysParamDefinitions[m_SysParamIndex].description;
m_PrevRunNameLabel.text = $"Run Name: {PlayerPrefs.GetString("SimWindow/runName")}";
m_ProjectIdLabel.text = $"Project ID: {CloudProjectSettings.projectId}";
m_PrevExecutionIdLabel.text = $"Execution ID: {PlayerPrefs.GetString("SimWindow/prevExecutionId")}";
}
static string IncrementRunName(string runName)
{
if (string.IsNullOrEmpty(runName))
return "Run0";
var stack = new Stack<char>();
var i = runName.Length - 1;
for (; i >= 0; i--)
{
if (!char.IsNumber(runName[i]))
break;
stack.Push(runName[i]);
}
if (stack.Count == 0)
return runName + "1";
var numericString = string.Concat(stack.ToArray());
var runVersion = int.Parse(numericString) + 1;
return runName.Substring(0, i + 1) + runVersion;
m_RunParameters = new RunParameters
{
runName = m_RunNameField.value,
totalIterations = m_TotalIterationsField.value,
instanceCount = m_InstanceCountField.value,
sysParamIndex = m_SysParamIndex,
scenarioConfig = (TextAsset)m_ScenarioConfigField.value,
currentOpenScenePath = SceneManager.GetSceneAt(0).path,
currentScenario = FindObjectOfType<ScenarioBase>()
};
m_TotalIterationsField.value,
m_InstanceCountField.value,
m_RunParameters.totalIterations,
m_RunParameters.instanceCount,
null);
try
{

void ValidateSettings()
{
if (string.IsNullOrEmpty(m_RunNameField.value))
if (string.IsNullOrEmpty(m_RunParameters.runName))
if (m_MainSceneField.value == null)
throw new MissingFieldException("Main scene unselected");
if (m_ScenarioField.value == null)
throw new MissingFieldException("Scenario unselected");
var scenario = (ScenarioBase)m_ScenarioField.value;
if (!StaticData.IsSubclassOfRawGeneric(typeof(UnitySimulationScenario<>), scenario.GetType()))
if (m_RunParameters.instanceCount <= 0)
throw new NotSupportedException("Invalid instance count specified");
if (m_RunParameters.totalIterations <= 0)
throw new NotSupportedException("Invalid total iteration count specified");
if (string.IsNullOrEmpty(m_RunParameters.currentOpenScenePath))
throw new MissingFieldException("Invalid scene path");
if (m_RunParameters.currentScenario == null)
throw new MissingFieldException(
"There is not a Unity Simulation compatible scenario present in the scene");
if (!StaticData.IsSubclassOfRawGeneric(
typeof(UnitySimulationScenario<>), m_RunParameters.currentScenario.GetType()))
if (m_RunParameters.scenarioConfig != null &&
Path.GetExtension(m_RunParameters.scenarioConfigAssetPath) != ".json")
throw new NotSupportedException(
"Scenario configuration must be a JSON text asset");
// Create build directory
var projectBuildDirectory = $"{m_BuildDirectory}/{m_RunNameField.value}";
var projectBuildDirectory = $"{m_BuildDirectory}/{m_RunParameters.runName}";
// Create Linux build
Debug.Log("Creating Linux build...");
scenes = new[] { AssetDatabase.GetAssetPath(m_MainSceneField.value) },
locationPathName = Path.Combine(projectBuildDirectory, $"{m_RunNameField.value}.x86_64"),
scenes = new[] { m_RunParameters.currentOpenScenePath },
locationPathName = Path.Combine(projectBuildDirectory, $"{m_RunParameters.runName}.x86_64"),
throw new Exception($"Build did not succeed: status = {summary.result}");
Debug.Log("Created Linux build");
throw new Exception($"The Linux build did not succeed: status = {summary.result}");
// Zip the build
Debug.Log("Starting to zip...");
Zip.DirectoryContents(projectBuildDirectory, m_RunNameField.value);
EditorUtility.DisplayProgressBar("Unity Simulation Run", "Zipping Linux build...", 0f);
Zip.DirectoryContents(projectBuildDirectory, m_RunParameters.runName);
Debug.Log("Created build zip");
List<AppParam> GenerateAppParamIds(CancellationToken token, float progressStart, float progressEnd)
List<AppParam> UploadAppParam()
var scenario = (ScenarioBase)m_ScenarioField.value;
var configuration = JObject.Parse(scenario.SerializeToJson());
var constants = configuration["constants"];
constants["totalIterations"] = m_TotalIterationsField.value;
constants["instanceCount"] = m_InstanceCountField.value;
var configuration = JObject.Parse(m_RunParameters.scenarioConfig != null
? File.ReadAllText(m_RunParameters.scenarioConfigAssetPath)
: m_RunParameters.currentScenario.SerializeToJson());
var progressIncrement = (progressEnd - progressStart) / m_InstanceCountField.value;
var constants = configuration["constants"];
constants["totalIterations"] = m_RunParameters.totalIterations;
constants["instanceCount"] = m_RunParameters.instanceCount;
for (var i = 0; i < m_InstanceCountField.value; i++)
var appParamName = $"{m_RunParameters.runName}";
var appParamsString = JsonConvert.SerializeObject(configuration, Formatting.Indented);
var appParamId = API.UploadAppParam(appParamName, appParamsString);
appParamIds.Add(new AppParam
if (token.IsCancellationRequested)
return null;
var appParamName = $"{m_RunNameField.value}_{i}";
constants["instanceIndex"] = i;
var appParamsString = JsonConvert.SerializeObject(configuration, Formatting.Indented);
var appParamId = API.UploadAppParam(appParamName, appParamsString);
appParamIds.Add(new AppParam
{
id = appParamId,
name = appParamName,
num_instances = 1
});
EditorUtility.DisplayProgressBar(
"Unity Simulation Run",
$"Uploading app-param-ids for instances: {i + 1}/{m_InstanceCountField.value}",
progressStart + progressIncrement * i);
}
id = appParamId,
name = appParamName,
num_instances = m_RunParameters.instanceCount
});
EditorUtility.DisplayProgressBar("Unity Simulation Run", "Uploading build...", 0.1f);
m_RunButton.SetEnabled(false);
m_RunButton.SetEnabled(false);
// Upload build
Debug.Log("Uploading build...");
m_RunNameField.value,
m_RunParameters.runName,
cancellationTokenSource: cancellationTokenSource);
Debug.Log($"Build upload complete: build id {buildId}");
var appParams = GenerateAppParamIds(token, 0.1f, 0.9f);
null, null,
cancellationTokenSource,
progress =>
{
EditorUtility.DisplayProgressBar(
"Unity Simulation Run", "Uploading build...", progress * 0.90f);
});
Debug.Log("Run cancelled");
Debug.Log("The build upload process has been cancelled. Aborting Unity Simulation launch.");
Debug.Log($"Generated app-param ids: {appParams.Count}");
// Generate and upload app-params
EditorUtility.DisplayProgressBar("Unity Simulation Run", "Uploading app-params...", 0.90f);
var appParams = UploadAppParam();
EditorUtility.DisplayProgressBar("Unity Simulation Run", "Uploading run definition...", 0.9f);
// Upload run definition
EditorUtility.DisplayProgressBar("Unity Simulation Run", "Uploading run definition...", 0.95f);
name = m_RunNameField.value,
sys_param_id = m_SysParam.id,
name = m_RunParameters.runName,
sys_param_id = m_SysParamDefinitions[m_RunParameters.sysParamIndex].id,
Debug.Log($"Run definition upload complete: run definition id {runDefinitionId}");
EditorUtility.DisplayProgressBar("Unity Simulation Run", "Executing run...", 0.95f);
// Execute run
EditorUtility.DisplayProgressBar("Unity Simulation Run", "Executing run...", 1f);
cancellationTokenSource.Dispose();
Debug.Log($"Executing run: {run.executionId}");
// Cleanup
EditorUtility.ClearProgressBar();
PerceptionEditorAnalytics.ReportRunInUnitySimulationSucceeded(runGuid, run.executionId);
EditorUtility.ClearProgressBar();
// Set new Player Preferences
PlayerPrefs.SetString("SimWindow/runName", m_RunParameters.runName);
PlayerPrefs.SetString("SimWindow/prevExecutionId", run.executionId);
PlayerPrefs.SetInt("SimWindow/totalIterations", m_RunParameters.totalIterations);
PlayerPrefs.SetInt("SimWindow/instanceCount", m_RunParameters.instanceCount);
PlayerPrefs.SetInt("SimWindow/sysParamIndex", m_RunParameters.sysParamIndex);
PlayerPrefs.SetString("SimWindow/scenarioConfig",
m_RunParameters.scenarioConfig != null ? m_RunParameters.scenarioConfigAssetPath : string.Empty);
SetFieldsFromPlayerPreferences();
}
struct RunParameters
{
public string runName;
public int totalIterations;
public int instanceCount;
public int sysParamIndex;
public TextAsset scenarioConfig;
public string currentOpenScenePath;
public ScenarioBase currentScenario;
PerceptionEditorAnalytics.ReportRunInUnitySimulationSucceeded(runGuid, run.executionId);
public string scenarioConfigAssetPath => AssetDatabase.GetAssetPath(scenarioConfig);
}
}
}

54
com.unity.perception/Editor/Randomization/Editors/ScenarioBaseEditor.cs


using UnityEngine;
using System.IO;
using UnityEngine;
using Object = UnityEngine.Object;
namespace UnityEditor.Perception.Randomization
{

ScenarioBase m_Scenario;
SerializedObject m_SerializedObject;
const string k_ConfigFilePlayerPrefKey = "ScenarioBaseEditor/configFilePath";
public override VisualElement CreateInspectorGUI()
{
m_Scenario = (ScenarioBase)target;

CreatePropertyFields();
CheckIfConstantsExist();
var serializeConstantsButton = m_Root.Q<Button>("serialize");
serializeConstantsButton.clicked += () =>
var generateConfigButton = m_Root.Q<Button>("generate-json-config");
generateConfigButton.clicked += () =>
m_Scenario.SerializeToFile();
var filePath = GetSaveFilePath(
"Generate Scenario JSON Configuration", Application.dataPath,
"scenarioConfiguration", "json", k_ConfigFilePlayerPrefKey);
if (string.IsNullOrEmpty(filePath))
return;
m_Scenario.SerializeToFile(filePath);
var newConfigFileAsset = AssetDatabase.LoadAssetAtPath<Object>(m_Scenario.defaultConfigFileAssetPath);
EditorGUIUtility.PingObject(newConfigFileAsset);
EditorUtility.RevealInFinder(filePath);
PlayerPrefs.SetString(k_ConfigFilePlayerPrefKey, filePath);
var deserializeConstantsButton = m_Root.Q<Button>("deserialize");
var deserializeConstantsButton = m_Root.Q<Button>("import-json-config");
var filePath = GetOpenFilePath(
"Import Scenario JSON Configuration", Application.dataPath, "json", k_ConfigFilePlayerPrefKey);
if (string.IsNullOrEmpty(filePath))
return;
m_Scenario.DeserializeFromFile(m_Scenario.defaultConfigFilePath);
m_Scenario.DeserializeFromFile(filePath);
PlayerPrefs.SetString(k_ConfigFilePlayerPrefKey, filePath);
};
return m_Root;

m_InspectorPropertiesContainer.style.marginBottom = 0;
m_ConstantsListVisualContainer.style.display = new StyleEnum<DisplayStyle>(DisplayStyle.None);
}
}
static string GetSaveFilePath(
string title, string defaultDirectory, string defaultFileName, string fileExtension, string playerPrefKey)
{
var prevFilePath = PlayerPrefs.GetString(playerPrefKey);
var prevDirectory = defaultDirectory;
var prevFileName = defaultFileName;
if (File.Exists(prevFilePath))
{
prevDirectory = Path.GetDirectoryName(prevFilePath);
prevFileName = Path.GetFileNameWithoutExtension(prevFilePath);
}
return EditorUtility.SaveFilePanel(
title, prevDirectory, prevFileName, fileExtension);
}
static string GetOpenFilePath(string title, string defaultDirectory, string fileExtension, string playerPrefKey)
{
var prevFilePath = PlayerPrefs.GetString(playerPrefKey);
var prevDirectory = defaultDirectory;
if (File.Exists(prevFilePath))
prevDirectory = Path.GetDirectoryName(prevFilePath);
return EditorUtility.OpenFilePanel(title, prevDirectory, fileExtension);
}
}
}

48
com.unity.perception/Editor/Randomization/Uxml/RunInUnitySimulationWindow.uxml


<UXML xmlns="UnityEngine.UIElements" xmlns:editor="UnityEditor.UIElements">
<TextField name="run-name" label="Run Name"/>
<editor:IntegerField name="total-iterations" label="Total Iterations"/>
<editor:IntegerField name="instance-count" label="Instance Count" max-value="10000"/>
<editor:ObjectField name="main-scene" label="Main Scene" allow-scene-objects="false"/>
<editor:ObjectField name="scenario" label="Scenario"/>
<VisualElement class="unity-base-field">
<Label text="Sys-Param" class="unity-base-field__label"/>
<editor:ToolbarMenu name="sys-param" class="unity-base-field__input" style="border-width: 1px;"/>
</VisualElement>
<VisualElement style="align-items: center;">
<Button name="run-button" text="Build and Run" style="margin: 10px; padding: 2 20; font-size: 13px;"/>
<VisualElement style="margin: 2 4 2 4;">
<Style src="../Uss/RunInUnitySimulationWindowStyles.uss"/>
<VisualElement class="sim-window__container-outer">
<Label text="Simulation Parameters" class="sim-window__header-1"/>
<TextField name="run-name" label="Run Name"/>
<editor:IntegerField name="total-iterations" label="Total Iterations"
tooltip="The number of scenario iterations to execute"/>
<editor:IntegerField name="instance-count" label="Instance Count" max-value="10000"
tooltip="The number of instances to distribute the work load across"/>
<VisualElement class="unity-base-field"
tooltip="The compute resources configuration to execute the simulation with">
<Label text="Sys-Param" class="unity-base-field__label"/>
<editor:ToolbarMenu name="sys-param" class="unity-base-field__input" style="border-width: 1px;"/>
</VisualElement>
<Label text="Optional Configuration" class="sim-window__header-1" style="margin-top: 18px;"/>
<editor:ObjectField name="scenario-config" label="Scenario JSON Config" allow-scene-object="false"
tooltip="Selects a scenario JSON configuration to load during the run.
You can leave this option blank to use the scenario settings currently configured in the editor."/>
<VisualElement style="align-items: center; margin-top: 8px;">
<Button name="run-button" text="Build and Run" style="margin: 10px; padding: 2 20; font-size: 13px;"/>
</VisualElement>
</VisualElement>
<VisualElement class="sim-window__container-outer">
<Label text="Previous Run Information" class="sim-window__header-1"/>
<Label name="prev-run-name" text="Run Name: " class="sim-window__label-prev-result"/>
<Label name="project-id" text="Project ID: " class="sim-window__label-prev-result"/>
<Label name="execution-id" text="Execution ID: " class="sim-window__label-prev-result"/>
<VisualElement style="flex-direction: row; margin-top: 2px;">
<Button name="copy-execution-id" text="Copy Execution ID" style="flex-grow: 1; flex-shrink: 0;"/>
<Button name="copy-project-id" text="Copy Project ID" style="flex-grow: 1; flex-shrink: 0;"/>
</VisualElement>
</VisualElement>
</VisualElement>
</UXML>

7
com.unity.perception/Editor/Randomization/Uxml/ScenarioBaseElement.uxml


<TextElement
class="scenario__info-box"
text="Scenarios control the execution flow of your simulation by applying randomization parameters. Make sure to always have only one scenario active within your scene."/>
<Toggle label="Quit On Complete" tooltip="Quit the application when the scenario completes" binding-path="quitOnComplete" style="margin-top:5px"/>
<Button name="serialize" text="Serialize To Config File" style="flex-grow: 1;"
<Button name="generate-json-config" text="Generate JSON Config" style="flex-grow: 1;"
<Button name="deserialize" text="Deserialize From Config File" style="flex-grow: 1;"
tooltip="Deserializes scenario constants and randomizer settings from a scenario_configuration.json file located in the Assets/StreamingAssets project folder"/>
<Button name="import-json-config" text="Import JSON Config" style="flex-grow: 1;"
tooltip="Imports scenario constants and randomizer settings from a selected JSON file"/>
</VisualElement>
</VisualElement>
</VisualElement>

5
com.unity.perception/Editor/Randomization/VisualElements/Randomizer/AddRandomizerMenu.cs


m_MenuItemsMap.Add(string.Empty, rootList);
var randomizerTypeSet = new HashSet<Type>();
foreach (var randomizer in m_RandomizerList.scenario.m_Randomizers)
foreach (var randomizer in m_RandomizerList.scenario.randomizers)
randomizerTypeSet.Add(randomizer.GetType());
foreach (var randomizerType in StaticData.randomizerTypes)

var menuAttribute = (AddRandomizerMenuAttribute)Attribute.GetCustomAttribute(randomizerType, typeof(AddRandomizerMenuAttribute));
var menuAttribute = (AddRandomizerMenuAttribute)Attribute.GetCustomAttribute(
randomizerType, typeof(AddRandomizerMenuAttribute));
if (menuAttribute != null)
{
var pathItems = menuAttribute.menuPath.Split('/');

8
com.unity.perception/Editor/Randomization/VisualElements/Randomizer/RandomizerList.cs


public void RemoveRandomizer(RandomizerElement element)
{
Undo.RegisterCompleteObjectUndo(m_Property.serializedObject.targetObject, "Remove Randomizer");
scenario.RemoveRandomizer(element.randomizerType);
scenario.RemoveRandomizerAt(element.parent.IndexOf(element));
m_Property.serializedObject.Update();
RefreshList();
}

if (currentIndex == nextIndex)
return;
if (nextIndex > currentIndex)
nextIndex--;
scenario.ReorderRandomizer(currentIndex, nextIndex);
var randomizer = scenario.GetRandomizer(currentIndex);
scenario.RemoveRandomizerAt(currentIndex);
scenario.InsertRandomizer(nextIndex, randomizer);
m_Property.serializedObject.Update();
RefreshList();
}

205
com.unity.perception/Runtime/Randomization/Scenarios/Scenario.cs


using System;
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using UnityEngine.Perception.Randomization.Parameters;
using UnityEngine.Perception.Randomization.Randomizers;
using UnityEngine.Perception.Randomization.Samplers;
namespace UnityEngine.Perception.Randomization.Scenarios
{

/// <inheritdoc/>
public override ScenarioConstants genericConstants => constants;
/// <inheritdoc/>
public override string SerializeToJson()
{
var configObj = new JObject
{
["constants"] = SerializeConstants(),
["randomizers"] = SerializeRandomizers()
};
return JsonConvert.SerializeObject(configObj, Formatting.Indented);
}
JObject SerializeConstants()
{
var constantsObj = new JObject();
var constantsFields = constants.GetType().GetFields();
foreach (var constantsField in constantsFields)
constantsObj.Add(new JProperty(constantsField.Name, constantsField.GetValue(constants)));
return constantsObj;
}
JObject SerializeRandomizers()
{
var randomizersObj = new JObject();
foreach (var randomizer in m_Randomizers)
{
var randomizerObj = SerializeRandomizer(randomizer);
if (randomizerObj.Count > 0)
randomizersObj.Add(new JProperty(randomizer.GetType().Name, randomizerObj));
}
return randomizersObj;
}
static JObject SerializeRandomizer(Randomizer randomizer)
{
var randomizerObj = new JObject();
var parameterFields = randomizer.GetType().GetFields();
foreach (var parameterField in parameterFields)
{
if (!IsSubclassOfRawGeneric(typeof(NumericParameter<>), parameterField.FieldType))
continue;
var parameter = (Parameter)parameterField.GetValue(randomizer);
var parameterObj = SerializeParameter(parameter);
if (parameterObj.Count > 0)
randomizerObj.Add(new JProperty(parameterField.Name, parameterObj));
}
return randomizerObj;
}
static JObject SerializeParameter(Parameter parameter)
{
var parameterObj = new JObject();
var samplerFields = parameter.GetType().GetFields();
foreach (var samplerField in samplerFields)
{
if (samplerField.FieldType != typeof(ISampler))
continue;
var sampler = (ISampler)samplerField.GetValue(parameter);
var samplerObj = SerializeSampler(sampler);
parameterObj.Add(new JProperty(samplerField.Name, samplerObj));
}
return parameterObj;
}
static JObject SerializeSampler(ISampler sampler)
{
var samplerObj = new JObject();
var fields = sampler.GetType().GetFields();
foreach (var field in fields)
{
samplerObj.Add(new JProperty(field.Name, JToken.FromObject(field.GetValue(sampler))));
}
if (sampler.GetType() != typeof(ConstantSampler))
{
var rangeProperty = sampler.GetType().GetProperty("range");
if (rangeProperty != null)
{
var range = (FloatRange)rangeProperty.GetValue(sampler);
var rangeObj = new JObject
{
new JProperty("minimum", range.minimum),
new JProperty("maximum", range.maximum)
};
samplerObj.Add(new JProperty("range", rangeObj));
}
}
return samplerObj;
}
/// <inheritdoc/>
public override void DeserializeFromFile(string configFilePath)
{
if (string.IsNullOrEmpty(configFilePath) || !File.Exists(configFilePath))
Debug.Log($"No configuration file found at {defaultConfigFilePath}");
else
{
#if UNITY_EDITOR
Debug.Log($"Deserialized scenario configuration from <a href=\"file:///${configFilePath}\">{configFilePath}</a>. " +
"Using undo in the editor will revert these changes to your scenario.");
#else
Debug.Log($"Deserialized scenario configuration from <a href=\"file:///${configFilePath}\">{configFilePath}</a>");
#endif
var jsonText = File.ReadAllText(configFilePath);
DeserializeFromJson(jsonText);
}
}
/// <inheritdoc/>
public override void DeserializeFromJson(string json)
{
var jsonObj = JObject.Parse(json);
var constantsObj = (JObject)jsonObj["constants"];
DeserializeConstants(constantsObj);
var randomizersObj = (JObject)jsonObj["randomizers"];
DeserializeRandomizers(randomizersObj);
}
void DeserializeConstants(JObject constantsObj)
{
constants = constantsObj.ToObject<T>();
}
void DeserializeRandomizers(JObject randomizersObj)
{
var randomizerTypeMap = new Dictionary<string, Randomizer>();
foreach (var randomizer in randomizers)
randomizerTypeMap.Add(randomizer.GetType().Name, randomizer);
foreach (var randomizerPair in randomizersObj)
{
if (!randomizerTypeMap.ContainsKey(randomizerPair.Key))
continue;
var randomizer = randomizerTypeMap[randomizerPair.Key];
var randomizerObj = (JObject)randomizerPair.Value;
DeserializeRandomizer(randomizer, randomizerObj);
}
}
static void DeserializeRandomizer(Randomizer randomizer, JObject randomizerObj)
{
foreach (var parameterPair in randomizerObj)
{
var parameterField = randomizer.GetType().GetField(parameterPair.Key);
if (parameterField == null)
continue;
var parameter = (Parameter)parameterField.GetValue(randomizer);
var parameterObj = (JObject)parameterPair.Value;
DeserializeParameter(parameter, parameterObj);
}
}
static void DeserializeParameter(Parameter parameter, JObject parameterObj)
{
foreach (var samplerPair in parameterObj)
{
var samplerField = parameter.GetType().GetField(samplerPair.Key);
if (samplerField == null)
continue;
var sampler = (ISampler)samplerField.GetValue(parameter);
var samplerObj = (JObject)samplerPair.Value;
DeserializeSampler(sampler, samplerObj);
}
}
static void DeserializeSampler(ISampler sampler, JObject samplerObj)
{
foreach (var samplerFieldPair in samplerObj)
{
if (samplerFieldPair.Key == "range")
{
var rangeObj = (JObject)samplerFieldPair.Value;
var field = sampler.GetType().GetField(samplerFieldPair.Key);
var range = new FloatRange(rangeObj["minimum"].ToObject<float>(), rangeObj["maximum"].ToObject<float>());
field.SetValue(sampler, range);
}
else
{
var field = sampler.GetType().GetField(samplerFieldPair.Key);
if (field != null)
{
field.SetValue(sampler, JsonConvert.DeserializeObject(samplerFieldPair.Value.ToString(), field.FieldType));
}
}
}
}
static bool IsSubclassOfRawGeneric(Type generic, Type toCheck) {
while (toCheck != null && toCheck != typeof(object)) {
var cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck;
if (generic == cur) {
return true;
}
toCheck = toCheck.BaseType;
}
return false;
}
}
}

337
com.unity.perception/Runtime/Randomization/Scenarios/ScenarioBase.cs


using UnityEngine.Perception.Randomization.Parameters;
using UnityEngine.Perception.Randomization.Randomizers;
using UnityEngine.Perception.Randomization.Samplers;
using UnityEngine.Perception.Randomization.Scenarios.Serialization;
namespace UnityEngine.Perception.Randomization.Scenarios
{

{
const string k_ScenarioIterationMetricDefinitionId = "DB1B258E-D1D0-41B6-8751-16F601A2E230";
static ScenarioBase s_ActiveScenario;
bool m_FirstScenarioFrame = true;
// ReSharper disable once InconsistentNaming
[SerializeReference] internal List<Randomizer> m_Randomizers = new List<Randomizer>();
bool m_SkipFrame = true;
bool m_WaitingForFinalUploads;
/// <summary>
/// The list of randomizers managed by this scenario
/// </summary>
[SerializeReference] protected List<Randomizer> m_Randomizers = new List<Randomizer>();
/// If true, this scenario will quit the Unity application when it's finished executing
/// On some platforms, the simulation capture package cannot capture the first frame of output,
/// so this field is used to track whether the first frame has been skipped yet.
[HideInInspector]
public bool quitOnComplete = true;
protected bool m_SkipFrame = true;
/// <summary>
/// Setting this field to true will cause the scenario to enter an idle state. By default, scenarios will enter
/// the idle state after its isScenarioComplete property has returned true.
/// </summary>
protected bool m_Idle;
IEnumerable<Randomizer> activeRandomizers
/// <summary>
/// Enumerates over all enabled randomizers
/// </summary>
public IEnumerable<Randomizer> activeRandomizers
{
get
{

/// Return the list of randomizers attached to this scenario
/// </summary>
public IReadOnlyList<Randomizer> randomizers => m_Randomizers.AsReadOnly();
/// <summary>
/// The name of the Json file this scenario's constants are serialized to/from.
/// </summary>
public virtual string configFileName => "scenario_configuration";
/// <summary>
/// Returns the active parameter scenario in the scene

}
/// <summary>
/// Returns the asset location of the JSON serialized configuration.
/// This API is used for finding the config file using the AssetDatabase API.
/// </summary>
public string defaultConfigFileAssetPath =>
"Assets/StreamingAssets/" + configFileName + ".json";
/// <summary>
/// Returns the absolute file path of the JSON serialized configuration
/// </summary>
public virtual string defaultConfigFilePath =>
Application.dataPath + "/StreamingAssets/" + configFileName + ".json";
/// <summary>
/// Returns this scenario's non-typed serialized constants
/// </summary>
public abstract ScenarioConstants genericConstants { get; }

public abstract bool isScenarioComplete { get; }
/// <summary>
/// Progresses the current scenario iteration
/// This method selects what the next iteration index will be. By default, the scenario will simply progress to
/// the next iteration, but this behaviour can be overriden.
/// </summary>
protected virtual void IncrementIteration()
{

/// Serializes the scenario's constants and randomizer settings to a JSON string
/// </summary>
/// <returns>The scenario configuration as a JSON string</returns>
public abstract string SerializeToJson();
public virtual string SerializeToJson()
{
return ScenarioSerializer.SerializeToJsonString(this);
}
public void SerializeToFile()
/// <param name="filePath">The file path to serialize the scenario to</param>
public virtual void SerializeToFile(string filePath)
Directory.CreateDirectory(Application.dataPath + "/StreamingAssets/");
using (var writer = new StreamWriter(defaultConfigFilePath, false))
{
writer.Write(SerializeToJson());
}
ScenarioSerializer.SerializeToFile(this, filePath);
}
/// <summary>

public abstract void DeserializeFromJson(string json);
public virtual void DeserializeFromJson(string json)
{
ScenarioSerializer.Deserialize(this, json);
}
/// <summary>
/// Overwrites this scenario's randomizer settings and scenario constants using a configuration file located at

public abstract void DeserializeFromFile(string configFilePath);
public virtual void DeserializeFromFile(string configFilePath)
{
if (string.IsNullOrEmpty(configFilePath))
throw new ArgumentException($"{nameof(configFilePath)} is null or empty");
if (!File.Exists(configFilePath))
throw new ArgumentException($"No configuration file found at {configFilePath}");
var jsonText = File.ReadAllText(configFilePath);
DeserializeFromJson(jsonText);
/// <summary>
/// Overwrites this scenario's randomizer settings and scenario constants using a configuration file located at
/// this scenario's defaultConfigFilePath
/// </summary>
public void DeserializeFromFile()
{
DeserializeFromFile(defaultConfigFilePath);
var absolutePath = Path.GetFullPath(configFilePath);
#if UNITY_EDITOR
Debug.Log($"Deserialized scenario configuration from {absolutePath}. " +
"Using undo in the editor will revert these changes to your scenario.");
#else
Debug.Log($"Deserialized scenario configuration from {absolutePath}");
#endif
}
/// <summary>

}
/// <summary>
/// Awake is called when this scenario MonoBehaviour is created or instantiated
/// OnAwake is called right after this scenario MonoBehaviour is created or instantiated
/// </summary>
protected virtual void OnAwake() { }
/// <summary>
/// OnStart is called after Awake but before the first Update method call
protected virtual void Awake()
protected virtual void OnStart()
{
#if !UNITY_EDITOR
var args = Environment.GetCommandLineArgs();
var filePath = string.Empty;
for (var i = 0; i < args.Length - 1; i++)
{
if (args[i] == "--scenario-config-file")
{
filePath = args[i + 1];
break;
}
}
if (string.IsNullOrEmpty(filePath))
{
Debug.Log("No --scenario-config-file command line arg specified. " +
"Proceeding with editor assigned scenario configuration values.");
return;
}
try
{
DeserializeFromFile(filePath);
}
catch (Exception exception)
{
Debug.LogException(exception);
Debug.LogError("An exception was caught while attempting to parse a " +
$"scenario configuration file at {filePath}. Cleaning up and exiting simulation.");
m_Idle = true;
}
#endif
}
/// <summary>
/// OnComplete is called when this scenario's isScenarioComplete property
/// returns true during its main update loop
/// </summary>
protected virtual void OnComplete()
{
DatasetCapture.ResetSimulation();
m_Idle = true;
}
/// <summary>
/// OnIdle is called each frame after the scenario has completed
/// </summary>
protected virtual void OnIdle()
{
Manager.Instance.Shutdown();
if (!Manager.FinalUploadsDone)
return;
#if UNITY_EDITOR
EditorApplication.ExitPlaymode();
#else
Application.Quit();
#endif
}
void Awake()
// Don't skip the first frame if executing on Unity Simulation
if (Configuration.Instance.IsSimulationRunningInCloud())
m_SkipFrame = false;
m_IterationMetricDefinition = DatasetCapture.RegisterMetricDefinition("scenario_iteration", "Iteration information for dataset sequences",
m_IterationMetricDefinition = DatasetCapture.RegisterMetricDefinition(
"scenario_iteration", "Iteration information for dataset sequences",
OnAwake();
}
/// <summary>

activeScenario = null;
}
/// <summary>
/// Start is called after Awake but before the first Update method call
/// </summary>
protected virtual void Start()
void Start()
{
var randomSeedMetricDefinition = DatasetCapture.RegisterMetricDefinition(
"random-seed",

#if !UNITY_EDITOR
DeserializeFromFile();
#endif
OnStart();
/// <summary>
/// Update is called once per frame
/// </summary>
protected virtual void Update()
void Update()
{
// TODO: remove this check when the perception camera can capture the first frame of output
if (m_SkipFrame)

}
// Wait for any final uploads before exiting quitting
if (m_WaitingForFinalUploads && quitOnComplete)
if (m_Idle)
Manager.Instance.Shutdown();
if (!Manager.FinalUploadsDone)
return;
#if UNITY_EDITOR
EditorApplication.ExitPlaymode();
#else
Application.Quit();
#endif
OnIdle();
// Iterate Scenario
if (m_FirstScenarioFrame)
// Increment iteration and cleanup last iteration
if (isIterationComplete)
m_FirstScenarioFrame = false;
}
else
{
currentIterationFrame++;
framesSinceInitialization++;
if (isIterationComplete)
{
IncrementIteration();
currentIterationFrame = 0;
foreach (var randomizer in activeRandomizers)
randomizer.IterationEnd();
}
IncrementIteration();
currentIterationFrame = 0;
foreach (var randomizer in activeRandomizers)
randomizer.IterationEnd();
}
// Quit if scenario is complete

randomizer.ScenarioComplete();
DatasetCapture.ResetSimulation();
m_WaitingForFinalUploads = true;
return;
OnComplete();
}
// Perform new iteration tasks

// Perform new frame tasks
foreach (var randomizer in activeRandomizers)
randomizer.Update();
// Iterate scenario frame count
currentIterationFrame++;
framesSinceInitialization++;
/// Finds and returns a randomizer attached to this scenario of the specified Randomizer type
/// Called by the "Add Randomizer" button in the scenario Inspector
/// <typeparam name="T">The type of randomizer to find</typeparam>
/// <returns>A randomizer of the specified type</returns>
/// <param name="randomizerType">The type of randomizer to create</param>
/// <returns>The newly created randomizer</returns>
public T GetRandomizer<T>() where T : Randomizer
internal Randomizer CreateRandomizer(Type randomizerType)
foreach (var randomizer in m_Randomizers)
if (randomizer is T typedRandomizer)
return typedRandomizer;
throw new ScenarioException($"A Randomizer of type {typeof(T).Name} was not added to this scenario");
if (!randomizerType.IsSubclassOf(typeof(Randomizer)))
throw new ScenarioException(
$"Cannot add non-randomizer type {randomizerType.Name} to randomizer list");
var newRandomizer = (Randomizer)Activator.CreateInstance(randomizerType);
AddRandomizer(newRandomizer);
return newRandomizer;
/// Creates a new randomizer and adds it to this scenario
/// Append a randomizer to the end of the randomizer list
/// <typeparam name="T">The type of randomizer to create</typeparam>
/// <returns>The newly created randomizer</returns>
public T CreateRandomizer<T>() where T : Randomizer, new()
/// <param name="newRandomizer"></param>
public void AddRandomizer(Randomizer newRandomizer)
return (T)CreateRandomizer(typeof(T));
InsertRandomizer(m_Randomizers.Count, newRandomizer);
internal Randomizer CreateRandomizer(Type randomizerType)
/// <summary>
/// Insert a randomizer at a given index within the randomizer list
/// </summary>
/// <param name="index">The index to place the randomizer</param>
/// <param name="newRandomizer">The randomizer to add to the list</param>
/// <exception cref="ScenarioException"></exception>
public void InsertRandomizer(int index, Randomizer newRandomizer)
if (!randomizerType.IsSubclassOf(typeof(Randomizer)))
throw new ScenarioException(
$"Cannot add non-randomizer type {randomizerType.Name} to randomizer list");
if (randomizer.GetType() == randomizerType)
if (randomizer.GetType() == newRandomizer.GetType())
$"Two Randomizers of the same type ({randomizerType.Name}) cannot both be active simultaneously");
var newRandomizer = (Randomizer)Activator.CreateInstance(randomizerType);
m_Randomizers.Add(newRandomizer);
$"Cannot add another randomizer of type ${newRandomizer.GetType()} when " +
$"a scenario of this type is already present in the scenario");
m_Randomizers.Insert(index, newRandomizer);
#if UNITY_EDITOR
if (Application.isPlaying)
newRandomizer.Create();

return newRandomizer;
/// Removes a randomizer of the specified type from this scenario
/// Remove the randomizer present at the given index
/// <typeparam name="T">The type of scenario to remove</typeparam>
public void RemoveRandomizer<T>() where T : Randomizer, new()
/// <param name="index">The index of the randomizer to remove</param>
public void RemoveRandomizerAt(int index)
RemoveRandomizer(typeof(T));
}
internal void RemoveRandomizer(Type randomizerType)
{
if (!randomizerType.IsSubclassOf(typeof(Randomizer)))
throw new ScenarioException(
$"Cannot remove non-randomizer type {randomizerType.Name} from randomizer list");
var removed = false;
for (var i = 0; i < m_Randomizers.Count; i++)
if (m_Randomizers[i].GetType() == randomizerType)
{
m_Randomizers.RemoveAt(i);
removed = true;
break;
}
if (!removed)
throw new ScenarioException(
$"No active Randomizer of type {randomizerType.Name} could be removed");
m_Randomizers.RemoveAt(index);
/// Returns the execution order index of a randomizer of the given type
/// Returns the randomizer present at the given index
/// <typeparam name="T">The type of randomizer to index</typeparam>
/// <returns>The randomizer index</returns>
/// <exception cref="ScenarioException"></exception>
public int GetRandomizerIndex<T>() where T : Randomizer, new()
/// <param name="index">The lookup index</param>
/// <returns>The randomizer present at the given index</returns>
public Randomizer GetRandomizer(int index)
for (var i = 0; i < m_Randomizers.Count; i++)
{
var randomizer = m_Randomizers[i];
if (randomizer is T)
return i;
}
throw new ScenarioException($"A Randomizer of type {typeof(T).Name} was not added to this scenario");
return m_Randomizers[index];
/// Moves a randomizer from one index to another
/// Finds and returns a randomizer attached to this scenario of the specified Randomizer type
/// <param name="currentIndex">The index of the randomizer to move</param>
/// <param name="nextIndex">The index to move the randomizer to</param>
public void ReorderRandomizer(int currentIndex, int nextIndex)
/// <typeparam name="T">The type of randomizer to find</typeparam>
/// <returns>A randomizer of the specified type</returns>
/// <exception cref="ScenarioException"></exception>
public T GetRandomizer<T>() where T : Randomizer
if (currentIndex == nextIndex)
return;
if (nextIndex > currentIndex)
nextIndex--;
var randomizer = m_Randomizers[currentIndex];
m_Randomizers.RemoveAt(currentIndex);
m_Randomizers.Insert(nextIndex, randomizer);
foreach (var randomizer in m_Randomizers)
if (randomizer is T typedRandomizer)
return typedRandomizer;
throw new ScenarioException($"A Randomizer of type {typeof(T).Name} was not added to this scenario");
}
void ValidateParameters()

24
com.unity.perception/Runtime/Randomization/Scenarios/UnitySimulationScenario.cs


public sealed override bool isScenarioComplete => currentIteration >= constants.totalIterations;
/// <inheritdoc/>
public override string defaultConfigFilePath =>
Configuration.Instance.IsSimulationRunningInCloud()
? new Uri(Configuration.Instance.SimulationConfig.app_param_uri).LocalPath
: base.defaultConfigFilePath;
/// <inheritdoc/>
protected sealed override void IncrementIteration()
{
currentIteration += constants.instanceCount;

protected override void Start()
protected override void OnAwake()
base.Start();
// Don't skip the first frame if executing on Unity Simulation
if (Configuration.Instance.IsSimulationRunningInCloud())
m_SkipFrame = false;
}
/// <inheritdoc/>
protected override void OnStart()
{
if (Configuration.Instance.IsSimulationRunningInCloud())
{
DeserializeFromFile(new Uri(Configuration.Instance.SimulationConfig.app_param_uri).LocalPath);
constants.instanceIndex = int.Parse(Configuration.Instance.GetInstanceId()) - 1;
}
else
base.OnStart();
currentIteration = constants.instanceIndex;
}
}

2
com.unity.perception/Tests/Editor/RandomizerEditorTests.cs


// if ScenarioBase.CreateRandomizer<>() was coded correctly
Assert.DoesNotThrow(() =>
{
m_Scenario.CreateRandomizer<ErrorsOnCreateTestRandomizer>();
m_Scenario.AddRandomizer(new ErrorsOnCreateTestRandomizer());
});
}
}

10
com.unity.perception/Tests/Runtime/Randomization/RandomizerTests/RandomizerTests.cs


IEnumerator CreateNewScenario(int totalIterations, int framesPerIteration)
{
m_Scenario = m_TestObject.AddComponent<FixedLengthScenario>();
m_Scenario.quitOnComplete = false;
m_Scenario.constants.totalIterations = totalIterations;
m_Scenario.constants.framesPerIteration = framesPerIteration;
yield return null; // Skip first frame

public void OneRandomizerInstancePerTypeTest()
{
m_Scenario = m_TestObject.AddComponent<FixedLengthScenario>();
m_Scenario.quitOnComplete = false;
m_Scenario.CreateRandomizer<ExampleTransformRandomizer>();
Assert.Throws<ScenarioException>(() => m_Scenario.CreateRandomizer<ExampleTransformRandomizer>());
m_Scenario.AddRandomizer(new ExampleTransformRandomizer());
Assert.Throws<ScenarioException>(() => m_Scenario.AddRandomizer(new ExampleTransformRandomizer()));
}
[UnityTest]

m_Scenario.CreateRandomizer<ExampleTransformRandomizer>();
m_Scenario.AddRandomizer(new ExampleTransformRandomizer());
var transform = m_TestObject.transform;
var initialPosition = Vector3.zero;
transform.position = initialPosition;

public IEnumerator OnIterationStartExecutesEveryIteration()
{
yield return CreateNewScenario(10, 2);
m_Scenario.CreateRandomizer<ExampleTransformRandomizer>();
m_Scenario.AddRandomizer(new ExampleTransformRandomizer());
var transform = m_TestObject.transform;
var initialRotation = Quaternion.identity;
transform.rotation = initialRotation;

18
com.unity.perception/Editor/Randomization/Uss/RunInUnitySimulationWindowStyles.uss


.sim-window__header-1 {
font-size: 14px;
-unity-font-style: bold;
margin-bottom: 1px;
margin-left: 2px;
}
.sim-window__container-outer {
background-color: #2A2A2A;
margin-bottom: 8px;
margin-top: 2px;
padding: 2px;
}
.sim-window__label-prev-result {
padding: 2 2 0 1; /*top, right, bottom, left*/
margin: 1 3 1 3;
}

3
com.unity.perception/Editor/Randomization/Uss/RunInUnitySimulationWindowStyles.uss.meta


fileFormatVersion: 2
guid: 67ac54c923534486a149e4d1d85d9c83
timeCreated: 1613109854

3
com.unity.perception/Runtime/Randomization/Scenarios/Serialization.meta


fileFormatVersion: 2
guid: 16628d9798ec4551b9648c56ef672ecc
timeCreated: 1612905161

3
com.unity.perception/Tests/Runtime/Randomization/ScenarioTests.meta


fileFormatVersion: 2
guid: 60317fa8c31048249aef19666120f42d
timeCreated: 1613004387

188
com.unity.perception/Runtime/Randomization/Scenarios/Serialization/JsonConverters.cs


using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace UnityEngine.Perception.Randomization.Scenarios.Serialization
{
class GroupItemsConverter : JsonConverter
{
public override bool CanWrite => true;
public override bool CanRead => true;
public override bool CanConvert(Type objectType)
{
return objectType == typeof(IGroupItem);
}
public override void WriteJson(
JsonWriter writer, object value, JsonSerializer serializer)
{
var output = new JObject();
var groupItems = (Dictionary<string, IGroupItem>)value;
foreach (var itemKey in groupItems.Keys)
{
var itemValue = groupItems[itemKey];
var newObj = new JObject();
if (itemValue is Parameter)
newObj["param"] = JObject.FromObject(itemValue);
else
newObj["scalar"] = JObject.FromObject(itemValue);
output[itemKey] = newObj;
}
output.WriteTo(writer);
}
public override object ReadJson(
JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jsonObject = JObject.Load(reader);
var groupItems = new Dictionary<string, IGroupItem>();
foreach (var property in jsonObject.Properties())
{
var value = (JObject)property.Value;
IGroupItem groupItem;
if (value.ContainsKey("param"))
groupItem = serializer.Deserialize<Parameter>(value["param"].CreateReader());
else if (value.ContainsKey("scalar"))
groupItem = serializer.Deserialize<Scalar>(value["scalar"].CreateReader());
else
throw new KeyNotFoundException("No GroupItem key found");
groupItems.Add(property.Name, groupItem);
}
return groupItems;
}
}
class ParameterItemsConverter : JsonConverter
{
public override bool CanRead => true;
public override bool CanWrite => true;
public override bool CanConvert(Type objectType)
{
return objectType == typeof(IParameterItem);
}
public override void WriteJson(
JsonWriter writer, object value, JsonSerializer serializer)
{
var output = new JObject();
var parameterItems = (Dictionary<string, IParameterItem>)value;
foreach (var itemKey in parameterItems.Keys)
{
var itemValue = parameterItems[itemKey];
var newObj = new JObject();
if (itemValue is SamplerOptions)
newObj["samplerOptions"] = JObject.FromObject(itemValue);
else
newObj["scalar"] = JObject.FromObject(itemValue);
output[itemKey] = newObj;
}
output.WriteTo(writer);
}
public override object ReadJson(
JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jsonObject = JObject.Load(reader);
var parameterItems = new Dictionary<string, IParameterItem>();
foreach (var property in jsonObject.Properties())
{
var value = (JObject)property.Value;
IParameterItem parameterItem;
if (value.ContainsKey("samplerOptions"))
parameterItem = serializer.Deserialize<SamplerOptions>(value["samplerOptions"].CreateReader());
else if (value.ContainsKey("scalar"))
parameterItem = serializer.Deserialize<Scalar>(value["scalar"].CreateReader());
else
throw new KeyNotFoundException("No ParameterItem key found");
parameterItems.Add(property.Name, parameterItem);
}
return parameterItems;
}
}
class SamplerOptionsConverter : JsonConverter
{
public override bool CanRead => true;
public override bool CanWrite => true;
public override bool CanConvert(Type objectType)
{
return objectType == typeof(SamplerOptions);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var options = (SamplerOptions)value;
var output = new JObject { ["metadata"] = JObject.FromObject(options.metadata) };
string key;
if (options.defaultSampler is ConstantSampler)
key = "constant";
else if (options.defaultSampler is UniformSampler)
key = "uniform";
else if (options.defaultSampler is NormalSampler)
key = "normal";
else
throw new TypeAccessException($"Cannot serialize type ${options.defaultSampler.GetType()}");
output[key] = JObject.FromObject(options.defaultSampler);
output.WriteTo(writer);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jsonObject = JObject.Load(reader);
var samplerOption = new SamplerOptions { metadata = jsonObject["metadata"].ToObject<StandardMetadata>() };
if (jsonObject.ContainsKey("constant"))
samplerOption.defaultSampler = jsonObject["constant"].ToObject<ConstantSampler>();
else if (jsonObject.ContainsKey("uniform"))
samplerOption.defaultSampler = jsonObject["uniform"].ToObject<UniformSampler>();
else if (jsonObject.ContainsKey("normal"))
samplerOption.defaultSampler = jsonObject["normal"].ToObject<NormalSampler>();
else
throw new KeyNotFoundException("No valid SamplerOption key type found");
return samplerOption;
}
}
class ScalarConverter : JsonConverter
{
public override bool CanRead => true;
public override bool CanWrite => false;
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Scalar);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new InvalidOperationException("Use default serialization.");
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jsonObject = JObject.Load(reader);
var value = (JObject)jsonObject["value"];
var scalar = new Scalar { metadata = jsonObject["metadata"].ToObject<StandardMetadata>() };
if (value.ContainsKey("str"))
scalar.value = new StringScalarValue { str = value["str"].Value<string>() };
else if (value.ContainsKey("num"))
scalar.value = new DoubleScalarValue { num = value["num"].Value<double>() };
else if (value.ContainsKey("bool"))
scalar.value = new BooleanScalarValue { boolean = value["bool"].Value<bool>() };
else
throw new KeyNotFoundException("No valid ScalarValue key type found");
return scalar;
}
}
}

3
com.unity.perception/Runtime/Randomization/Scenarios/Serialization/JsonConverters.cs.meta


fileFormatVersion: 2
guid: f2954e9f75904733971e6c6ab1f275d8
timeCreated: 1612905182

3
com.unity.perception/Runtime/Randomization/Scenarios/Serialization/SerializationStructures.cs.meta


fileFormatVersion: 2
guid: f1253a3d94bb468b93f80c2178652540
timeCreated: 1612927921

3
com.unity.perception/Runtime/Randomization/Scenarios/Serialization/ScenarioSerializer.cs.meta


fileFormatVersion: 2
guid: 39dfd341dac24b8fb2567380a105f770
timeCreated: 1612461856

280
com.unity.perception/Runtime/Randomization/Scenarios/Serialization/ScenarioSerializer.cs


using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using UnityEngine.Perception.Randomization.Parameters;
using UnityEngine.Perception.Randomization.Randomizers;
using UnityEngine.Perception.Randomization.Samplers;
namespace UnityEngine.Perception.Randomization.Scenarios.Serialization
{
static class ScenarioSerializer
{
#region Serialization
public static string SerializeToJsonString(ScenarioBase scenario)
{
return JsonConvert.SerializeObject(SerializeToJsonObject(scenario), Formatting.Indented);
}
public static void SerializeToFile(ScenarioBase scenario, string filePath)
{
Directory.CreateDirectory(Application.dataPath + "/StreamingAssets/");
using (var writer = new StreamWriter(filePath, false))
{
writer.Write(SerializeToJsonString(scenario));
}
}
public static JObject SerializeToJsonObject(ScenarioBase scenario)
{
return new JObject
{
["constants"] = SerializeConstants(scenario.genericConstants),
["randomizers"] = JObject.FromObject(SerializeScenarioToTemplate(scenario))
};
}
static JObject SerializeConstants(ScenarioConstants constants)
{
var constantsObj = new JObject();
var constantsFields = constants.GetType().GetFields();
foreach (var constantsField in constantsFields)
constantsObj.Add(new JProperty(constantsField.Name, constantsField.GetValue(constants)));
return constantsObj;
}
static TemplateConfigurationOptions SerializeScenarioToTemplate(ScenarioBase scenario)
{
return new TemplateConfigurationOptions
{
groups = SerializeRandomizers(scenario.randomizers)
};
}
static Dictionary<string, Group> SerializeRandomizers(IEnumerable<Randomizer> randomizers)
{
var serializedRandomizers = new Dictionary<string, Group>();
foreach (var randomizer in randomizers)
{
var randomizerData = SerializeRandomizer(randomizer);
if (randomizerData.items.Count == 0)
continue;
serializedRandomizers.Add(randomizer.GetType().Name, randomizerData);
}
return serializedRandomizers;
}
static Group SerializeRandomizer(Randomizer randomizer)
{
var randomizerData = new Group();
var fields = randomizer.GetType().GetFields();
foreach (var field in fields)
{
if (field.FieldType.IsSubclassOf(typeof(Randomization.Parameters.Parameter)))
{
if (!IsSubclassOfRawGeneric(typeof(NumericParameter<>), field.FieldType))
continue;
var parameter = (Randomization.Parameters.Parameter)field.GetValue(randomizer);
var parameterData = SerializeParameter(parameter);
if (parameterData.items.Count == 0)
continue;
randomizerData.items.Add(field.Name, parameterData);
}
else
{
var scalarValue = ScalarFromField(field, randomizer);
if (scalarValue != null)
randomizerData.items.Add(field.Name, new Scalar { value = scalarValue });
}
}
return randomizerData;
}
static Parameter SerializeParameter(Randomization.Parameters.Parameter parameter)
{
var parameterData = new Parameter();
var fields = parameter.GetType().GetFields();
foreach (var field in fields)
{
if (field.FieldType.IsAssignableFrom(typeof(ISampler)))
{
var sampler = (ISampler)field.GetValue(parameter);
var samplerData = SerializeSampler(sampler);
if (samplerData.defaultSampler == null)
continue;
parameterData.items.Add(field.Name, samplerData);
}
else
{
var scalarValue = ScalarFromField(field, parameter);
if (scalarValue != null)
parameterData.items.Add(field.Name, new Scalar { value = scalarValue });
}
}
return parameterData;
}
static SamplerOptions SerializeSampler(ISampler sampler)
{
var samplerData = new SamplerOptions();
if (sampler is Samplers.ConstantSampler constantSampler)
samplerData.defaultSampler = new ConstantSampler
{
value = constantSampler.value
};
else if (sampler is Samplers.UniformSampler uniformSampler)
samplerData.defaultSampler = new UniformSampler
{
min = uniformSampler.range.minimum,
max = uniformSampler.range.maximum
};
else if (sampler is Samplers.NormalSampler normalSampler)
samplerData.defaultSampler = new NormalSampler
{
min = normalSampler.range.minimum,
max = normalSampler.range.maximum,
mean = normalSampler.mean,
standardDeviation = normalSampler.standardDeviation
};
else
throw new ArgumentException($"Invalid sampler type ({sampler.GetType()})");
return samplerData;
}
static IScalarValue ScalarFromField(FieldInfo field, object obj)
{
if (field.FieldType == typeof(string))
return new StringScalarValue { str = (string)field.GetValue(obj) };
if (field.FieldType == typeof(bool))
return new BooleanScalarValue { boolean = (bool)field.GetValue(obj) };
if (field.FieldType == typeof(float) || field.FieldType == typeof(double) || field.FieldType == typeof(int))
return new DoubleScalarValue { num = Convert.ToDouble(field.GetValue(obj)) };
return null;
}
#endregion
#region Deserialization
public static void Deserialize(ScenarioBase scenario, string json)
{
var jsonData = JObject.Parse(json);
if (jsonData.ContainsKey("constants"))
DeserializeConstants(scenario.genericConstants, (JObject)jsonData["constants"]);
if (jsonData.ContainsKey("randomizers"))
DeserializeTemplateIntoScenario(
scenario, jsonData["randomizers"].ToObject<TemplateConfigurationOptions>());
}
static void DeserializeConstants(ScenarioConstants constants, JObject constantsData)
{
var serializer = new JsonSerializer();
serializer.Populate(constantsData.CreateReader(), constants);
}
static void DeserializeTemplateIntoScenario(ScenarioBase scenario, TemplateConfigurationOptions template)
{
DeserializeRandomizers(scenario.randomizers, template.groups);
}
static void DeserializeRandomizers(IEnumerable<Randomizer> randomizers, Dictionary<string, Group> groups)
{
var randomizerTypeMap = new Dictionary<string, Randomizer>();
foreach (var randomizer in randomizers)
randomizerTypeMap.Add(randomizer.GetType().Name, randomizer);
foreach (var randomizerPair in groups)
{
if (!randomizerTypeMap.ContainsKey(randomizerPair.Key))
continue;
var randomizer = randomizerTypeMap[randomizerPair.Key];
DeserializeRandomizer(randomizer, randomizerPair.Value);
}
}
static void DeserializeRandomizer(Randomizer randomizer, Group randomizerData)
{
foreach (var pair in randomizerData.items)
{
var field = randomizer.GetType().GetField(pair.Key);
if (field == null)
continue;
if (pair.Value is Parameter parameterData)
DeserializeParameter((Randomization.Parameters.Parameter)field.GetValue(randomizer), parameterData);
else
DeserializeScalarValue(randomizer, field, (Scalar)pair.Value);
}
}
static void DeserializeParameter(Randomization.Parameters.Parameter parameter, Parameter parameterData)
{
foreach (var pair in parameterData.items)
{
var field = parameter.GetType().GetField(pair.Key);
if (field == null)
continue;
if (pair.Value is SamplerOptions samplerOptions)
field.SetValue(parameter, DeserializeSampler(samplerOptions.defaultSampler));
else
DeserializeScalarValue(parameter, field, (Scalar)pair.Value);
}
}
static ISampler DeserializeSampler(ISamplerOption samplerOption)
{
if (samplerOption is ConstantSampler constantSampler)
return new Samplers.ConstantSampler
{
value = (float)constantSampler.value
};
if (samplerOption is UniformSampler uniformSampler)
return new Samplers.UniformSampler
{
range = new FloatRange
{
minimum = (float)uniformSampler.min,
maximum = (float)uniformSampler.max
}
};
if (samplerOption is NormalSampler normalSampler)
return new Samplers.NormalSampler
{
range = new FloatRange
{
minimum = (float)normalSampler.min,
maximum = (float)normalSampler.max
},
mean = (float)normalSampler.mean,
standardDeviation = (float)normalSampler.standardDeviation
};
throw new ArgumentException($"Cannot deserialize unsupported sampler type {samplerOption.GetType()}");
}
static void DeserializeScalarValue(object obj, FieldInfo field, Scalar scalar)
{
object value;
if (scalar.value is StringScalarValue stringValue)
value = stringValue.str;
else if (scalar.value is BooleanScalarValue booleanValue)
value = booleanValue.boolean;
else if (scalar.value is DoubleScalarValue doubleValue)
value = doubleValue.num;
else
throw new ArgumentException(
$"Cannot deserialize unsupported scalar type {scalar.value.GetType()}");
field.SetValue(obj, Convert.ChangeType(value, field.FieldType));
}
#endregion
static bool IsSubclassOfRawGeneric(Type generic, Type toCheck) {
while (toCheck != null && toCheck != typeof(object)) {
var cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck;
if (generic == cur) {
return true;
}
toCheck = toCheck.BaseType;
}
return false;
}
}
}

95
com.unity.perception/Runtime/Randomization/Scenarios/Serialization/SerializationStructures.cs


using System.Collections.Generic;
using Newtonsoft.Json;
namespace UnityEngine.Perception.Randomization.Scenarios.Serialization
{
#region Interfaces
interface IGroupItem { }
interface IParameterItem { }
interface ISamplerOption { }
interface IScalarValue { }
#endregion
#region GroupedObjects
class TemplateConfigurationOptions
{
public Dictionary<string, Group> groups = new Dictionary<string, Group>();
}
class StandardMetadata
{
public string name = string.Empty;
public string description = string.Empty;
}
class Group
{
public StandardMetadata metadata = new StandardMetadata();
[JsonConverter(typeof(GroupItemsConverter))]
public Dictionary<string, IGroupItem> items = new Dictionary<string, IGroupItem>();
}
class Parameter : IGroupItem
{
public StandardMetadata metadata = new StandardMetadata();
[JsonConverter(typeof(ParameterItemsConverter))]
public Dictionary<string, IParameterItem> items = new Dictionary<string, IParameterItem>();
}
#endregion
#region SamplerOptions
[JsonConverter(typeof(SamplerOptionsConverter))]
class SamplerOptions : IParameterItem
{
public StandardMetadata metadata = new StandardMetadata();
public ISamplerOption defaultSampler;
}
class UniformSampler : ISamplerOption
{
public double min;
public double max;
}
class NormalSampler : ISamplerOption
{
public double min;
public double max;
public double mean;
public double standardDeviation;
}
class ConstantSampler : ISamplerOption
{
public double value;
}
#endregion
#region ScalarValues
[JsonConverter(typeof(ScalarConverter))]
class Scalar : IGroupItem, IParameterItem
{
public StandardMetadata metadata = new StandardMetadata();
public IScalarValue value;
}
class StringScalarValue : IScalarValue
{
public string str;
}
class DoubleScalarValue : IScalarValue
{
public double num;
}
class BooleanScalarValue : IScalarValue
{
[JsonProperty("bool")]
public bool boolean;
}
#endregion
}

3
com.unity.perception/Tests/Runtime/Randomization/ScenarioTests/Resources.meta


fileFormatVersion: 2
guid: 587e6f3ccc7d4d11beb10c2078c268f7
timeCreated: 1613005737

67
com.unity.perception/Tests/Runtime/Randomization/ScenarioTests/Resources/SampleScenarioConfiguration.json


{
"constants": {
"framesPerIteration": 1,
"totalIterations": 100,
"instanceCount": 1,
"instanceIndex": 0,
"randomSeed": 539662031
},
"randomizers": {
"groups": {
"RotationRandomizer": {
"metadata": {
"name": "",
"description": ""
},
"items": {
"rotation": {
"param": {
"metadata": {
"name": "",
"description": ""
},
"items": {
"x": {
"samplerOptions": {
"metadata": {
"name": "",
"description": ""
},
"uniform": {
"min": 0.0,
"max": 360.0
}
}
},
"y": {
"samplerOptions": {
"metadata": {
"name": "",
"description": ""
},
"uniform": {
"min": 0.0,
"max": 360.0
}
}
},
"z": {
"samplerOptions": {
"metadata": {
"name": "",
"description": ""
},
"uniform": {
"min": 0.0,
"max": 360.0
}
}
}
}
}
}
}
}
}
}
}

3
com.unity.perception/Tests/Runtime/Randomization/ScenarioTests/Resources/SampleScenarioConfiguration.json.meta


fileFormatVersion: 2
guid: f8b765e3d9c34160bee64b2709a4edd8
timeCreated: 1613005744

159
com.unity.perception/Tests/Runtime/Randomization/ScenarioTests/ScenarioTests.cs


using System;
using System.Collections;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.Perception.Randomization.Randomizers.SampleRandomizers;
using UnityEngine.Perception.Randomization.Samplers;
using UnityEngine.Perception.GroundTruth;
using UnityEngine.Perception.Randomization.Scenarios;
using UnityEngine.TestTools;
using Object = UnityEngine.Object;
namespace RandomizationTests
{
[TestFixture]
public class ScenarioTests
{
GameObject m_TestObject;
FixedLengthScenario m_Scenario;
[SetUp]
public void Setup()
{
m_TestObject = new GameObject();
}
[TearDown]
public void TearDown()
{
Object.DestroyImmediate(m_TestObject);
}
// TODO: update this function once the perception camera doesn't skip the first frame
IEnumerator CreateNewScenario(int totalIterations, int framesPerIteration)
{
m_Scenario = m_TestObject.AddComponent<FixedLengthScenario>();
m_Scenario.constants.totalIterations = totalIterations;
m_Scenario.constants.framesPerIteration = framesPerIteration;
yield return null; // Skip Start() frame
yield return null; // Skip first Update() frame
}
[Test]
public void ScenarioConfigurationSerializesProperly()
{
m_TestObject = new GameObject();
m_Scenario = m_TestObject.AddComponent<FixedLengthScenario>();
m_Scenario.AddRandomizer(new RotationRandomizer());
string RemoveWhitespace(string str) =>
string.Join("", str.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries));
var expectedConfigAsset = (TextAsset)Resources.Load("SampleScenarioConfiguration");
var expectedText = RemoveWhitespace(expectedConfigAsset.text);
var scenarioJson = RemoveWhitespace(m_Scenario.SerializeToJson());
Assert.AreEqual(expectedText, scenarioJson);
}
[Test]
public void ScenarioConfigurationOverwrittenDuringDeserialization()
{
m_TestObject = new GameObject();
m_Scenario = m_TestObject.AddComponent<FixedLengthScenario>();
var constants = new FixedLengthScenario.Constants
{
framesPerIteration = 2,
totalIterations = 2
};
var changedConstants = new FixedLengthScenario.Constants
{
framesPerIteration = 0,
totalIterations = 0
};
// Serialize some values
m_Scenario.constants = constants;
var serializedConfig = m_Scenario.SerializeToJson();
// Change the values
m_Scenario.constants = changedConstants;
m_Scenario.DeserializeFromJson(serializedConfig);
// Check if the values reverted correctly
Assert.AreEqual(m_Scenario.constants.framesPerIteration, constants.framesPerIteration);
Assert.AreEqual(m_Scenario.constants.totalIterations, constants.totalIterations);
}
[UnityTest]
public IEnumerator IterationsCanLastMultipleFrames()
{
const int frameCount = 5;
yield return CreateNewScenario(1, frameCount);
for (var i = 0; i < frameCount; i++)
{
Assert.AreEqual(0, m_Scenario.currentIteration);
yield return null;
}
Assert.AreEqual(1, m_Scenario.currentIteration);
}
[UnityTest]
public IEnumerator FinishesWhenIsScenarioCompleteIsTrue()
{
const int iterationCount = 5;
yield return CreateNewScenario(iterationCount, 1);
for (var i = 0; i < iterationCount; i++)
{
Assert.False(m_Scenario.isScenarioComplete);
yield return null;
}
Assert.True(m_Scenario.isScenarioComplete);
}
[UnityTest]
public IEnumerator StartNewDatasetSequenceEveryIteration()
{
var perceptionCamera = SetupPerceptionCamera();
yield return CreateNewScenario(2, 2);
Assert.AreEqual(DatasetCapture.SimulationState.SequenceTime, 0);
// Second frame, first iteration
yield return null;
Assert.AreEqual(DatasetCapture.SimulationState.SequenceTime, perceptionCamera.simulationDeltaTime);
// Third frame, second iteration, SequenceTime has been reset
yield return null;
Assert.AreEqual(DatasetCapture.SimulationState.SequenceTime, 0);
}
[UnityTest]
public IEnumerator GeneratedRandomSeedsChangeWithScenarioIteration()
{
yield return CreateNewScenario(3, 1);
var seeds = new uint[3];
for (var i = 0; i < 3; i++)
seeds[i] = SamplerState.NextRandomState();
yield return null;
for (var i = 0; i < 3; i++)
Assert.AreNotEqual(seeds[i], SamplerState.NextRandomState());
}
PerceptionCamera SetupPerceptionCamera()
{
m_TestObject.SetActive(false);
var camera = m_TestObject.AddComponent<Camera>();
camera.orthographic = true;
camera.orthographicSize = 1;
var perceptionCamera = m_TestObject.AddComponent<PerceptionCamera>();
perceptionCamera.captureRgbImages = false;
m_TestObject.SetActive(true);
return perceptionCamera;
}
}
}

177
com.unity.perception/Tests/Runtime/Randomization/ScenarioTests.cs


using System;
using System.Collections;
using System.IO;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.Perception.Randomization.Randomizers.SampleRandomizers;
using UnityEngine.Perception.Randomization.Samplers;
using UnityEngine.Perception.GroundTruth;
using UnityEngine.Perception.Randomization.Scenarios;
using UnityEngine.TestTools;
using Object = UnityEngine.Object;
namespace RandomizationTests
{
[TestFixture]
public class ScenarioTests
{
GameObject m_TestObject;
FixedLengthScenario m_Scenario;
[SetUp]
public void Setup()
{
m_TestObject = new GameObject();
}
[TearDown]
public void TearDown()
{
Object.DestroyImmediate(m_TestObject);
}
// TODO: update this function once the perception camera doesn't skip the first frame
IEnumerator CreateNewScenario(int totalIterations, int framesPerIteration)
{
m_Scenario = m_TestObject.AddComponent<FixedLengthScenario>();
m_Scenario.quitOnComplete = false;
m_Scenario.constants.totalIterations = totalIterations;
m_Scenario.constants.framesPerIteration = framesPerIteration;
yield return null; // Skip Start() frame
yield return null; // Skip first Update() frame
}
[UnityTest]
public IEnumerator ScenarioConfigurationSerializesProperly()
{
yield return CreateNewScenario(10, 10);
var scenario = m_Scenario.GetComponent<FixedLengthScenario>();
scenario.CreateRandomizer<HueOffsetRandomizer>();
const string expectedConfig = @"{
""constants"": {
""framesPerIteration"": 10,
""totalIterations"": 10,
""instanceCount"": 1,
""instanceIndex"": 0,
""randomSeed"": 539662031
},
""randomizers"": {
""HueOffsetRandomizer"": {
""hueOffset"": {
""value"": {
""range"": {
""minimum"": -180.0,
""maximum"": 180.0
}
}
}
}
}
}";
Assert.AreEqual(expectedConfig, scenario.SerializeToJson());
}
[UnityTest]
public IEnumerator ScenarioConfigurationOverwrittenDuringDeserialization()
{
yield return CreateNewScenario(10, 10);
var constants = new FixedLengthScenario.Constants
{
framesPerIteration = 2,
totalIterations = 2
};
var changedConstants = new FixedLengthScenario.Constants
{
framesPerIteration = 0,
totalIterations = 0
};
// Serialize some values
m_Scenario.constants = constants;
var serializedConfig = m_Scenario.SerializeToJson();
// Change the values
m_Scenario.constants = changedConstants;
m_Scenario.DeserializeFromJson(serializedConfig);
// Check if the values reverted correctly
Assert.AreEqual(m_Scenario.constants.framesPerIteration, constants.framesPerIteration);
Assert.AreEqual(m_Scenario.constants.totalIterations, constants.totalIterations);
yield return null;
}
[UnityTest]
public IEnumerator IterationsCanLastMultipleFrames()
{
const int frameCount = 5;
yield return CreateNewScenario(1, frameCount);
for (var i = 0; i < frameCount; i++)
{
Assert.AreEqual(0, m_Scenario.currentIteration);
yield return null;
}
Assert.AreEqual(1, m_Scenario.currentIteration);
}
[UnityTest]
public IEnumerator FinishesWhenIsScenarioCompleteIsTrue()
{
const int iterationCount = 5;
yield return CreateNewScenario(iterationCount, 1);
for (var i = 0; i < iterationCount; i++)
{
Assert.False(m_Scenario.isScenarioComplete);
yield return null;
}
Assert.True(m_Scenario.isScenarioComplete);
}
[UnityTest]
public IEnumerator StartNewDatasetSequenceEveryIteration()
{
var perceptionCamera = SetupPerceptionCamera();
yield return CreateNewScenario(2, 2);
Assert.AreEqual(DatasetCapture.SimulationState.SequenceTime, 0);
// Second frame, first iteration
yield return null;
Assert.AreEqual(DatasetCapture.SimulationState.SequenceTime, perceptionCamera.simulationDeltaTime);
// Third frame, second iteration, SequenceTime has been reset
yield return null;
Assert.AreEqual(DatasetCapture.SimulationState.SequenceTime, 0);
}
[UnityTest]
public IEnumerator GeneratedRandomSeedsChangeWithScenarioIteration()
{
yield return CreateNewScenario(3, 1);
var seeds = new uint[3];
for (var i = 0; i < 3; i++)
seeds[i] = SamplerState.NextRandomState();
yield return null;
for (var i = 0; i < 3; i++)
Assert.AreNotEqual(seeds[i], SamplerState.NextRandomState());
}
PerceptionCamera SetupPerceptionCamera()
{
m_TestObject.SetActive(false);
var camera = m_TestObject.AddComponent<Camera>();
camera.orthographic = true;
camera.orthographicSize = 1;
var perceptionCamera = m_TestObject.AddComponent<PerceptionCamera>();
perceptionCamera.captureRgbImages = false;
m_TestObject.SetActive(true);
return perceptionCamera;
}
}
}

/com.unity.perception/Tests/Runtime/Randomization/ScenarioTests.cs.meta → /com.unity.perception/Tests/Runtime/Randomization/ScenarioTests/ScenarioTests.cs.meta

正在加载...
取消
保存