浏览代码

Merge pull request #79 from Unity-Technologies/randomizers

Added Randomizers and RandomizerTags
/main
GitHub 4 年前
当前提交
f4522d55
共有 237 个文件被更改,包括 2053 次插入550 次删除
  1. 4
      com.unity.perception/CHANGELOG.md
  2. 74
      com.unity.perception/Documentation~/Randomization/Index.md
  3. 50
      com.unity.perception/Documentation~/Randomization/Scenarios.md
  4. 66
      com.unity.perception/Editor/Randomization/StaticData.cs
  5. 220
      com.unity.perception/Editor/Randomization/Uss/Styles.uss
  6. 4
      com.unity.perception/Editor/Randomization/Uss/Styles.uss.meta
  7. 17
      com.unity.perception/Editor/Randomization/Uxml/ScenarioBaseElement.uxml
  8. 7
      com.unity.perception/Editor/Unity.Perception.Editor.asmdef
  9. 5
      com.unity.perception/Runtime/GroundTruth/Labelers/Visualization/Materials/OutlineMaterial.mat
  10. 76
      com.unity.perception/Runtime/Randomization/Parameters/CategoricalParameter.cs
  11. 4
      com.unity.perception/Runtime/Randomization/Parameters/CategoricalParameterBase.cs
  12. 15
      com.unity.perception/Runtime/Randomization/Parameters/NumericParameter.cs
  13. 74
      com.unity.perception/Runtime/Randomization/Parameters/Parameter.cs
  14. 2
      com.unity.perception/Runtime/Randomization/Samplers/FloatRange.cs
  15. 16
      com.unity.perception/Runtime/Randomization/Samplers/SamplerTypes/ConstantSampler.cs
  16. 21
      com.unity.perception/Runtime/Randomization/Samplers/SamplerTypes/NormalSampler.cs
  17. 21
      com.unity.perception/Runtime/Randomization/Samplers/SamplerTypes/UniformSampler.cs
  18. 2
      com.unity.perception/Runtime/Randomization/Samplers/SamplerUtility.cs
  19. 27
      com.unity.perception/Runtime/Randomization/Scenarios/FixedLengthScenario.cs
  20. 26
      com.unity.perception/Runtime/Randomization/Scenarios/Scenario.cs
  21. 289
      com.unity.perception/Runtime/Randomization/Scenarios/ScenarioBase.cs
  22. 12
      com.unity.perception/Runtime/Randomization/Scenarios/ScenarioBase.cs.meta
  23. 10
      com.unity.perception/Tests/Runtime/Randomization/ParameterTests/CategoricalParameterTests.cs
  24. 63
      com.unity.perception/Tests/Runtime/Randomization/ScenarioTests.cs
  25. 1
      com.unity.perception/package.json
  26. 2
      com.unity.perception/Editor/Randomization/Uxml/Sampler/FloatRangeElement.uxml
  27. 2
      com.unity.perception/Editor/Randomization/VisualElements/Randomizer/DragToReorderManipulator.cs.meta
  28. 2
      com.unity.perception/Editor/Randomization/VisualElements/Parameter/CategoricalOptionElement.cs
  29. 3
      com.unity.perception/Editor/Randomization/VisualElements/Sampler/FloatRangeElement.cs
  30. 35
      com.unity.perception/Editor/Randomization/VisualElements/Randomizer/DragToReorderManipulator.cs
  31. 5
      com.unity.perception/Editor/Randomization/VisualElements/Sampler/SamplerElement.cs
  32. 22
      com.unity.perception/Runtime/Randomization/Parameters/ParameterTypes/NumericParameters/BooleanParameter.cs
  33. 8
      com.unity.perception/Runtime/Randomization/Parameters/ParameterTypes/NumericParameters/FloatParameter.cs
  34. 8
      com.unity.perception/Runtime/Randomization/Parameters/ParameterTypes/NumericParameters/IntegerParameter.cs
  35. 12
      com.unity.perception/Runtime/Randomization/Parameters/ParameterTypes/NumericParameters/Vector2Parameter.cs
  36. 13
      com.unity.perception/Runtime/Randomization/Parameters/ParameterTypes/NumericParameters/Vector3Parameter.cs
  37. 14
      com.unity.perception/Runtime/Randomization/Parameters/ParameterTypes/NumericParameters/Vector4Parameter.cs
  38. 3
      com.unity.perception.meta
  39. 29
      com.unity.perception/Documentation~/Randomization/RandomizerTags.md
  40. 42
      com.unity.perception/Documentation~/Randomization/Randomizers.md
  41. 8
      com.unity.perception/Editor/Randomization/Editors.meta
  42. 5
      com.unity.perception/Editor/Randomization/Icons/ChevronLeft.png
  43. 144
      com.unity.perception/Editor/Randomization/Icons/ChevronLeft.png.meta
  44. 6
      com.unity.perception/Editor/Randomization/Icons/ChevronRight.png
  45. 144
      com.unity.perception/Editor/Randomization/Icons/ChevronRight.png.meta
  46. 8
      com.unity.perception/Editor/Randomization/PropertyDrawers.meta
  47. 3
      com.unity.perception/Editor/Randomization/Uxml/Parameter.meta
  48. 3
      com.unity.perception/Editor/Randomization/Uxml/Randomizer.meta
  49. 14
      com.unity.perception/Editor/Randomization/Uxml/RunInUSimWindow.uxml
  50. 10
      com.unity.perception/Editor/Randomization/Uxml/RunInUSimWindow.uxml.meta
  51. 3
      com.unity.perception/Editor/Randomization/Uxml/Sampler.meta
  52. 8
      com.unity.perception/Editor/Randomization/VisualElements.meta
  53. 3
      com.unity.perception/Runtime/Randomization/Parameters/ParameterTypes/CategorialParameters.meta
  54. 3
      com.unity.perception/Runtime/Randomization/Parameters/ParameterTypes/NumericParameters.meta
  55. 8
      com.unity.perception/Runtime/Randomization/Randomizers.meta
  56. 65
      com.unity.perception/Runtime/Randomization/Scenarios/USimScenario.cs
  57. 3
      com.unity.perception/Runtime/Randomization/Scenarios/USimScenario.cs.meta
  58. 8
      com.unity.perception/Tests/Runtime/Randomization/RandomizerTests.meta
  59. 88
      com.unity.perception/Editor/Randomization/Editors/ScenarioBaseEditor.cs
  60. 8
      com.unity.perception/Editor/Randomization/Editors/ScenarioBaseEditor.cs.meta
  61. 11
      com.unity.perception/Editor/Randomization/Editors/RunInUSimWindow.cs.meta
  62. 247
      com.unity.perception/Editor/Randomization/Editors/RunInUSimWindow.cs
  63. 25
      com.unity.perception/Editor/Randomization/PropertyDrawers/ColorHsvaDrawer.cs
  64. 8
      com.unity.perception/Editor/Randomization/PropertyDrawers/ColorHsvaDrawer.cs.meta
  65. 26
      com.unity.perception/Editor/Randomization/PropertyDrawers/ParameterDrawer.cs
  66. 8
      com.unity.perception/Editor/Randomization/PropertyDrawers/ParameterDrawer.cs.meta
  67. 8
      com.unity.perception/Editor/Randomization/Uxml/Parameter/CategoricalOptionElement.uxml
  68. 12
      com.unity.perception/Editor/Randomization/Uxml/Parameter/CategoricalParameterTemplate.uxml
  69. 10
      com.unity.perception/Editor/Randomization/Uxml/Parameter/ParameterDrawer.uxml
  70. 8
      com.unity.perception/Editor/Randomization/Uxml/Parameter/ParameterDrawer.uxml.meta
  71. 6
      com.unity.perception/Editor/Randomization/Uxml/Parameter/ParameterElement.uxml
  72. 3
      com.unity.perception/Editor/Randomization/Uxml/Randomizer/AddRandomizerMenu.uxml.meta
  73. 3
      com.unity.perception/Editor/Randomization/Uxml/Randomizer/MenuDirectoryElement.uxml.meta
  74. 3
      com.unity.perception/Editor/Randomization/Uxml/Randomizer/RandomizerElement.uxml.meta
  75. 3
      com.unity.perception/Editor/Randomization/Uxml/Randomizer/RandomizerList.uxml.meta
  76. 13
      com.unity.perception/Editor/Randomization/Uxml/Randomizer/AddRandomizerMenu.uxml
  77. 6
      com.unity.perception/Editor/Randomization/Uxml/Randomizer/MenuDirectoryElement.uxml
  78. 16
      com.unity.perception/Editor/Randomization/Uxml/Randomizer/RandomizerElement.uxml
  79. 8
      com.unity.perception/Editor/Randomization/Uxml/Randomizer/RandomizerList.uxml
  80. 10
      com.unity.perception/Editor/Randomization/Uxml/Sampler/SamplerElement.uxml
  81. 3
      com.unity.perception/Editor/Randomization/VisualElements/Parameter.meta
  82. 8
      com.unity.perception/Editor/Randomization/VisualElements/Parameter/CategoricalOptionElement.cs.meta
  83. 39
      com.unity.perception/Editor/Randomization/VisualElements/Parameter/ColorHsvaField.cs
  84. 8
      com.unity.perception/Editor/Randomization/VisualElements/Parameter/ColorHsvaField.cs.meta
  85. 48
      com.unity.perception/Editor/Randomization/VisualElements/Parameter/DrawerParameterElement.cs
  86. 8
      com.unity.perception/Editor/Randomization/VisualElements/Parameter/DrawerParameterElement.cs.meta
  87. 196
      com.unity.perception/Editor/Randomization/VisualElements/Parameter/ParameterElement.cs
  88. 8
      com.unity.perception/Editor/Randomization/VisualElements/Parameter/ParameterElement.cs.meta
  89. 3
      com.unity.perception/Editor/Randomization/VisualElements/Randomizer.meta

4
com.unity.perception/CHANGELOG.md


### Added
Added Randomizers and RandomizerTags
Removed ParameterConfigurations (replaced with Randomizers)
### Fixed

74
com.unity.perception/Documentation~/Randomization/Index.md


*NOTE: The Perception package's randomization toolset is currently marked as experimental and is subject to change.*
The randomization toolset simplifies randomizing aspects of generating synthetic data. It facilitates exposing parameters for randomization, offers samplers to pick random values from parameters, and provides scenarios to define a full randomization process. Each of these also allows for custom implementations to fit particular randomization needs.
The randomization toolset simplifies randomizing aspects of generating synthetic data. It facilitates exposing parameters for randomization, offers samplers to pick random values from parameters, and provides scenarios to coordinate a full randomization process. Each of these also allows for custom implementations to fit particular randomization needs.
**What is Domain Randomization?**
#### What is Domain Randomization?
**How can a Unity project be randomized using the Perception Randomization toolset?**
#### How can a Unity project be randomized using the Perception Randomization toolset?
1. Create a parameter configuration
2. Add parameters to the parameter configuration
3. Customize parameter properties and samplers
4. Create a scenario to control simulation execution
1. Create a scenario
2. Define and add randomizers to the scenario
3. Customize parameters and samplers in the randomizers
4. Generate randomized perception data
As the first step mentions, randomization begins with creating a new ParameterConfiguration component. From here, users can configure and organize new random parameters to control various aspects of their simulation directly from the inspector in the Unity editor.
Beginning with step 1, add a scenario component to your simulation. This scenario will act as the central hub for all randomization activities that occur when your scene is executed.
![Example Parameters](./Images/ParameterConfiguration.png)
Next, add a few randomizers to the scenario. The randomizers, in conjunction with the scenario, will perform the actual randomization activities within the simulation.
Next, create a few parameters and modify their properties. Parameters often customize their random variables through the parameter configuration using samplers. Samplers enable users to specify a type of probabilty distribution to use when generating random values.
After adding the necessary randomizers, configure the random parameters assigned to each randomizer to further customize how the simulation is randomized. The random parameters and samplers exposed in each randomizer's inspector can be manipulated to specify different probabilty distributions to use when generating random values.
Finally, add a Scenario component to the scene. Scenarios are used to coordinate the application of randomizations during the execution of a simulation.
Once the project has been randomized and your scene has been configured with the data capture tools available in the perception package, enter play mode in the editor or execute your scenario through the Unity Simulation Cloud service to generate domain randomized perception data.
Continue reading for more details concerning the primary components driving randomizations in the perception package, including:
1. Scenarios
2. Randomizers
3. Randomizer Tags
4. Parameters
5. Samplers
Continue reading for more details concerning the three primary components driving randomizations in the perception package: parameters, samplers, and scenarios.
## Scenarios
## Parameters
Within a randomized simulation, the scenario component has three responsibilities:
1. Controlling the execution flow of your simulation
2. Defining a list of randomizers
3. Defining constants that can be configured externally from a built Unity player
Parameters are used to map common types of simulation properties to random variables. For example, a Vector3 size parameter can be used to randomize the x, y, and z dimensions of an obstacle. Or a material parameter can be used to swap between different terrain surface materials. Additionally, parameter sub-properties can be modified from the parameter configuration in playmode better visualize the impact of different randomization settings.
The fundamental principle of domain randomization is to simulate environments under a variety of randomized conditions. Each **iteration** of a scenario is intended to encapsulate one complete run of a simulated environment under uniquely randomized conditions. Scenarios futher define what conditions determine the end of an iteration and how many iterations to perform.
To read more about how to create custom parameter types, navigate over to the [parameters doc](Parameters.md).
To actually randomize a simulation, randomizers can be added to a scenario to vary different simulation properties. At runtime, the scenario will execute each randomizer according to its place within the randomizers list.
Scenarios can also define constants from which to expose global simulation behaviors automatically. By modifying serialized constants externally, users can customize their simulation runtime even after their project has been built.
To read more about scenarios and how to customize them, navigate over to the [scenarios doc](Scenarios.md).
## Randomizers
Randomizers encapsulate specific randomization activities to perform during the lifecycle of a randomized simulation. For example, randomizers exist for spawning objects, repositioning lights, varying the color of objects, etc. Randomizers expose random parameters to their inspector interface to further customize these variations.
To read more about how to create custom parameter types, navigate over to the [randomizers doc](Randomizers.md).
## Samplers
Samplers generate random float values by sampling from probability distributions. They are considered bounded since each random sampler generates float values within a range defined by a minumum and maximum value. The values generated from samplers are often used to randomize the sub components of parameters.
## Randomizer Tags
![Example Parameters](./Images/ColorParameter.png)
RandomizerTags are the primary mechanism by which randomizers query for a certain subset of GameObjects to randoize within a simulation. For example, a rotation randomizer could query for all GameObjects with a RotationRandomizerTag component to obtain an array of all objects the randomizer should vary for the given simulation iteration.
For example, a color parameter has four independently randomizable components: hue, saturation, value, and alpha. Each of the four samplers attached to a color parameter can employ a unique probability distribution to customize how new colors are sampled within a simulation. Out of the box, the perception package supports uniform and normal distribution sampling. So in our color example, a user may choose a normal distribution for their hue, a uniform distribution for saturation, and a constant value sampler for the value and alpha color components.
To read more about how to use RandomizerTags, navigate over to the [RandomizerTags doc](RandomizerTags.md).
Take a look at the [samplers doc](Samplers.md) to learn more about implementing custom probability distributions and samplers that can integrate with the perception package.
## Parameters
## Scenarios
Parameters are classes that utilize samplers to deterministically generate random typed values. Parameters are often exposed within the inspector interface of randomizers to allow users to customize said randomizer's behavior. To accomplish this, parameters combine and transform the float values produced by one or more samplers into various C# types. For example, a Vector3 parameter can be used to map three samplers to the x, y, and z dimensions of a GameObject. Or a material parameter can utilize a sampler to randomly select one material from a list of possible options.
Scenarios have three responsibilities:
1. Controlling the execution flow of your simulation
2. Customizing the application of random parameters in your project
3. Defining constants that can be configured externally from a built Unity player
To read more about how to create custom parameter types, navigate over to the [parameters doc](Parameters.md).
The fundamental principle of domain randomization is to simulate environments under a variety of randomized conditions. Each **iteration** of a scenario is intended to encapsulate one complete run of a simulated environment under uniquely randomized conditions. Scenarios determine how to setup a new iteration, what conditions determine the end of an iteration, how to clean up a completed iteration, and finally how many iterations to perform. Each of these behaviors can be customized for a new scenario by deriving from the Scenario class in the perception package.
It is possible to configure parameters to affect simulation properties directly from the parameter configuration. While useful, this feature is constrained to a particular set of use cases. Instead, a user can reference existing parameters in their scenario to implement more intricate randomizations. For example, a user can reference a `SpawnCount` parameter and a `ObjectPosition` parameter to randomize the positions of a dynamic number of objects during the setup step of a scenario.
## Samplers
![Example Parameters](./Images/TestScenario.png)
Samplers generate bounded random float values by sampling from probability distributions. They are considered bounded since each random sampler generates float values within a range defined by a minumum and maximum value.
Finally, scenarios define constants from which to expose global simulation behaviors automatically. By modifying serialized constants externally, users can customize their simulation runtime even after their project has been built.
Take a look at the [samplers doc](Samplers.md) to learn more about implementing custom probability distributions and samplers that can integrate with the perception package.
Visit our [randomization tutorial doc](Tutorial.md) to get started using the perception package's randomization tools in an example project.
Visit our [randomization tutorial doc]() to get started using the perception package's randomization tools in an example project.

