您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

247 行
9.1 KiB

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);
}
}
}