50
com.unity.perception/Documentation~/Randomization/Scenarios.md


# Scenarios
Scenarios have three responsibilities:
1. Controlling the execution flow of your simulation
2. Customizing the application of random parameters in your project
3. Defining constants that can be configured externally from a built Unity player
1. Controlling the execution flow of your simulation
2. Defining a list of randomizers
3. Defining constants that can be configured externally from a built Unity player
By default, the perception package includes one ready-made scenario, the `FixedLengthScenario` class. This scenario runs each iteration for a fixed number of frames and is compatible with the Run in USim window for cloud simulation execution.
## Scenario Cloud Execution (USim)
Users can utilize Unity's Unity Simulation (USim) service to execute a scenario in the cloud through the perception package's Run in USim window. To open this window from the Unity editor using the top menu bar, navigate to `Window -> Run in USim`.
From the newly opened editor window, customize the following settings to configure a new USim run:
1. **Run Name** - the name of the USim run (example: TestRun0)
2. **Total Iterations** - The number of scenario iterations to complete during the run
3. **Instance Count** - The number of USim worker instances to distribute execution between
4. **Main Scene** - The Unity scene to execute
5. **Scenario** - The scenario to execute
6. **USim Worker Config** - the type of USim worker instance to execute the scenario with. Determines per instance specifications such as the number of CPU cores, amount of memory, and presence of a GPU for accelerated execution.
By default, the perception package includes one ready-made scenario, the `FixedFrameLengthScenario` class. This scenario is useful for when all created parameters have target GameObjects configured directly in the `ParameterConfiguration` and the scenario execution requires little modification.
NOTE: To execute a scenario using the Run in USim window, the scenario class must implement the USimScenario class.
More commonly, users will find the need to create their own Scenario class. Below is an overview of the more common scenario properties and methods a user can override:
## Custom Scenarios
For use cases where the scenario should run for an arbitrary number of frames, implementing a custom scenario may be necessary. Below are the two most common scenario properties a user might want to override to implement custom scenario iteration conditions:
3. **Initialize** - actions to complete before the scenario has begun iterating
4. **Setup** - actions to complete at the beginning of each iteration
5. **Teardown** - actions to complete at the end of each iteration
6. **OnComplete** - actions to complete after the scenario as completed
Scenarios define constants from which to expose global simulation behaviors like a starting iteration value or a total iteration count. Users can serialize these scenario constants to JSON, modify them in an external program, and finally reimport the JSON constants at runtime to configure their simulation even after their project has been built. Below is an example of the constants used in the `FixedLengthScenario` class:
Scenarios define constants from which to expose global simulation behaviors like a starting iteration value or a total iteration count. Users can serialize these scenario constants to JSON, modify them in an external program, and finally reimport the JSON constants at runtime to configure their simulation even after their project has been built. Below is an example of the constants class used in the `FixedLengthScenario` class:
public class Constants
public class Constants : USimConstants
public int iterationFrameLength = 1;
public int startingIteration;
public int totalIterations = 1000;
public int framesPerIteration = 1;
A few key things to note here:
1. Make sure to include the [Serializable] attribute on a constant class. This will ensure that the constants can be manipulated from the Unity inspector.
2. By default, UnityEngine.Object class references cannot be serialized to JSON in a meaningful way. This includes Monobehaviors and SerializedObjects. For more information on what can and can't be serialized, take a look at the [Unity JsonUtility manual](https://docs.unity3d.com/ScriptReference/JsonUtility.html).
3. A scenario class's Serialize() and Deserialized() methods can be overriden to implement custom serialization strategies.
There are a few key things to note here:
1. The constants class will need to inherit from USimConstants to be compatible with the Run in USim window. Deriving from USimConstants will add a few key properties to the constants class that are needed to coordinate a USim run.
2. Make sure to include the [Serializable] attribute on a constant class. This will ensure that the constants can be manipulated from the Unity inspector.
3. By default, UnityEngine.Object class references cannot be serialized to JSON in a meaningful way. This includes Monobehaviors and SerializedObjects. For more information on what can and can't be serialized, take a look at the [Unity JsonUtility manual](https://docs.unity3d.com/ScriptReference/JsonUtility.html).
4. A scenario class's Serialize() and Deserialized() methods can be overriden to implement custom serialization strategies.

66
com.unity.perception/Editor/Randomization/StaticData.cs


using System;
using System.Collections;
using System.Linq;
using System.Reflection;
using UnityEngine.Experimental.Perception.Randomization.Randomizers;
using UnityEngine.Experimental.Perception.Randomization.Samplers;
namespace UnityEngine.Experimental.Perception.Randomization.Editor

internal static readonly string samplerSerializedFieldType;
internal static Type[] parameterTypes;
internal static Type[] randomizerTypes;
parameterTypes = GetConstructableDerivedTypes<Parameter>();
randomizerTypes = GetConstructableDerivedTypes<Randomizer>();
samplerTypes = GetConstructableDerivedTypes<ISampler>();
var samplerType = typeof(ISampler);
samplerSerializedFieldType = $"{samplerType.Assembly.GetName().Name} {samplerType.FullName}";

types.Add(type);
}
return types.ToArray();
}
internal static object GetManagedReferenceValue(SerializedProperty prop, bool parent=false)
{
var path = prop.propertyPath.Replace(".Array.data[", "[");
object obj = prop.serializedObject.targetObject;
var elements = path.Split('.');
if (parent)
elements = elements.Take(elements.Count() - 1).ToArray();
foreach (var element in elements)
{
if (element.Contains("["))
{
var elementName = element.Substring(0, element.IndexOf("["));
var index = Convert.ToInt32(element.Substring(element.IndexOf("[")).Replace("[","").Replace("]",""));
obj = GetArrayValue(obj, elementName, index);
}
else
obj = GetValue(obj, element);
}
return obj;
}
static object GetValue(object source, string name)
{
if (source == null)
return null;
var type = source.GetType();
var f = type.GetField(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
if (f == null)
{
var p = type.GetProperty(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
return p == null ? null : p.GetValue(source, null);
}
return f.GetValue(source);
}
static object GetArrayValue(object source, string name, int index)
{
var value = GetValue(source, name);
if (!(value is IEnumerable enumerable))
return null;
var enumerator = enumerable.GetEnumerator();
while (index-- >= 0)
enumerator.MoveNext();
return enumerator.Current;
}
public 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;
}
}
}

220
com.unity.perception/Editor/Randomization/Uss/Styles.uss


.dark-viewport {
border-radius: 5px;
background-color: #191919;
padding: 2px;
}
.parameter-container {
border-width: 2px;
border-radius: 5px;
flex-direction: row;
margin: 2px 1px;
}
.parameter-type-menu {
margin: 1px 3px;
border-width: 1px;
border-radius: 3px;
background-color: #585858;
/* General randomization classes */
.randomization__remove-item-button {
width: 12px;
height: 14px;
background-image: resource("Packages/com.unity.perception/Editor/Randomization/Icons/X.png");
#parameter-type-menu .unity-toolbar-menu__text {
font-size: 13px;
padding: 3px;
}
.sampler-type-menu {
flex-grow: 1.5;
border-radius: 3px;
border-width: 1px;
margin-top: 2px;
.randomization__collapse-toggle {
flex-shrink: 0;
margin-left: 3px;
width: 10px;
height: 10px;
background-image: resource("Packages/com.unity.perception/Editor/Randomization/Icons/FoldoutOpen.png");
.sampler-name {
/*color: lightgreen;*/
-unity-font-style: bold;
.collapsed .randomization__collapse-toggle {
background-image: resource("Packages/com.unity.perception/Editor/Randomization/Icons/FoldoutClosed.png");
.parameter-type-label-container {
flex-direction: row;
justify-content: space-between;
align-items: center;
margin-bottom: 1px;
.randomization__collapse-toggle:hover {
-unity-background-image-tint-color: cornflowerblue;
.parameter-type-label-box {
flex-direction: row;
align-items: center;
justify-content: flex-start;
flex-grow: 0;
-unity-text-align: middle-center;
border-width: 0;
padding: 1px;
font-size: 13px;
-unity-font-style: bold;
.collapsed .randomization__collapsible-container {
display: none;
.parameter-type-label {
color: cornflowerblue;
/*min-width: 120px;*/
-unity-text-align: middle-left;
.randomization__chevron-left {
height: 12px;
width: 12px;
background-image: resource("Packages/com.unity.perception/Editor/Randomization/Icons/ChevronLeft.png");
.parameter-type-label-box .unity-base-text-field__input {
padding-left: 6px;
padding-right: 6px;
.randomization__chevron-right {
height: 12px;
width: 12px;
background-image: resource("Packages/com.unity.perception/Editor/Randomization/Icons/ChevronRight.png");
.unity-toggle {
margin-left: 0;
/* Scenario classes */
.scenario__info-box {
border-width: 1px;
border-color: #191919;
padding: 2px 4px 2px 4px;
white-space: normal;
margin-top: 4px;
margin-bottom: 4px;
.property-selection-container {
flex-direction: row;
.scenario__dark-viewport {
border-radius: 5px;
background-color: #191919;
padding: 2px;
.property-select-menu {
flex-grow: 1;
border-width: 1px;
padding-bottom: 1px;
border-radius: 3px;
margin-right: 1px;
}
.remove-parameter-button {
background-image: resource("Packages/com.unity.perception/Editor/Randomization/Icons/X.png");
width: 16px;
height: 16px;
/* Randomizer classes */
.randomizer__drag-bar {
width: 100px;
height: 6px;
background-color: rgba(100,149,237,0.4);
position: absolute;
.move-buttons-container {
width: auto;
margin-right: 6px;
border-color: black;
border-top-left-radius: 3px;
border-bottom-left-radius: 3px;
background-color: #2A2A2A;
padding: 3px 2px;
justify-content: center;
.randomizer__element {
border-width: 2px;
border-radius: 5px;
margin: 2px 1px;
background-color: #383838;
flex-direction: row;
.drag-handle {
.randomizer__drag-handle {
flex-shrink: 0;
height: 70%;
height: 100%;
.move-button {
border-width: 0;
width: 42px;
height: 16px;
padding: 9px;
-unity-background-scale-mode: scale-to-fit;
.randomizer__drag-handle:hover {
-unity-background-image-tint-color: black;
.unity-imgui-container {
margin-left: 1px;
margin-right: 3px;
.randomizer__add-menu-directory-item {
padding: 2px 10px 2px 20px;
.search-icon {
width: 16px;
height: 16px;
margin-top: 2px;
flex-shrink: 0;
background-image: resource("Packages/com.unity.perception/Editor/Randomization/Icons/Search.png");
.randomizer__add-menu-directory-item:hover {
background-color: #3E5F96;
.collapse-parameter-toggle {
flex-shrink: 0;
margin-right: 2px;
width: 12px;
height: 12px;
background-image: resource("Packages/com.unity.perception/Editor/Randomization/Icons/FoldoutOpen.png");
}
.collapsed-parameter .collapse-parameter-toggle {
background-image: resource("Packages/com.unity.perception/Editor/Randomization/Icons/FoldoutClosed.png");
.randomizer__reordering-hover-indicator {
width: 100px;
height: 6px;
background-color: rgba(100,149,237,0.4);
position: absolute;
.collapsed-parameter .parameter-properties-container {
display: none;
.randomizer__menu-search-icon {
position: absolute;
left: 10px;
top: 9px;
width: 10px;
height: 10px;
background-image: resource("Packages/com.unity.perception/Editor/Randomization/Icons/Search.png");
.collapsed-parameter .move-buttons-container {
flex-direction: row;
.randomizer__menu-search-bar .unity-base-text-field__input {
padding-left: 15px;
.collapsed-parameter .move-button {
width: 16px;
}
.categorical-option {
/* Parameter classes */
.parameter__categorical-option {
flex-direction: row;
background-color: #3F3F3F;
margin: 1px;

.options-list-view {
.parameter__categorical-options-list {
background-color: #191919;
border-radius: 4px;
margin-right: 2px;

.option-property-field {
.parameter__categorical-option-property-field {
.uniform-probability .hide-when-uniform {
display: none;
}
.add-option-button {
.parameter__categorical-options-list-button {
align-self: flex-end;
border-width: 0;
border-top-right-radius: 0;

margin-right: 2px;
}
.add-option-button:hover {
.parameter__categorical-options-list-button:hover {
.add-option-button:active {
.parameter__categorical-options-list-button:active {
.remove-option-button {
width: 12px;
height: 14px;
background-image: resource("Packages/com.unity.perception/Editor/Randomization/Icons/X.png");
}
.parameter-drag-bar {
width: 100px;
height: 6px;
background-color: rgba(100,149,237,0.4);
position: absolute;
/* Sampler classes */
.sampler__type-menu {
flex-grow: 1.5;
border-radius: 3px;
border-width: 1px;
margin-top: 2px;
margin-right: 3px;
.float-range .unity-base-field__label {
.sampler__float-range .unity-base-field__label {
min-width: auto;
margin-right: 4px;
}

4
com.unity.perception/Editor/Randomization/Uss/Styles.uss.meta


fileFormatVersion: 2
guid: 5e1a10e7ee7a46898d0138a9d08615ee
timeCreated: 1589577216
guid: a3321de444a246f59e8a0445d5d2bf5d
timeCreated: 1601665266

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


<UXML xmlns="UnityEngine.UIElements" xmlns:editor="UnityEditor.UIElements">
<VisualElement>
<Style src="../Uss/Styles.uss"/>
<Label style="white-space: normal; margin-bottom: 5px; margin-top: 2px" 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."/>
<VisualElement name="inspector-properties" style="margin-bottom: 20px;"/>
<VisualElement name="configuration-container" class="dark-viewport">
<Toggle label="Quit On Complete" tooltip="Quit the application when the scenario completes" binding-path="quitOnComplete" style="margin-left: 3px"/>
<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."/>
<VisualElement name="inspector-properties" style="margin-bottom: 4px;"/>
<VisualElement class="scenario__dark-viewport" style="padding-left: 16px">
<Toggle label="Quit On Complete" tooltip="Quit the application when the scenario completes" binding-path="quitOnComplete"/>
<editor:PropertyField binding-path="constants" tooltip="A custom list of parameters for this scenario that will be JSON serialized. You can add or remove constants by modifying the code for this scenario class. Only these properties of the simulation can be changed externally from a built player."/>
<editor:PropertyField
binding-path="constants"
tooltip="A list of parameters for this scenario that will be JSON serialized."/>
<editor:PropertyField tooltip="Read constants from JSON when the application starts" name="deserialize-on-start" label="Deserialize On Start" binding-path="deserializeOnStart" style="padding-left: 4px;"/>
<VisualElement style="flex-direction: row;">
<Button name="serialize-constants" text="Serialize Constants" style="flex-grow: 1;"/>
<Button name="deserialize-constants" text="Deserialize Constants" style="flex-grow: 1;"/>

<VisualElement name="randomizer-list-placeholder"/>
</VisualElement>
</UXML>

7
com.unity.perception/Editor/Unity.Perception.Editor.asmdef


{
"name": "Unity.Perception.Editor",
"rootNamespace": "",
"references": [
"Unity.RenderPipelines.Core.Runtime",
"Unity.RenderPipelines.HighDefinition.Runtime",

"Unity.Perception.Runtime",
"PathCreatorEditor",
"PathCreator",
"UnityEngine.UI"
"UnityEngine.UI",
"Unity.Simulation.Client.Editor"
],
"includePlatforms": [
"Editor"

"overrideReferences": true,
"precompiledReferences": [
"Newtonsoft.Json.dll"
"Newtonsoft.Json.dll",
"ZipUtility.dll"
],
"autoReferenced": true,
"defineConstraints": [],

5
com.unity.perception/Runtime/GroundTruth/Labelers/Visualization/Materials/OutlineMaterial.mat


m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier:
version: 1
version: 2
--- !u!21 &2100000
Material:
serializedVersion: 6

m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: 3050
m_CustomRenderQueue: 3000
stringTagMap:
RenderType: Transparent
disabledShaderPasses:

- _Color: {r: 1, g: 1, b: 1, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
m_BuildTextureStacks: []

76
com.unity.perception/Runtime/Randomization/Parameters/CategoricalParameter.cs


[Serializable]
public abstract class CategoricalParameter<T> : CategoricalParameterBase
{
[SerializeField] internal bool uniform;
[SerializeField] internal bool uniform = true;
[SerializeReference] ISampler m_Sampler = new UniformSampler(0f, 1f);
[SerializeField] List<T> m_Categories = new List<T>();

/// Returns a list containing the samplers attached to this parameter
/// Returns an IEnumerable that iterates over each sampler field in this parameter
public override ISampler[] samplers => new [] { m_Sampler };
internal override IEnumerable<ISampler> samplers
{
get { yield return m_Sampler; }
}
/// <summary>
/// The sample type generated by this parameter

public float GetProbability(int index) => probabilities[index];
/// <summary>
/// Constructs a new categorical parameter
/// </summary>
protected CategoricalParameter() { }
/// <summary>
/// Create a new categorical parameter from a list of categories with uniform probabilities
/// Updates this parameter's list of categorical options
/// <param name="categoricalOptions">List of categories</param>
/// <exception cref="ArgumentException"></exception>
protected CategoricalParameter(IEnumerable<T> categoricalOptions)
/// <param name="categoricalOptions">The categorical options to configure</param>
public void SetOptions(IEnumerable<T> categoricalOptions)
if (categories.Count == 0)
throw new ArgumentException("List of options is empty");
uniform = true;
foreach (var option in categoricalOptions)
AddOption(option, 1f);
m_Categories.Clear();
probabilities.Clear();
foreach (var category in categoricalOptions)
AddOption(category, 1f);
NormalizeProbabilities();
/// Creates a new categorical parameter from a list of categories and their associated probabilities
/// Updates this parameter's list of categorical options
/// <param name="categoricalOptions">List of categories and their associated probabilities</param>
/// <exception cref="ArgumentException"></exception>
protected CategoricalParameter(IEnumerable<(T, float)> categoricalOptions)
/// <param name="categoricalOptions">The categorical options to configure</param>
public void SetOptions(IEnumerable<(T, float)> categoricalOptions)
if (categories.Count == 0)
throw new ArgumentException("List of options is empty");
m_Categories.Clear();
probabilities.Clear();
internal override void AddOption()
{
m_Categories.Add(default);
probabilities.Add(0f);
}
internal void AddOption(T option, float probability)
void AddOption(T option, float probability)
internal override void RemoveOption(int index)
{
m_Categories.RemoveAt(index);
probabilities.RemoveAt(index);
}
internal override void ClearOptions()
{
m_Categories.Clear();
probabilities.Clear();
}
/// <summary>
/// Returns a list of the potential categories this parameter can generate
/// </summary>

/// Validates the categorical probabilities assigned to this parameter
/// </summary>
/// <exception cref="ParameterValidationException"></exception>
internal override void Validate()
public override void Validate()
{
base.Validate();
if (!uniform)

}
}
internal void NormalizeProbabilities()
void NormalizeProbabilities()
{
var totalProbability = 0f;
for (var i = 0; i < probabilities.Count; i++)

: m_Categories[BinarySearch(randomValue)];
}
internal sealed override void ApplyToTarget(int seedOffset)
/// <summary>
/// Generates a generic sample
/// </summary>
/// <returns>The generated sample</returns>
public override object GenericSample()
if (!hasTarget)
return;
target.ApplyValueToTarget(Sample());
return Sample();
}
}
}

4
com.unity.perception/Runtime/Randomization/Parameters/CategoricalParameterBase.cs


public abstract class CategoricalParameterBase : Parameter
{
[SerializeField] internal List<float> probabilities = new List<float>();
internal abstract void AddOption();
internal abstract void RemoveOption(int index);
internal abstract void ClearOptions();
}
}

15
com.unity.perception/Runtime/Randomization/Parameters/NumericParameter.cs


/// <returns>A NativeArray containing generated samples</returns>
public abstract NativeArray<T> Samples(int sampleCount, out JobHandle jobHandle);
internal sealed override void ApplyToTarget(int seedOffset)
/// <summary>
/// Generates a generic sample
/// </summary>
/// <returns>The generated sample</returns>
public override object GenericSample()
if (!hasTarget)
return;
target.ApplyValueToTarget(Sample());
return Sample();
internal override void Validate()
/// <summary>
/// Validate the settings of this parameter
/// </summary>
public override void Validate()
{
base.Validate();
foreach (var sampler in samplers)

74
com.unity.perception/Runtime/Randomization/Parameters/Parameter.cs


using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Experimental.Perception.Randomization.Samplers;

[Serializable]
public abstract class Parameter
{
/// <summary>
/// Returns the display name of a parameter type
/// </summary>
/// <param name="type">A subclass of Parameter</param>
/// <returns>The parameter type's display name</returns>
public static string GetDisplayName(Type type)
{
return type.Name.Replace("Parameter", "");
}
/// The name of the parameter
/// </summary>
[HideInInspector] public string name = "Parameter";
/// <summary>
/// The target this parameter apply a sample to
/// </summary>
[HideInInspector, SerializeField] public ParameterTarget target = new ParameterTarget();
/// <summary>
/// Indicates whether this parameter has a target GameObject
/// </summary>
public bool hasTarget => target.gameObject != null;
/// <summary>
/// An array containing a reference to each sampler field in this parameter
/// Returns an IEnumerable that iterates over each sampler field in this parameter
public abstract ISampler[] samplers { get; }
internal abstract IEnumerable<ISampler> samplers { get; }
/// <summary>
/// Constructs a new parameter

}
/// <summary>
/// Returns the display name of a parameter type
/// </summary>
/// <param name="type">A subclass of Parameter</param>
/// <returns>The parameter type's display name</returns>
public static string GetDisplayName(Type type)
{
return type.Name.Replace("Parameter", "");
}
/// <summary>
/// Deterministically ensures that no sampler shares the same seed when a parameter is initialized
/// </summary>
void InitializeSamplers()

}
/// <summary>
/// Resets sampler states and then offsets those states using the current scenario iteration
/// Resets the state of each sampler employed by this parameter
/// <param name="scenarioIteration">The current scenario iteration</param>
public void ResetState(int scenarioIteration)
public void ResetState()
{
sampler.IterateState(scenarioIteration);
}
}
/// <summary>
/// Offsets the state of each sampler employed by this parameter
/// </summary>
/// <param name="offsetIndex">Often the current scenario iteration</param>
public void IterateState(int offsetIndex)
{
foreach (var sampler in samplers)
sampler.IterateState(offsetIndex);
/// Applies one sampled value to this parameters assigned target gameobject
/// Generates a generic sample
internal abstract void ApplyToTarget(int seedOffset);
/// <returns>The generated sample</returns>
public abstract object GenericSample();
internal virtual void Validate()
{
if (hasTarget)
{
if (target.component == null)
throw new ParameterValidationException($"Null component target on parameter \"{name}\"");
if (string.IsNullOrEmpty(target.propertyName))
throw new ParameterValidationException($"Invalid property target on parameter \"{name}\"");
}
}
public virtual void Validate() { }
}
}

2
com.unity.perception/Runtime/Randomization/Samplers/FloatRange.cs


using System;
using Unity.Assertions;
using Assert = UnityEngine.Assertions.Assert;
namespace UnityEngine.Experimental.Perception.Randomization.Samplers
{

16
com.unity.perception/Runtime/Randomization/Samplers/SamplerTypes/ConstantSampler.cs


public void ResetState() { }
/// <summary>
/// Resets a sampler's state to its base random seed and then offsets said seed using an index value.
/// Note that ConstantSamplers do not have random states.
/// </summary>
/// <param name="index">Often a the active scenario's currentIteration</param>
public void ResetState(int index) { }
/// <summary>
/// Set the base seed value of this sampler.
/// Note that ConstantSamplers do not have base seeds.
/// </summary>
/// <param name="seed">The seed that will replace the sampler's current seed</param>
public void Rebase(uint seed) { }
/// <summary>
/// Deterministically offsets a sampler's state when generating values within a batched job.
/// Deterministically offsets a sampler's state.
/// Note that ConstantSamplers do not have a state to iterate.
/// </summary>
/// <param name="offsetIndex">

21
com.unity.perception/Runtime/Randomization/Samplers/SamplerTypes/NormalSampler.cs


}
/// <summary>
/// Resets a sampler's state to its base random seed and then offsets said seed using an index value
/// </summary>
/// <param name="index">Often a the active scenario's currentIteration</param>
public void ResetState(int index)
{
ResetState();
IterateState(index);
}
/// <summary>
/// Set the base seed value of this sampler
/// </summary>
/// <param name="seed">The seed that will replace the sampler's current seed</param>
public void Rebase(uint seed)
{
baseSeed = seed;
}
/// <summary>
/// Deterministically offsets a sampler's state when generating values within a batched job
/// Deterministically offsets a sampler's state
/// </summary>
/// <param name="offsetIndex">
/// The index used to offset the sampler's state.

21
com.unity.perception/Runtime/Randomization/Samplers/SamplerTypes/UniformSampler.cs


}
/// <summary>
/// Resets a sampler's state to its base random seed and then offsets said seed using an index value
/// </summary>
/// <param name="index">Often a the active scenario's currentIteration</param>
public void ResetState(int index)
{
ResetState();
IterateState(index);
}
/// <summary>
/// Set the base seed value of this sampler
/// </summary>
/// <param name="seed">The seed that will replace the sampler's current seed</param>
public void Rebase(uint seed)
{
baseSeed = seed;
}
/// <summary>
/// Deterministically offsets a sampler's state when generating values within a batched job
/// Deterministically offsets a sampler's state
/// </summary>
/// <param name="offsetIndex">
/// The index used to offset the sampler's state.

2
com.unity.perception/Runtime/Randomization/Samplers/SamplerUtility.cs


}
/// <summary>
/// Non-deterministically generates a random seed
/// Non-deterministically generates a non-zero random seed
/// </summary>
/// <returns>A non-deterministically generated random seed</returns>
public static uint GenerateRandomSeed()

27
com.unity.perception/Runtime/Randomization/Scenarios/FixedLengthScenario.cs


/// A scenario that runs for a fixed number of frames during each iteration
/// </summary>
[AddComponentMenu("Perception/Randomization/Scenarios/Fixed Length Scenario")]
public class FixedLengthScenario: Scenario<FixedLengthScenario.Constants>
public class FixedLengthScenario: USimScenario<FixedLengthScenario.Constants>
public class Constants
public class Constants : USimConstants
/// <summary>
/// The iteration index begin the simulation on
/// </summary>
public int startingIteration;
/// <summary>
/// The total number of iterations to complete before the simulation terminates
/// </summary>
public int totalIterations = 1000;
}
/// <summary>

/// <summary>
/// Returns whether the scenario has completed
/// </summary>
public override bool isScenarioComplete => currentIteration >= constants.totalIterations;
/// <summary>
/// Called before the scenario begins iterating
/// </summary>
public override void OnInitialize()
{
currentIteration = constants.startingIteration;
}
}
}

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


public T constants = new T();
/// <summary>
/// Returns this scenario's non-typed serialized constants
/// </summary>
public override object genericConstants => constants;
/// <summary>
public sealed override void Serialize()
public override void Serialize()
{
Directory.CreateDirectory(Application.dataPath + "/StreamingAssets/");
using (var writer = new StreamWriter(serializedConstantsFilePath, false))

/// Deserializes this scenario's constants from a json file in the Unity StreamingAssets folder
/// </summary>
/// <exception cref="ScenarioException"></exception>
public sealed override void Deserialize()
public override void Deserialize()
if (!File.Exists(serializedConstantsFilePath))
throw new ScenarioException($"JSON scenario constants file does not exist at path {serializedConstantsFilePath}");
var jsonText = File.ReadAllText(serializedConstantsFilePath);
constants = JsonUtility.FromJson<T>(jsonText);
if (string.IsNullOrEmpty(serializedConstantsFilePath))
{
Debug.Log("No constants file specified. Running scenario with built in constants.");
}
else if (File.Exists(serializedConstantsFilePath))
{
var jsonText = File.ReadAllText(serializedConstantsFilePath);
constants = JsonUtility.FromJson<T>(jsonText);
}
else
{
Debug.LogWarning($"JSON scenario constants file does not exist at path {serializedConstantsFilePath}");
}
}
}
}

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


using System;
using System.Collections.Generic;
using Unity.Simulation;
using UnityEngine.Experimental.Perception.Randomization.Randomizers;
using UnityEngine.Experimental.Perception.Randomization.Samplers;
using UnityEngine.Experimental.Perception.Randomization.Configuration;
using UnityEngine.Experimental.Perception.Randomization.Parameters;
/// The base class of all scenario classes
/// Derive ScenarioBase to implement a custom scenario
[DefaultExecutionOrder(-1)]
bool m_WaitingForFinalUploads;
RandomizerTagManager m_TagManager = new RandomizerTagManager();
IEnumerable<Randomizer> activeRandomizers
{
get
{
foreach (var randomizer in m_Randomizers)
if (randomizer.enabled)
yield return randomizer;
}
}
// ReSharper disable once InconsistentNaming
[SerializeReference] internal List<Randomizer> m_Randomizers = new List<Randomizer>();
/// If true, this scenario will quit the Unity application when it's finished executing
/// The RandomizerTagManager attached to this scenario
[HideInInspector] public bool quitOnComplete = true;
public RandomizerTagManager tagManager => m_TagManager;
/// When true, this scenario will deserializes constants from a Json file before it begins executing
/// Return the list of randomizers attached to this scenario
/// </summary>
public IReadOnlyList<Randomizer> randomizers => m_Randomizers.AsReadOnly();
/// <summary>
/// If true, this scenario will quit the Unity application when it's finished executing
[HideInInspector] public bool deserializeOnStart;
[HideInInspector] public bool quitOnComplete = true;
/// <summary>
/// The name of the Json file this scenario's constants are serialized to/from.

/// <summary>
/// Returns the active parameter scenario in the scene
/// </summary>
public static ScenarioBase ActiveScenario
public static ScenarioBase activeScenario
if (s_ActiveScenario != null)
throw new ScenarioException("There cannot be more than one active ParameterConfiguration");
if (value != null && s_ActiveScenario != null && value != s_ActiveScenario)
throw new ScenarioException("There cannot be more than one active Scenario");
s_ActiveScenario = value;
}
}

Application.dataPath + "/StreamingAssets/" + serializedConstantsFileName + ".json";
/// <summary>
/// Returns this scenario's non-typed serialized constants
/// </summary>
public abstract object genericConstants { get; }
/// <summary>
/// The number of frames that have elapsed since the current scenario iteration was Setup
/// </summary>
public int currentIterationFrame { get; private set; }

public abstract bool isScenarioComplete { get; }
/// <summary>
/// Called before the scenario begins iterating
/// Progresses the current scenario iteration
public virtual void OnInitialize() { }
protected virtual void IncrementIteration()
{
currentIteration++;
}
/// Called at the beginning of every scenario iteration
/// Serializes the scenario's constants to a JSON file located at serializedConstantsFilePath
public virtual void OnIterationSetup() { }
public abstract void Serialize();
/// Called at the start of every frame
/// Deserializes constants saved in a JSON file located at serializedConstantsFilePath
public virtual void OnFrameStart() { }
public abstract void Deserialize();
/// Called the frame after an iteration ends
/// This method executed directly after this scenario has been registered and initialized
public virtual void OnIterationTeardown() { }
protected virtual void OnAwake() { }
/// <summary>
/// Called when the scenario has finished iterating
/// </summary>
public virtual void OnComplete() { }
/// <summary>
/// Serializes the scenario's constants to a JSON file located at serializedConstantsFilePath
/// </summary>
public abstract void Serialize();
/// <summary>
/// Deserializes constants saved in a JSON file located at serializedConstantsFilePath
/// </summary>
public abstract void Deserialize();
void Awake()
{
activeScenario = this;
OnAwake();
foreach (var randomizer in m_Randomizers)
randomizer.Initialize(this, tagManager);
foreach (var randomizer in m_Randomizers)
randomizer.Create();
ValidateParameters();
}
ActiveScenario = this;
activeScenario = this;
}
void OnDisable()

void Start()
{
if (deserializeOnStart)
Deserialize();
foreach (var config in ParameterConfiguration.configurations)
config.ValidateParameters();
OnInitialize();
Deserialize();
}
void Update()

return;
}
// Wait for any final uploads before exiting quitting
if (m_WaitingForFinalUploads && quitOnComplete)
{
Manager.Instance.Shutdown();
if (!Manager.FinalUploadsDone)
return;
#if UNITY_EDITOR
UnityEditor.EditorApplication.ExitPlaymode();
#else
Application.Quit();
#endif
return;
}
// Iterate Scenario
if (m_FirstScenarioFrame)
{

framesSinceInitialization++;
if (isIterationComplete)
{
currentIteration++;
IncrementIteration();
OnIterationTeardown();
foreach (var randomizer in activeRandomizers)
randomizer.IterationEnd();
}
}

OnComplete();
foreach (var randomizer in activeRandomizers)
randomizer.ScenarioComplete();
if (quitOnComplete)
#if UNITY_EDITOR
UnityEditor.EditorApplication.isPlaying = false;
#else
Application.Quit();
#endif
m_WaitingForFinalUploads = true;
return;
}
// Perform new iteration tasks

foreach (var config in ParameterConfiguration.configurations)
config.ResetParameterStates(currentIteration);
foreach (var config in ParameterConfiguration.configurations)
config.ApplyParameters(currentIteration, ParameterApplicationFrequency.OnIterationSetup);
OnIterationSetup();
IterateParameterStates();
foreach (var randomizer in activeRandomizers)
randomizer.IterationStart();
foreach (var config in ParameterConfiguration.configurations)
config.ApplyParameters(framesSinceInitialization, ParameterApplicationFrequency.EveryFrame);
OnFrameStart();
foreach (var randomizer in activeRandomizers)
randomizer.Update();
}
/// <summary>
/// Finds and returns a randomizer attached to this scenario of the specified Randomizer type
/// </summary>
/// <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
{
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");
}
/// <summary>
/// Creates a new randomizer and adds it to this scenario
/// </summary>
/// <typeparam name="T">The type of randomizer to create</typeparam>
/// <returns>The newly created randomizer</returns>
public T CreateRandomizer<T>() where T : Randomizer, new()
{
return (T)CreateRandomizer(typeof(T));
}
internal Randomizer CreateRandomizer(Type randomizerType)
{
if (!randomizerType.IsSubclassOf(typeof(Randomizer)))
throw new ScenarioException(
$"Cannot add non-randomizer type {randomizerType.Name} to randomizer list");
foreach (var randomizer in m_Randomizers)
if (randomizer.GetType() == randomizerType)
throw new ScenarioException(
$"Two Randomizers of the same type ({randomizerType.Name}) cannot both be active simultaneously");
var newRandomizer = (Randomizer)Activator.CreateInstance(randomizerType);
m_Randomizers.Add(newRandomizer);
newRandomizer.Initialize(this, tagManager);
newRandomizer.Create();
return newRandomizer;
}
/// <summary>
/// Removes a randomizer of the specified type from this scenario
/// </summary>
/// <typeparam name="T">The type of scenario to remove</typeparam>
public void RemoveRandomizer<T>() where T : Randomizer, new()
{
RemoveRandomizer(typeof(T));
}
internal void RemoveRandomizer(Type randomizerType)
{
if (!randomizerType.IsSubclassOf(typeof(Randomizer)))
throw new ScenarioException(
$"Cannot add non-randomizer type {randomizerType.Name} to 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");
}
/// <summary>
/// Returns the execution order index of a randomizer of the given type
/// </summary>
/// <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()
{
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");
}
/// <summary>
/// Moves a randomizer from one index to another
/// </summary>
/// <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)
{
if (currentIndex == nextIndex)
return;
if (nextIndex > currentIndex)
nextIndex--;
var randomizer = m_Randomizers[currentIndex];
m_Randomizers.RemoveAt(currentIndex);
m_Randomizers.Insert(nextIndex, randomizer);
}
/// <summary>
/// Generates a random seed by hashing the current scenario iteration with a given base random seed
/// </summary>
/// <param name="baseSeed">Used to offset the seed generator</param>
/// <returns>The generated random seed</returns>
public uint GenerateRandomSeed(uint baseSeed = SamplerUtility.largePrime)
{
var seed = SamplerUtility.IterateSeed((uint)currentIteration, baseSeed);
return SamplerUtility.IterateSeed((uint)currentIteration, seed);
}
/// <summary>
/// Generates a random seed by hashing three values together: an arbitrary iteration value,
/// the current scenario iteration, and a base random seed
/// </summary>
/// <param name="iteration">An offset value hashed inside the seed generator</param>
/// <param name="baseSeed">An offset value hashed inside the seed generator</param>
/// <returns>The generated random seed</returns>
public uint GenerateIterativeRandomSeed(int iteration, uint baseSeed = SamplerUtility.largePrime)
{
return SamplerUtility.IterateSeed((uint)iteration, baseSeed);
}
void ValidateParameters()
{
foreach (var randomizer in m_Randomizers)
foreach (var parameter in randomizer.parameters)
parameter.Validate();
}
void IterateParameterStates()
{
foreach (var randomizer in m_Randomizers)
{
foreach (var parameter in randomizer.parameters)
{
parameter.ResetState();
parameter.IterateState(currentIteration);
}
}
}
}
}

12
com.unity.perception/Runtime/Randomization/Scenarios/ScenarioBase.cs.meta


fileFormatVersion: 2
fileFormatVersion: 2
timeCreated: 1589772146
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

10
com.unity.perception/Tests/Runtime/Randomization/ParameterTests/CategoricalParameterTests.cs


public void NegativeProbabilities()
{
var parameter = new StringParameter();
parameter.AddOption("option1", 1f);
parameter.AddOption("option2", -1f);
Assert.Throws<ParameterValidationException>(() => parameter.Validate());
var optionsArray = new [] { ("option1", 1f), ("option1", -1f) };
Assert.Throws<ParameterValidationException>(() => parameter.SetOptions(optionsArray));
}
[Test]

parameter.AddOption("option1", 0f);
parameter.AddOption("option2", 0f);
Assert.Throws<ParameterValidationException>(() => parameter.Validate());
var optionsArray = new [] { ("option1", 0f), ("option1", 0f) };
Assert.Throws<ParameterValidationException>(() => parameter.SetOptions(optionsArray));
}
}
}

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


using NUnit.Framework;
using UnityEngine;
using UnityEngine.Perception.GroundTruth;
using UnityEngine.Experimental.Perception.Randomization.Configuration;
using UnityEngine.Experimental.Perception.Randomization.Parameters;
using UnityEngine.Experimental.Perception.Randomization.Samplers;
using UnityEngine.Experimental.Perception.Randomization.Scenarios;
using UnityEngine.TestTools;
using Object = UnityEngine.Object;

var constants = new FixedLengthScenario.Constants
{
framesPerIteration = 2,
startingIteration = 2,
totalIterations = 2
};

startingIteration = 0,
totalIterations = 0
};

// Check if the values reverted correctly
Assert.AreEqual(m_Scenario.constants.framesPerIteration, constants.framesPerIteration);
Assert.AreEqual(m_Scenario.constants.startingIteration, constants.startingIteration);
Assert.AreEqual(m_Scenario.constants.totalIterations, constants.totalIterations);
// Clean up serialized constants

yield return null;
}
Assert.True(m_Scenario.isScenarioComplete);
}
[UnityTest]
public IEnumerator AppliesParametersEveryFrame()
{
var config = m_TestObject.AddComponent<ParameterConfiguration>();
var parameter = config.AddParameter<Vector3Parameter>();
parameter.x = new UniformSampler(1, 2);
parameter.y = new UniformSampler(1, 2);
parameter.z = new UniformSampler(1, 2);
parameter.target.AssignNewTarget(
m_TestObject, m_TestObject.transform, "position", ParameterApplicationFrequency.EveryFrame);
var initialPosition = Vector3.zero;
yield return CreateNewScenario(1, 5);
// ReSharper disable once Unity.InefficientPropertyAccess
Assert.AreNotEqual(initialPosition, m_TestObject.transform.position);
// ReSharper disable once Unity.InefficientPropertyAccess
initialPosition = m_TestObject.transform.position;
yield return null;
// ReSharper disable once Unity.InefficientPropertyAccess
Assert.AreNotEqual(initialPosition, m_TestObject.transform.position);
}
[UnityTest]
public IEnumerator AppliesParametersEveryIteration()
{
var config = m_TestObject.AddComponent<ParameterConfiguration>();
var parameter = config.AddParameter<Vector3Parameter>();
parameter.x = new UniformSampler(1, 2);
parameter.y = new UniformSampler(1, 2);
parameter.z = new UniformSampler(1, 2);
var transform = m_TestObject.transform;
var prevPosition = new Vector3();
transform.position = prevPosition;
parameter.target.AssignNewTarget(
m_TestObject, transform, "position", ParameterApplicationFrequency.OnIterationSetup);
yield return CreateNewScenario(2, 2);
Assert.AreNotEqual(prevPosition, transform.position);
// ReSharper disable once Unity.InefficientPropertyAccess
prevPosition = transform.position;
yield return null;
// ReSharper disable once Unity.InefficientPropertyAccess
Assert.AreEqual(prevPosition, transform.position);
// ReSharper disable once Unity.InefficientPropertyAccess
prevPosition = transform.position;
yield return null;
// ReSharper disable once Unity.InefficientPropertyAccess
Assert.AreNotEqual(prevPosition, transform.position);
}
[UnityTest]

1
com.unity.perception/package.json


"com.unity.nuget.newtonsoft-json": "1.1.2",
"com.unity.render-pipelines.core": "7.1.6",
"com.unity.entities": "0.8.0-preview.8",
"com.unity.simulation.client": "0.0.10-preview.9",
"com.unity.simulation.capture": "0.0.10-preview.13",
"com.unity.simulation.core": "0.0.10-preview.19"
},

2
com.unity.perception/Editor/Randomization/Uxml/Sampler/FloatRangeElement.uxml


<VisualElement>
<VisualElement style="flex-direction: row; margin: 1px, 1px, 1px, 3px;">
<Label text="Range" class="unity-base-field__label" style="min-width: 147;"/>
<VisualElement class="float-range" style="flex-grow: 1; flex-direction: row;">
<VisualElement class="sampler__float-range" style="flex-grow: 1; flex-direction: row;">
<editor:FloatField label="Min" tooltip="Minimum" name="minimum" style="flex-grow: 1; flex-shrink: 0; flex-basis: 0; margin-bottom: 0;"/>
<editor:FloatField label="Max" tooltip="Maximum" name="maximum" style="flex-grow: 1; flex-shrink: 0; flex-basis: 0; margin-right: 2px; margin-bottom: 0;"/>
</VisualElement>

2
com.unity.perception/Editor/Randomization/VisualElements/Randomizer/DragToReorderManipulator.cs.meta


fileFormatVersion: 2
guid: dd62abede5784c84f90495b367408ced
guid: f2b59fa8baf440f597257d8eb8219afa
MonoImporter:
externalObjects: {}
serializedVersion: 2

2
com.unity.perception/Editor/Randomization/VisualElements/Parameter/CategoricalOptionElement.cs


m_ProbabilitiesProperty = probabilitiesProperty;
var template = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(
$"{StaticData.uxmlDir}/CategoricalOptionElement.uxml");
$"{StaticData.uxmlDir}/Parameter/CategoricalOptionElement.uxml");
template.CloneTree(this);
}

3
com.unity.perception/Editor/Randomization/VisualElements/Sampler/FloatRangeElement.cs


{
public FloatRangeElement(SerializedProperty property)
{
var template = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>($"{StaticData.uxmlDir}/FloatRangeElement.uxml");
var template = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(
$"{StaticData.uxmlDir}/Sampler/FloatRangeElement.uxml");
template.CloneTree(this);
var minimumField = this.Q<FloatField>("minimum");

35
com.unity.perception/Editor/Randomization/VisualElements/Randomizer/DragToReorderManipulator.cs


using UnityEngine.UIElements;
using UnityEngine.Experimental.Perception.Randomization.VisualElements;
using UnityEngine.UIElements;
class ParameterDragManipulator : MouseManipulator
class DragToReorderManipulator : MouseManipulator
ParameterElement m_ParameterElement;
RandomizerElement m_RandomizerElement;
VisualElement m_DragBar;
VisualElement m_ReorderingIndicator;
m_DragHandle = target.Q<VisualElement>("drag-handle");
m_ParameterElement = (ParameterElement)target;
m_RandomizerElement = (RandomizerElement)target;
m_DragHandle = m_RandomizerElement.Q<VisualElement>("drag-handle");
m_DragHandle.RegisterCallback<MouseDownEvent>(OnMouseDown);
m_DragHandle.RegisterCallback<MouseMoveEvent>(OnMouseMove);
m_DragHandle.RegisterCallback<MouseUpEvent>(OnMouseUp);

return;
}
if (m_ParameterElement.ConfigEditor.FilterString != string.Empty)
return;
m_DragBar = new ParameterDragBar();
m_DragBar.style.width = new StyleLength(m_ParameterContainer.resolvedStyle.width);
target.parent.Add(m_DragBar);
m_ReorderingIndicator = new RandomizerReorderingIndicator();
m_ReorderingIndicator.style.width = new StyleLength(m_ParameterContainer.resolvedStyle.width);
target.parent.Add(m_ReorderingIndicator);
m_DragBar.style.top = evt.localMousePosition.y + m_Offset;
m_ReorderingIndicator.style.top = evt.localMousePosition.y + m_Offset;
m_Active = true;
m_DragHandle.CaptureMouse();

if (!m_Active || !m_DragHandle.HasMouseCapture())
return;
m_DragBar.style.top = evt.localMousePosition.y + m_Offset;
m_ReorderingIndicator.style.top = evt.localMousePosition.y + m_Offset;
evt.StopPropagation();
}

return;
var dragBarY = evt.localMousePosition.y + m_Offset;
m_DragBar.RemoveFromHierarchy();
m_ReorderingIndicator.RemoveFromHierarchy();
m_Active = false;
m_DragHandle.ReleaseMouse();

middlePoints[p++] = middleHeight + localY;
}
var randomizerIndex = m_RandomizerElement.parent.IndexOf(m_RandomizerElement);
ReorderParameter(m_ParameterElement.ParameterIndex, i);
ReorderParameter(randomizerIndex, i);
ReorderParameter(m_ParameterElement.ParameterIndex, middlePoints.Length);
ReorderParameter(randomizerIndex, middlePoints.Length);
m_ParameterElement.ConfigEditor.ReorderParameter(currentIndex, nextIndex);
m_RandomizerElement.randomizerList.ReorderRandomizer(currentIndex, nextIndex);
}
}
}

5
com.unity.perception/Editor/Randomization/VisualElements/Sampler/SamplerElement.cs


public SamplerElement(SerializedProperty property, Parameter parameter)
{
m_Property = property;
var template = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>($"{StaticData.uxmlDir}/SamplerElement.uxml");
var template = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(
$"{StaticData.uxmlDir}/Sampler/SamplerElement.uxml");
template.CloneTree(this);
m_Parameter = parameter;

m_Properties.Clear();
var currentProperty = m_Property.Copy();
var nextSiblingProperty = m_Property.Copy();
nextSiblingProperty.Next(false);
nextSiblingProperty.NextVisible(false);
if (currentProperty.NextVisible(true))
{

22
com.unity.perception/Runtime/Randomization/Parameters/ParameterTypes/NumericParameters/BooleanParameter.cs


using System;
using System.Collections.Generic;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;

[HideInInspector, SerializeReference] public ISampler value = new UniformSampler(0f, 1f);
/// <summary>
/// Returns the sampler employed by this parameter
/// A threshold value that transforms random values within the range [0, 1] to boolean values.
/// Values greater than the threshold are true, and values less than the threshold are false.
public override ISampler[] samplers => new[] { value };
[Range(0, 1)] public float threshold = 0.5f;
static bool Sample(float t) => t >= 0.5f;
/// <summary>
/// Returns an IEnumerable that iterates over each sampler field in this parameter
/// </summary>
internal override IEnumerable<ISampler> samplers
{
get { yield return value; }
}
bool Sample(float t) => t >= threshold;
/// <summary>
/// Generates a boolean sample

jobHandle = new SamplesJob
{
rngSamples = rngSamples,
samples = samples
samples = samples,
threshold = threshold
}.Schedule(jobHandle);
return samples;
}

{
[DeallocateOnJobCompletion] public NativeArray<float> rngSamples;
public NativeArray<bool> samples;
public float threshold;
samples[i] = Sample(rngSamples[i]);
samples[i] = rngSamples[i] >= threshold;
}
}
}

8
com.unity.perception/Runtime/Randomization/Parameters/ParameterTypes/NumericParameters/FloatParameter.cs


using System;
using System.Collections.Generic;
using Unity.Collections;
using Unity.Jobs;
using UnityEngine.Experimental.Perception.Randomization.Samplers;

[SerializeReference] public ISampler value = new UniformSampler(0f, 1f);
/// <summary>
/// Returns the sampler employed by this parameter
/// Returns an IEnumerable that iterates over each sampler field in this parameter
public override ISampler[] samplers => new []{ value };
internal override IEnumerable<ISampler> samplers
{
get { yield return value; }
}
/// <summary>
/// Generates a float sample

8
com.unity.perception/Runtime/Randomization/Parameters/ParameterTypes/NumericParameters/IntegerParameter.cs


using System;
using System.Collections.Generic;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;

[SerializeReference] public ISampler value = new UniformSampler(0f, 1f);
/// <summary>
/// Returns the sampler employed by this parameter
/// Returns an IEnumerable that iterates over each sampler field in this parameter
public override ISampler[] samplers => new[] { value };
internal override IEnumerable<ISampler> samplers
{
get { yield return value; }
}
/// <summary>
/// Generates an integer sample

12
com.unity.perception/Runtime/Randomization/Parameters/ParameterTypes/NumericParameters/Vector2Parameter.cs


using System;
using System.Collections.Generic;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;

[SerializeReference] public ISampler y = new UniformSampler(0f, 1f);
/// <summary>
/// Returns the samplers employed by this parameter
/// Returns an IEnumerable that iterates over each sampler field in this parameter
public override ISampler[] samplers => new []{ x, y };
internal override IEnumerable<ISampler> samplers
{
get
{
yield return x;
yield return y;
}
}
/// <summary>
/// Generates a Vector2 sample

13
com.unity.perception/Runtime/Randomization/Parameters/ParameterTypes/NumericParameters/Vector3Parameter.cs


using System;
using System.Collections.Generic;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;

[SerializeReference] public ISampler z = new UniformSampler(0f, 1f);
/// <summary>
/// Returns the samplers employed by this parameter
/// Returns an IEnumerable that iterates over each sampler field in this parameter
public override ISampler[] samplers => new []{ x, y, z };
internal override IEnumerable<ISampler> samplers
{
get
{
yield return x;
yield return y;
yield return z;
}
}
/// <summary>
/// Generates a Vector3 sample

14
com.unity.perception/Runtime/Randomization/Parameters/ParameterTypes/NumericParameters/Vector4Parameter.cs


using System;
using System.Collections.Generic;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;

[SerializeReference] public ISampler w = new UniformSampler(0f, 1f);
/// <summary>
/// The sampler used the samplers employed by this parameter
/// Returns an IEnumerable that iterates over each sampler field in this parameter
public override ISampler[] samplers => new []{ x, y, z, w };
internal override IEnumerable<ISampler> samplers
{
get
{
yield return x;
yield return y;
yield return z;
yield return w;
}
}
/// <summary>
/// Generates a Vector4 sample

3
com.unity.perception.meta


fileFormatVersion: 2
guid: 2c0b9128211c487091d21926e24c3303
timeCreated: 1600211324

29
com.unity.perception/Documentation~/Randomization/RandomizerTags.md


# Randomizer Tags
RandomizerTags are the primary mechanism by which randomizers query for a certain subset of GameObjects to randomize within a simulation.
More specifically, RandomizerTags are components that can be added to GameObjects to register them with the active scenario's TagManager. This TagManager is aware of all objects with tags in the scene and can be queried to find all GameObjects that contain a specific tag. Below is a simple example of a ColorRandomizer querying for all GameObjects with a ColorRandomizerTag that it will apply a random material base color to:
```
[Serializable]
[AddRandomizerMenu("Perception/Color Randomizer")]
public class ColorRandomizer : Randomizer
{
static readonly int k_BaseColor = Shader.PropertyToID("_BaseColor");
public ColorHsvaParameter colorParameter;
protected override void OnIterationStart()
{
var taggedObjects = tagManager.Query<ColorRandomizerTag>();
foreach (var taggedObject in taggedObjects)
{
var renderer = taggedObject.GetComponent<MeshRenderer>();
renderer.material.SetColor(k_BaseColor, colorParameter.Sample());
}
}
}
```
RandomizerTags can also be used to customize how randomizers apply their randomizations to a particular GameObject. Visit [part two of the randomization tutorial]() to explore an in depth example of implementing a LightRandomizer that does exactly this.

42
com.unity.perception/Documentation~/Randomization/Randomizers.md


# Randomizers
Randomizers encapsulate specific randomization activities to perform during the execution of a randomized simulation. For example, randomizers exist for spawning objects, repositioning lights, varying the color of objects, etc. Randomizers expose random parameters to their inspector interface to further customize these variations. Users can add a set of randomizers to a scenario in order to define an ordered list randomization activities to perform during the lifecycle of a simulation.
To define an entirely new randomizer, derive the Randomizer class and implement one or more of the methods listed in the section below to randomize GameObjects during the runtime of a simulation.
## Randomizer Hooks
1. OnCreate() - called when the Randomizer is added or loaded to a scenario
2. OnIterationStart() - called at the start of a new scenario iteration
3. OnIterationEnd() - called the after a scenario iteration has completed
4. OnScenarioComplete() - called the after the entire scenario has completed
5. OnStartRunning() - called on the first frame a Randomizer is enabled
6. OnStopRunning() - called on the first frame a disabled Randomizer is updated
7. OnUpdate() - executed every frame for enabled Randomizers
## Randomizer Coding Example
Below is the code for the sample rotation randomizer included with the perception package:
```
[Serializable]
[AddRandomizerMenu("Perception/Rotation Randomizer")]
public class RotationRandomizer : Randomizer
{
public Vector3Parameter rotation = new Vector3Parameter();
protected override void OnIterationStart()
{
var taggedObjects = tagManager.Query<RotationRandomizerTag>();
foreach (var taggedObject in taggedObjects)
taggedObject.transform.rotation = Quaternion.Euler(rotation.Sample());
}
}
```
There are a few key things to note from this example:
1. Make sure to add the [Serializable] tag to all randomizer implementations to ensure that the randomizer can be customized and saved within the Unity Editor.
2. The [AddRandomizerMenu] attribute customizes the "Add Randomizer" sub menu path in the scenario inspector for a particular randomizer. In this example, the RotationRandomizer can be added to a scenario by opening the add randomizer menu and clicking `Perception -> Rotation Randomizer`.
3. The line `var taggedObjects = tagManager.Query<RotationRandomizerTag>();` uses RandomizerTags in combination with the current Scenario's tagManager to query for all objects with RotationRandomizerTags to obtain the subset of GameObjects within the simulation that need to have their rotations randomzied. To learn more about how RandomizerTags work, visit the [RandomizerTags doc](RandomizerTags.md).

8
com.unity.perception/Editor/Randomization/Editors.meta


fileFormatVersion: 2
<<<<<<< HEAD
guid: 85401640505a48f9a8fe55045f5f28d8
timeCreated: 1600754567
=======
guid: 0b17046409af4c22bf74eec2a5965984
timeCreated: 1598135707
>>>>>>> 86d25d2... implemented parameter behaviours

5
com.unity.perception/Editor/Randomization/Icons/ChevronLeft.png

之前 之后
宽度: 32  |  高度: 32  |  大小: 2.0 KiB

144
com.unity.perception/Editor/Randomization/Icons/ChevronLeft.png.meta


fileFormatVersion: 2
guid: 8ccf58823c99c574f9cfb36e2e051078
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: 2
mipBias: -100
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: iPhone
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Windows Store Apps
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

6
com.unity.perception/Editor/Randomization/Icons/ChevronRight.png

之前 之后
宽度: 32  |  高度: 32  |  大小: 2.0 KiB

144
com.unity.perception/Editor/Randomization/Icons/ChevronRight.png.meta


fileFormatVersion: 2
guid: 799cb8d9484d2ea4a9d4ddd15bda85aa
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: 2
mipBias: -100
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: iPhone
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Windows Store Apps
maxTextureSize: 8192
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

8
com.unity.perception/Editor/Randomization/PropertyDrawers.meta


fileFormatVersion: 2
<<<<<<< HEAD
guid: 47a26876f92a4b19adb3b7b525efa830
timeCreated: 1600754588
=======
guid: d3107e026b2943c1868c9b3f8c6480d3
timeCreated: 1598135730
>>>>>>> 86d25d2... implemented parameter behaviours

3
com.unity.perception/Editor/Randomization/Uxml/Parameter.meta


fileFormatVersion: 2
guid: 9dff455d50ac4549bac301eb5926c83c
timeCreated: 1601668963

3
com.unity.perception/Editor/Randomization/Uxml/Randomizer.meta


fileFormatVersion: 2
guid: 006b3ab9c14b41f581f235810765469c
timeCreated: 1600991712

14
com.unity.perception/Editor/Randomization/Uxml/RunInUSimWindow.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="USim worker config" 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>
</UXML>

10
com.unity.perception/Editor/Randomization/Uxml/RunInUSimWindow.uxml.meta


fileFormatVersion: 2
guid: 678c5d944639402c9c4d50de04c77561
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}

3
com.unity.perception/Editor/Randomization/Uxml/Sampler.meta


fileFormatVersion: 2
guid: cdf29c5fa139470d961ca4605d792f0a
timeCreated: 1601668985

8
com.unity.perception/Editor/Randomization/VisualElements.meta


fileFormatVersion: 2
<<<<<<< HEAD
guid: 64930d74a0b54875ab472b72066417bc
timeCreated: 1600754567
=======
guid: 7f8f95a1bb144a96b9310164f5560387
timeCreated: 1598135666
>>>>>>> 86d25d2... implemented parameter behaviours

3
com.unity.perception/Runtime/Randomization/Parameters/ParameterTypes/CategorialParameters.meta


fileFormatVersion: 2
guid: e09548ac8cf147b6b8ce910112ae97c4
timeCreated: 1600890464

3
com.unity.perception/Runtime/Randomization/Parameters/ParameterTypes/NumericParameters.meta


fileFormatVersion: 2
guid: db7c97a0f5014b73a7af47aa6f6a276a
timeCreated: 1600890440

8
com.unity.perception/Runtime/Randomization/Randomizers.meta


fileFormatVersion: 2
<<<<<<< HEAD
guid: 80645bae9cd440ca81c7a1e03572e7da
timeCreated: 1600754588
=======
guid: ae6aad06c0e14f67aa7a9ad9004a1828
timeCreated: 1600274594
>>>>>>> c653d18... Implemented randomizer class. Ran into SerializeReference issue 1193322.

65
com.unity.perception/Runtime/Randomization/Scenarios/USimScenario.cs


using System;
namespace UnityEngine.Experimental.Perception.Randomization.Scenarios
{
/// <summary>
/// Defines a scenario that is compatible with the Run in USim window
/// </summary>
/// <typeparam name="T">The type of constants to serialize</typeparam>
public abstract class USimScenario<T> : Scenario<T> where T : USimConstants, new()
{
/// <summary>
/// Returns whether the entire scenario has completed
/// </summary>
public sealed override bool isScenarioComplete => currentIteration >= constants.totalIterations;
/// <summary>
/// OnAwake is executed directly after this scenario has been registered and initialized
/// </summary>
protected sealed override void OnAwake()
{
currentIteration = constants.instanceIndex;
}
/// <summary>
/// Progresses the current scenario iteration
/// </summary>
protected sealed override void IncrementIteration()
{
currentIteration += constants.instanceCount;
}
/// <summary>
/// Deserializes this scenario's constants from the USim AppParams Json file
/// </summary>
public sealed override void Deserialize()
{
if (string.IsNullOrEmpty(Unity.Simulation.Configuration.Instance.SimulationConfig.app_param_uri))
base.Deserialize();
else
constants = Unity.Simulation.Configuration.Instance.GetAppParams<T>();
}
}
/// <summary>
/// A class encapsulating the scenario constants fields required for USim cloud execution
/// </summary>
[Serializable]
public class USimConstants
{
/// <summary>
/// The total number of iterations to run a scenario for
/// </summary>
public int totalIterations = 100;
/// <summary>
/// The number of USim instances assigned to executed this scenario
/// </summary>
public int instanceCount = 1;
/// <summary>
/// The USim instance index of the currently executing worker
/// </summary>
public int instanceIndex;
}
}

3
com.unity.perception/Runtime/Randomization/Scenarios/USimScenario.cs.meta


fileFormatVersion: 2
guid: ee12a68b9b7b40aa9f60c97d6078a7cf
timeCreated: 1601067341

8
com.unity.perception/Tests/Runtime/Randomization/RandomizerTests.meta


fileFormatVersion: 2
<<<<<<< HEAD
guid: 6693ffffff2148b0a248e1d8c5ddc805
timeCreated: 1600754588
=======
guid: f9e02c502b7845229d26d377a0d871f1
timeCreated: 1600744200
>>>>>>> cb407fb... added randomizer tests

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


using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine.Experimental.Perception.Randomization.Scenarios;
using UnityEngine.Experimental.Perception.Randomization.VisualElements;
using UnityEngine.UIElements;
namespace UnityEngine.Experimental.Perception.Randomization.Editor
{
[CustomEditor(typeof(ScenarioBase), true)]
class ScenarioBaseEditor : UnityEditor.Editor
{
ScenarioBase m_Scenario;
SerializedObject m_SerializedObject;
VisualElement m_Root;
VisualElement m_InspectorPropertiesContainer;
VisualElement m_ConstantsContainer;
VisualElement m_RandomizerListPlaceholder;
SerializedProperty m_ConstantsProperty;
public override VisualElement CreateInspectorGUI()
{
m_Scenario = (ScenarioBase)target;
m_SerializedObject = new SerializedObject(m_Scenario);
m_Root = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(
$"{StaticData.uxmlDir}/ScenarioBaseElement.uxml").CloneTree();
var serializeConstantsButton = m_Root.Q<Button>("serialize-constants");
serializeConstantsButton.clicked += () => m_Scenario.Serialize();
var deserializeConstantsButton = m_Root.Q<Button>("deserialize-constants");
deserializeConstantsButton.clicked += () => m_Scenario.Deserialize();
m_RandomizerListPlaceholder = m_Root.Q<VisualElement>("randomizer-list-placeholder");
CreatePropertyFields();
CheckIfConstantsExist();
return m_Root;
}
void CreatePropertyFields()
{
m_InspectorPropertiesContainer = m_Root.Q<VisualElement>("inspector-properties");
m_InspectorPropertiesContainer.Clear();
var iterator = m_SerializedObject.GetIterator();
var foundProperties = false;
if (iterator.NextVisible(true))
{
do
{
switch (iterator.name)
{
case "m_Script":
break;
case "constants":
m_ConstantsProperty = iterator.Copy();
break;
case "m_Randomizers":
m_RandomizerListPlaceholder.Add(new RandomizerList(iterator.Copy()));
break;
default:
{
foundProperties = true;
var propertyField = new PropertyField(iterator.Copy());
propertyField.Bind(m_SerializedObject);
m_InspectorPropertiesContainer.Add(propertyField);
break;
}
}
} while (iterator.NextVisible(false));
}
if (!foundProperties)
m_InspectorPropertiesContainer.style.display = new StyleEnum<DisplayStyle>(DisplayStyle.None);
}
void CheckIfConstantsExist()
{
m_ConstantsContainer = m_Root.Q<VisualElement>("constants-container");
if (m_ConstantsProperty == null)
{
m_InspectorPropertiesContainer.style.marginBottom = 0;
m_ConstantsContainer.style.display = new StyleEnum<DisplayStyle>(DisplayStyle.None);
}
}
}
}

8
com.unity.perception/Editor/Randomization/Editors/ScenarioBaseEditor.cs.meta


fileFormatVersion: 2
<<<<<<< HEAD
guid: 57a29c3831024d55aca1a6267dabb56c
timeCreated: 1600754583
=======
guid: face5e97e23d402cbf6fafadb39fa0c3
timeCreated: 1596213301
>>>>>>> 86d25d2... implemented parameter behaviours

11
com.unity.perception/Editor/Randomization/Editors/RunInUSimWindow.cs.meta


fileFormatVersion: 2
guid: 2744f10e153915b46a2f6a274914753d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

247
com.unity.perception/Editor/Randomization/Editors/RunInUSimWindow.cs


using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Unity.Simulation.Client;
using UnityEditor;
using UnityEditor.Build.Reporting;
using UnityEditor.SceneManagement;
using UnityEditor.UIElements;
using UnityEngine.Experimental.Perception.Randomization.Editor;
using UnityEngine.Experimental.Perception.Randomization.Scenarios;
using UnityEngine.SceneManagement;
using UnityEngine.UIElements;
using ZipUtility;
namespace UnityEngine.Perception.Randomization.Editor
{
class RunInUSimWindow : EditorWindow
{
string m_BuildDirectory;
string m_BuildZipPath;
SysParamDefinition m_SysParam;
TextField m_RunNameField;
IntegerField m_TotalIterationsField;
IntegerField m_InstanceCountField;
ObjectField m_MainSceneField;
ObjectField m_ScenarioField;
Button m_RunButton;
[MenuItem("Window/Run in USim")]
static void ShowWindow()
{
var window = GetWindow<RunInUSimWindow>();
window.titleContent = new GUIContent("Run In Unity Simulation");
window.minSize = new Vector2(250, 50);
window.Show();
}
void OnEnable()
{
m_BuildDirectory = Application.dataPath + "/../Build";
Project.Activate();
Project.clientReadyStateChanged += CreateEstablishingConnectionUI;
CreateEstablishingConnectionUI(Project.projectIdState);
}
void OnFocus()
{
Application.runInBackground = true;
}
void OnLostFocus()
{
Application.runInBackground = false;
}
void CreateEstablishingConnectionUI(Project.State state)
{
rootVisualElement.Clear();
if (Project.projectIdState == Project.State.Pending)
{
var waitingText = new TextElement();
waitingText.text = "Waiting for connection to Unity Cloud...";
rootVisualElement.Add(waitingText);
}
else if (Project.projectIdState == Project.State.Invalid)
{
var waitingText = new TextElement();
waitingText.text = "The current project must be associated with a valid Unity Cloud project " +
"to run in Unity Simulation";
rootVisualElement.Add(waitingText);
}
else
{
CreateRunInUSimUI();
}
}
/// <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 = $"RunInUSim_{element.name}";
}
void CreateRunInUSimUI()
{
var root = rootVisualElement;
AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(
$"{StaticData.uxmlDir}/RunInUSimWindow.uxml").CloneTree(root);
m_RunNameField = root.Q<TextField>("run-name");
SetViewDataKey(m_RunNameField);
m_TotalIterationsField = root.Q<IntegerField>("total-iterations");
SetViewDataKey(m_TotalIterationsField);
m_InstanceCountField = root.Q<IntegerField>("instance-count");
SetViewDataKey(m_InstanceCountField);
m_MainSceneField = root.Q<ObjectField>("main-scene");
m_MainSceneField.objectType = typeof(SceneAsset);
if (SceneManager.sceneCount > 0)
{
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,
action =>
{
m_SysParam = definition;
sysParamMenu.text = definition.description;
});
}
sysParamMenu.text = sysParamDefinitions[0].description;
m_SysParam = sysParamDefinitions[0];
m_RunButton = root.Q<Button>("run-button");
m_RunButton.clicked += RunInUSim;
}
async void RunInUSim()
{
ValidateSettings();
CreateLinuxBuildAndZip();
await StartUSimRun();
}
void ValidateSettings()
{
if (string.IsNullOrEmpty(m_RunNameField.value))
throw new MissingFieldException("Empty run name");
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(USimScenario<>), scenario.GetType()))
throw new NotSupportedException("Scenario class must be derived from USimScenario to run in USim");
}
void CreateLinuxBuildAndZip()
{
// Create build directory
var projectBuildDirectory = $"{m_BuildDirectory}/{m_RunNameField.value}";
if (!Directory.Exists(projectBuildDirectory))
Directory.CreateDirectory(projectBuildDirectory);
// Create Linux build
Debug.Log("Creating Linux build...");
var buildPlayerOptions = new BuildPlayerOptions
{
scenes = new[] { AssetDatabase.GetAssetPath(m_MainSceneField.value) },
locationPathName = Path.Combine(projectBuildDirectory, $"{m_RunNameField.value}.x86_64"),
target = BuildTarget.StandaloneLinux64
};
var report = BuildPipeline.BuildPlayer(buildPlayerOptions);
var summary = report.summary;
if (summary.result != BuildResult.Succeeded)
throw new Exception($"Build did not succeed: status = {summary.result}");
Debug.Log("Created Linux build");
// Zip the build
Debug.Log("Starting to zip...");
Zip.DirectoryContents(projectBuildDirectory, m_RunNameField.value);
m_BuildZipPath = projectBuildDirectory + ".zip";
Debug.Log("Created build zip");
}
List<AppParam> GenerateAppParamIds(CancellationToken token)
{
var appParamIds = new List<AppParam>();
for (var i = 0; i < m_InstanceCountField.value; i++)
{
if (token.IsCancellationRequested)
return null;
var appParamName = $"{m_RunNameField.value}_{i}";
var appParamId = API.UploadAppParam(appParamName, new USimConstants
{
totalIterations = m_TotalIterationsField.value,
instanceCount = m_InstanceCountField.value,
instanceIndex = i
});
appParamIds.Add(new AppParam()
{
id = appParamId,
name = appParamName,
num_instances = 1
});
}
return appParamIds;
}
async Task StartUSimRun()
{
m_RunButton.SetEnabled(false);
var cancellationTokenSource = new CancellationTokenSource();
var token = cancellationTokenSource.Token;
Debug.Log("Uploading build...");
var buildId = await API.UploadBuildAsync(
m_RunNameField.value,
m_BuildZipPath,
cancellationTokenSource: cancellationTokenSource);
Debug.Log($"Build upload complete: build id {buildId}");
var appParams = GenerateAppParamIds(token);
if (token.IsCancellationRequested)
{
Debug.Log("Run cancelled");
return;
}
Debug.Log($"Generated app-param ids: {appParams.Count}");
var runDefinitionId = API.UploadRunDefinition(new RunDefinition
{
app_params = appParams.ToArray(),
name = m_RunNameField.value,
sys_param_id = m_SysParam.id,
build_id = buildId
});
Debug.Log($"Run definition upload complete: run definition id {runDefinitionId}");
var run = Run.CreateFromDefinitionId(runDefinitionId);
run.Execute();
cancellationTokenSource.Dispose();
Debug.Log($"Executing run: {run.executionId}");
m_RunButton.SetEnabled(true);
}
}
}

25
com.unity.perception/Editor/Randomization/PropertyDrawers/ColorHsvaDrawer.cs


using UnityEditor;
using UnityEngine.Experimental.Perception.Randomization.Parameters;
using UnityEngine.UIElements;
namespace UnityEngine.Perception.Randomization.Editor.PropertyDrawers
{
[CustomPropertyDrawer(typeof(ColorHsva), true)]
class ColorHsvaDrawer : PropertyDrawer
{
public override VisualElement CreatePropertyGUI(SerializedProperty property)
{
return new ColorHsvaField(property);
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
EditorGUI.PropertyField(position, property, label, true);
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return EditorGUI.GetPropertyHeight(property);
}
}
}

8
com.unity.perception/Editor/Randomization/PropertyDrawers/ColorHsvaDrawer.cs.meta


fileFormatVersion: 2
<<<<<<< HEAD
guid: e2ea91ddb1134cc6a14d2e3859a275f5
timeCreated: 1600754588
=======
guid: 5e8094c28dd142a09fbbd38ca560164b
timeCreated: 1598250942
>>>>>>> 86d25d2... implemented parameter behaviours

26
com.unity.perception/Editor/Randomization/PropertyDrawers/ParameterDrawer.cs


using System;
using UnityEditor;
using UnityEngine.Experimental.Perception.Randomization.Parameters;
using UnityEngine.UIElements;
namespace UnityEngine.Perception.Randomization.Editor.PropertyDrawers
{
[CustomPropertyDrawer(typeof(Parameter), true)]
class ParameterDrawer : PropertyDrawer
{
public override VisualElement CreatePropertyGUI(SerializedProperty property)
{
return new DrawerParameterElement(property);
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
EditorGUI.PropertyField(position, property, label, true);
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return EditorGUI.GetPropertyHeight(property);
}
}
}

8
com.unity.perception/Editor/Randomization/PropertyDrawers/ParameterDrawer.cs.meta


fileFormatVersion: 2
<<<<<<< HEAD
guid: e396dea59a4843529416020c3d0ef5af
timeCreated: 1600754588
=======
guid: d389620d3aa3471ca1877eb59cdfb465
timeCreated: 1598135745
>>>>>>> 86d25d2... implemented parameter behaviours

8
com.unity.perception/Editor/Randomization/Uxml/Parameter/CategoricalOptionElement.uxml


<UXML xmlns="UnityEngine.UIElements" xmlns:editor="UnityEditor.UIElements">
<VisualElement class="parameter__categorical-option">
<Button name="remove" class="randomization__remove-item-button"/>
<Label name="index-label" text="[0]" style="min-width: 50px;"/>
<editor:PropertyField name="option" class="parameter__categorical-option-property-field"/>
<editor:FloatField label="p:" name="probability" tooltip="Probability" class="randomization__collapsible-container" style="min-width: 40px; flex-grow: 0.15;"/>
</VisualElement>
</UXML>

12
com.unity.perception/Editor/Randomization/Uxml/Parameter/CategoricalParameterTemplate.uxml


<UXML xmlns="UnityEngine.UIElements" xmlns:editor="UnityEditor.UIElements">
<Toggle label="Uniform" name="uniform"/>
<editor:IntegerField label="Seed" name="seed"/>
<VisualElement class="parameter__categorical-options-list">
<ListView name="options"/>
</VisualElement>
<VisualElement style="flex-direction: row; justify-content: flex-end;">
<Button name="add-option" text="Add Option" class="parameter__categorical-options-list-button"/>
<Button name="add-folder" text="Add Folder" class="parameter__categorical-options-list-button"/>
<Button name="clear-options" text="Clear Options" class="parameter__categorical-options-list-button"/>
</VisualElement>
</UXML>

10
com.unity.perception/Editor/Randomization/Uxml/Parameter/ParameterDrawer.uxml


<UXML xmlns="UnityEngine.UIElements">
<VisualElement>
<Style src="../../Uss/Styles.uss"/>
<VisualElement style="flex-direction: row; align-items: center;">
<VisualElement name="collapse" class="randomization__collapse-toggle foldout-open"/>
<Label name="field-name" style="font-size: 12px;"/>
</VisualElement>
<VisualElement name="drawer" class="randomization__collapsible-container" style="padding-left: 18px;"/>
</VisualElement>
</UXML>

8
com.unity.perception/Editor/Randomization/Uxml/Parameter/ParameterDrawer.uxml.meta


fileFormatVersion: 2
<<<<<<< HEAD
guid: fbfd6bfda79247c1a4578907f9df9d6c
timeCreated: 1600754588
=======
guid: 6a4bb3efae29429292ccdfa63e661872
timeCreated: 1598240583
>>>>>>> 86d25d2... implemented parameter behaviours

6
com.unity.perception/Editor/Randomization/Uxml/Parameter/ParameterElement.uxml


<UXML xmlns="UnityEngine.UIElements">
<VisualElement>
<Style src="../../Uss/Styles.uss"/>
<VisualElement name="properties"/>
</VisualElement>
</UXML>

3
com.unity.perception/Editor/Randomization/Uxml/Randomizer/AddRandomizerMenu.uxml.meta


fileFormatVersion: 2
guid: 158d437640f24d70822be92bba46591b
timeCreated: 1600837112

3
com.unity.perception/Editor/Randomization/Uxml/Randomizer/MenuDirectoryElement.uxml.meta


fileFormatVersion: 2
guid: f792ce75baa443b1bc1c6e4e19f79b5b
timeCreated: 1600991861

3
com.unity.perception/Editor/Randomization/Uxml/Randomizer/RandomizerElement.uxml.meta


fileFormatVersion: 2
guid: f68d0e4ae9a94881adad2ff4835cddaf
timeCreated: 1600290113

3
com.unity.perception/Editor/Randomization/Uxml/Randomizer/RandomizerList.uxml.meta


fileFormatVersion: 2
guid: 61284b57145a4b87b960f780ead021cb
timeCreated: 1600368801

13
com.unity.perception/Editor/Randomization/Uxml/Randomizer/AddRandomizerMenu.uxml


<UXML xmlns="UnityEngine.UIElements">
<VisualElement name="container" style="width: 225px; height: 300px; background-color: #383838; border-width: 1px; border-color: #232323;">
<Style src="../../Uss/Styles.uss"/>
<TextField name="search-bar" class="randomizer__menu-search-bar" style="padding: 3px;"/>
<VisualElement name="directory-label" style="flex-direction: row; justify-content: space-between; align-items: center; background-color: #414141">
<VisualElement name="directory-chevron" class="randomization__chevron-left"/>
<TextElement name="directory-label-text" text="Randomizers" style="-unity-font-style: bold; padding: 3px;"/>
<VisualElement/>
</VisualElement>
<VisualElement name="menu-options"/>
<VisualElement class="randomizer__menu-search-icon"/>
</VisualElement>
</UXML>

6
com.unity.perception/Editor/Randomization/Uxml/Randomizer/MenuDirectoryElement.uxml


<UXML xmlns="UnityEngine.UIElements">
<VisualElement style="flex-direction: row; justify-content: space-between; align-items: center;" class="randomizer__add-menu-directory-item">
<TextElement name="directory" style="margin-right: 10px;"/>
<VisualElement class="randomization__chevron-right"/>
</VisualElement>
</UXML>

16
com.unity.perception/Editor/Randomization/Uxml/Randomizer/RandomizerElement.uxml


<UXML xmlns="UnityEngine.UIElements">
<VisualElement class="randomizer__element">
<VisualElement name="drag-handle" class="randomizer__drag-handle"/>
<VisualElement style="flex-grow: 1;">
<VisualElement style="flex-direction: row; justify-content: space-between;">
<VisualElement style="flex-direction: row; align-items: center;">
<VisualElement name="collapse" class="randomization__collapse-toggle foldout-open"/>
<Toggle name="enabled"/>
<TextElement name="class-name" text="Randomizer Class Name"/>
</VisualElement>
<Button name="remove" class="randomization__remove-item-button"/>
</VisualElement>
<VisualElement name="properties" class="randomization__collapsible-container" style="padding-left: 16px;"/>
</VisualElement>
</VisualElement>
</UXML>

8
com.unity.perception/Editor/Randomization/Uxml/Randomizer/RandomizerList.uxml


<UXML xmlns="UnityEngine.UIElements">
<VisualElement name="randomizers-container" class="scenario__dark-viewport" style="margin-top: 6px; min-height: 100px;"/>
<VisualElement style="flex-direction: row; align-items: center; justify-content: center; margin-top: 4px;">
<Button name="add-randomizer-button" text="Add Randomizer"/>
<Button name="expand-all" text="Expand All"/>
<Button name="collapse-all" text="Collapse All"/>
</VisualElement>
</UXML>

10
com.unity.perception/Editor/Randomization/Uxml/Sampler/SamplerElement.uxml


<UXML xmlns="UnityEngine.UIElements" xmlns:editor="UnityEditor.UIElements">
<VisualElement name="sampler-template" style="margin-bottom: 4px;">
<Style src="../../Uss/Styles.uss"/>
<VisualElement style="flex-direction: row; align-items: center;">
<Label name="sampler-name" text="Sampler Name" class="unity-base-field__label"/>
<editor:ToolbarMenu name="sampler-type-dropdown" text="Placeholder Sampler Type" class="sampler__type-menu"/>
</VisualElement>
<VisualElement name="fields-container" style="margin-left: 18px;"/>
</VisualElement>
</UXML>

3
com.unity.perception/Editor/Randomization/VisualElements/Parameter.meta


fileFormatVersion: 2
guid: 198e373f78464aa7a4bf4211e82434dc
timeCreated: 1601669088

8
com.unity.perception/Editor/Randomization/VisualElements/Parameter/CategoricalOptionElement.cs.meta


fileFormatVersion: 2
<<<<<<< HEAD
guid: 5b6d309152934df99cfff89a96b9703e
timeCreated: 1600754567
=======
guid: 3066f77d411047baafb6cc454adc6e37
timeCreated: 1595535184
>>>>>>> 86d25d2... implemented parameter behaviours

39
com.unity.perception/Editor/Randomization/VisualElements/Parameter/ColorHsvaField.cs


using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine.Experimental.Perception.Randomization.Parameters;
using UnityEngine.UIElements;
namespace UnityEngine.Perception.Randomization.Editor
{
class ColorHsvaField : ColorField
{
SerializedProperty m_Property;
SerializedProperty m_H;
SerializedProperty m_S;
SerializedProperty m_V;
SerializedProperty m_A;
public ColorHsvaField(SerializedProperty property)
{
m_Property = property;
label = m_Property.displayName;
m_H = m_Property.FindPropertyRelative("h");
m_S = m_Property.FindPropertyRelative("s");
m_V = m_Property.FindPropertyRelative("v");
m_A = m_Property.FindPropertyRelative("a");
rawValue = (Color)new ColorHsva(m_H.floatValue, m_S.floatValue, m_V.floatValue, m_A.floatValue);
this.RegisterValueChangedCallback(evt =>
{
var color = (ColorHsva)evt.newValue;
m_H.floatValue = color.h;
m_S.floatValue = color.s;
m_V.floatValue = color.v;
m_A.floatValue = color.a;
m_Property.serializedObject.ApplyModifiedProperties();
});
}
}
}

8
com.unity.perception/Editor/Randomization/VisualElements/Parameter/ColorHsvaField.cs.meta


fileFormatVersion: 2
<<<<<<< HEAD
guid: c70590a385e44f6fbe82f5c74cb92f71
timeCreated: 1600754567
=======
guid: 103b163a2467415ab86b0df8175b12a6
timeCreated: 1598254290
>>>>>>> 86d25d2... implemented parameter behaviours

48
com.unity.perception/Editor/Randomization/VisualElements/Parameter/DrawerParameterElement.cs


using UnityEditor;
using UnityEngine.Experimental.Perception.Randomization.Editor;
using UnityEngine.Experimental.Perception.Randomization.Parameters;
using UnityEngine.UIElements;
namespace UnityEngine.Perception.Randomization.Editor
{
class DrawerParameterElement : VisualElement
{
Parameter m_Parameter;
SerializedProperty m_Collapsed;
SerializedProperty m_Property;
const string k_CollapsedParameterClass = "collapsed";
bool collapsed
{
get => m_Collapsed.boolValue;
set
{
m_Collapsed.boolValue = value;
m_Property.serializedObject.ApplyModifiedPropertiesWithoutUndo();
if (value)
AddToClassList(k_CollapsedParameterClass);
else
RemoveFromClassList(k_CollapsedParameterClass);
}
}
public DrawerParameterElement(SerializedProperty property)
{
m_Property = property;
m_Collapsed = property.FindPropertyRelative("collapsed");
AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(
$"{StaticData.uxmlDir}/Parameter/ParameterDrawer.uxml").CloneTree(this);
var collapseToggle = this.Q<VisualElement>("collapse");
collapseToggle.RegisterCallback<MouseUpEvent>(evt => collapsed = !collapsed);
collapsed = m_Collapsed.boolValue;
var fieldNameField = this.Q<Label>("field-name");
fieldNameField.text = property.displayName;
var drawer = this.Q<VisualElement>("drawer");
drawer.Add(new ParameterElement(property));
}
}
}

8
com.unity.perception/Editor/Randomization/VisualElements/Parameter/DrawerParameterElement.cs.meta


fileFormatVersion: 2
<<<<<<< HEAD
guid: a9f0d234cfc24a8da54ec04d5ec9a129
timeCreated: 1600754567
=======
guid: e2eb905ca8c14b5cbe43e48418948be0
timeCreated: 1598255728
>>>>>>> 86d25d2... implemented parameter behaviours

196
com.unity.perception/Editor/Randomization/VisualElements/Parameter/ParameterElement.cs


using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine.Experimental.Perception.Randomization.Editor;
using UnityEngine.Experimental.Perception.Randomization.Parameters;
using UnityEngine.UIElements;
namespace UnityEngine.Perception.Randomization.Editor
{
class ParameterElement : VisualElement
{
VisualElement m_PropertiesContainer;
SerializedProperty m_SerializedProperty;
Parameter parameter => (Parameter)StaticData.GetManagedReferenceValue(m_SerializedProperty);
CategoricalParameterBase categoricalParameter => (CategoricalParameterBase)parameter;
public ParameterElement(SerializedProperty property)
{
var template = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(
$"{StaticData.uxmlDir}/Parameter/ParameterElement.uxml");
template.CloneTree(this);
m_SerializedProperty = property;
m_PropertiesContainer = this.Q<VisualElement>("properties");
CreatePropertyFields();
}
void CreatePropertyFields()
{
m_PropertiesContainer.Clear();
if (parameter is CategoricalParameterBase)
{
CreateCategoricalParameterFields();
return;
}
var currentProperty = m_SerializedProperty.Copy();
var nextSiblingProperty = m_SerializedProperty.Copy();
nextSiblingProperty.NextVisible(false);
if (currentProperty.NextVisible(true))
{
do
{
if (SerializedProperty.EqualContents(currentProperty, nextSiblingProperty))
break;
if (currentProperty.type.Contains("managedReference") &&
currentProperty.managedReferenceFieldTypename == StaticData.samplerSerializedFieldType)
m_PropertiesContainer.Add(new SamplerElement(currentProperty.Copy(), parameter));
else
{
var propertyField = new PropertyField(currentProperty.Copy());
propertyField.Bind(currentProperty.serializedObject);
m_PropertiesContainer.Add(propertyField);
}
} while (currentProperty.NextVisible(false));
}
}
void CreateCategoricalParameterFields()
{
var template = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(
$"{StaticData.uxmlDir}/Parameter/CategoricalParameterTemplate.uxml").CloneTree();
var optionsProperty = m_SerializedProperty.FindPropertyRelative("m_Categories");
var probabilitiesProperty = m_SerializedProperty.FindPropertyRelative("probabilities");
var probabilities = categoricalParameter.probabilities;
var listView = template.Q<ListView>("options");
listView.itemsSource = probabilities;
listView.itemHeight = 22;
listView.selectionType = SelectionType.None;
listView.style.flexGrow = 1.0f;
listView.style.height = new StyleLength(listView.itemHeight * 4);
VisualElement MakeItem() => new CategoricalOptionElement(
optionsProperty, probabilitiesProperty);
listView.makeItem = MakeItem;
void BindItem(VisualElement e, int i)
{
var optionElement = (CategoricalOptionElement)e;
optionElement.BindProperties(i);
var removeButton = optionElement.Q<Button>("remove");
removeButton.clicked += () =>
{
probabilitiesProperty.DeleteArrayElementAtIndex(i);
// First delete sets option to null, second delete removes option
var numOptions = optionsProperty.arraySize;
optionsProperty.DeleteArrayElementAtIndex(i);
if (numOptions == optionsProperty.arraySize)
optionsProperty.DeleteArrayElementAtIndex(i);
m_SerializedProperty.serializedObject.ApplyModifiedProperties();
listView.itemsSource = categoricalParameter.probabilities;
listView.Refresh();
};
}
listView.bindItem = BindItem;
var addOptionButton = template.Q<Button>("add-option");
addOptionButton.clicked += () =>
{
probabilitiesProperty.arraySize++;
optionsProperty.arraySize++;
m_SerializedProperty.serializedObject.ApplyModifiedProperties();
listView.itemsSource = categoricalParameter.probabilities;
listView.Refresh();
listView.ScrollToItem(probabilitiesProperty.arraySize);
};
var addFolderButton = template.Q<Button>("add-folder");
if (categoricalParameter.sampleType.IsSubclassOf(typeof(Object)))
{
addFolderButton.clicked += () =>
{
var folderPath = EditorUtility.OpenFolderPanel(
"Add Options From Folder", Application.dataPath, string.Empty);
if (folderPath == string.Empty)
return;
var categories = LoadAssetsFromFolder(folderPath, categoricalParameter.sampleType);
probabilitiesProperty.arraySize += categories.Count;
optionsProperty.arraySize += categories.Count;
var uniformProbability = 1f / categories.Count;
for (var i = 0; i < categories.Count; i++)
{
var optionProperty = optionsProperty.GetArrayElementAtIndex(i);
var probabilityProperty = probabilitiesProperty.GetArrayElementAtIndex(i);
optionProperty.objectReferenceValue = categories[i];
probabilityProperty.floatValue = uniformProbability;
}
m_SerializedProperty.serializedObject.ApplyModifiedProperties();
listView.itemsSource = categoricalParameter.probabilities;
listView.Refresh();
};
}
else
addFolderButton.style.display = new StyleEnum<DisplayStyle>(DisplayStyle.None);
var clearOptionsButton = template.Q<Button>("clear-options");
clearOptionsButton.clicked += () =>
{
probabilitiesProperty.arraySize = 0;
optionsProperty.arraySize = 0;
m_SerializedProperty.serializedObject.ApplyModifiedProperties();
listView.itemsSource = categoricalParameter.probabilities;
listView.Refresh();
};
var scrollView = listView.Q<ScrollView>();
listView.RegisterCallback<WheelEvent>(evt =>
{
if (Mathf.Approximately(scrollView.verticalScroller.highValue, 0f))
return;
if ((scrollView.scrollOffset.y <= 0f && evt.delta.y < 0f) ||
scrollView.scrollOffset.y >= scrollView.verticalScroller.highValue && evt.delta.y > 0f)
evt.StopImmediatePropagation();
});
var uniformToggle = template.Q<Toggle>("uniform");
var uniformProperty = m_SerializedProperty.FindPropertyRelative("uniform");
uniformToggle.BindProperty(uniformProperty);
void ToggleProbabilityFields(bool toggle)
{
if (toggle)
listView.AddToClassList("collapsed");
else
listView.RemoveFromClassList("collapsed");
}
ToggleProbabilityFields(uniformToggle.value);
if (Application.isPlaying)
uniformToggle.SetEnabled(false);
else
uniformToggle.RegisterCallback<ChangeEvent<bool>>(evt => ToggleProbabilityFields(evt.newValue));
var seedField = template.Q<IntegerField>("seed");
seedField.BindProperty(m_SerializedProperty.FindPropertyRelative("m_Sampler.<baseSeed>k__BackingField"));
m_PropertiesContainer.Add(template);
}
static List<Object> LoadAssetsFromFolder(string folderPath, Type assetType)
{
if (!folderPath.StartsWith(Application.dataPath))
throw new ApplicationException("Selected folder is not an asset folder in this project");
var assetsPath = "Assets" + folderPath.Remove(0, Application.dataPath.Length);
var assetIds = AssetDatabase.FindAssets($"t:{assetType.Name}", new []{assetsPath});
var assets = new List<Object>();
foreach (var guid in assetIds)
assets.Add(AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(guid), assetType));
return assets;
}
}
}

8
com.unity.perception/Editor/Randomization/VisualElements/Parameter/ParameterElement.cs.meta


fileFormatVersion: 2
<<<<<<< HEAD
guid: b15caa7425ab4b9a8367bd544ab4730d
timeCreated: 1600754567
=======
guid: ea72d77c64d1447aa195e2068f02cf74
timeCreated: 1595279847
>>>>>>> 86d25d2... implemented parameter behaviours

3
com.unity.perception/Editor/Randomization/VisualElements/Randomizer.meta


fileFormatVersion: 2
guid: c194e9893fcc4197ab26d01371347d07
timeCreated: 1601054944

部分文件因为文件数量过多而无法显示

正在加载...
取消
保存