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

688 行
22 KiB

using System;
using System.Linq;
using UnityEngine;
using UnityEditor;
using System.IO;
using System.Collections.Generic;
using Unity.Mathematics;
using UnityEngine.Profiling;
public class BuildWindow : EditorWindow
{
enum QuickstartMode
{
Singleplayer,
Multiplayer,
}
enum GameLoopMode
{
Serve,
Client,
Preview,
Undefined,
}
enum EditorRole
{
Unused,
Client,
Server,
Mixed,
}
[Serializable]
class QuickstartData
{
public QuickstartMode mode = QuickstartMode.Multiplayer;
public int levelIndex = 0;
public EditorRole editorRole;
public int clientCount = 1;
public bool headlessServer = true;
public string defaultArguments = "";
public List<QuickstartEntry> entries = new List<QuickstartEntry>();
}
[Serializable]
class QuickstartEntry
{
public GameLoopMode gameLoopMode = GameLoopMode.Client;
public bool runInEditor;
public bool headless;
// public Vector2 windowPos;
// public bool useWindowPos;
public string GetArguments(string levelname, string defaultArguments)
{
var arguments = "";
switch (gameLoopMode)
{
case GameLoopMode.Serve:
arguments += " +serve " + levelname;
break;
case GameLoopMode.Client:
arguments += " +client 127.0.0.1 ";
break;
case GameLoopMode.Preview:
arguments += " +preview " + levelname;
break;
}
if (headless)
arguments += " -batchmode -nographics";
arguments += " " + defaultArguments;
return arguments;
}
}
enum BuildAction
{
None,
BuildBundles,
ForceBuildBundles,
StartBuild,
BuildAndRun,
Run,
OpenBuildFolder,
}
const string quickStartDataKey = "QuickStartData";
private void OnEnable()
{
var str = EditorPrefs.GetString(quickStartDataKey, "");
if (str != "")
quickstartData = JsonUtility.FromJson<QuickstartData>(str);
else
quickstartData = new QuickstartData();
// TODO reset m_LevelInfos if LevelInfos are added/removed
}
[MenuItem("FPS Sample/Windows/Project Tools")]
public static void ShowWindow()
{
GetWindow<BuildWindow>(false, "Project Tools", true);
}
void OnGUI()
{
Profiler.BeginSample("BuildWindow.OnGUI");
// We keep levelinfos in a member variable. This forces a reference to the LevelInfos. Otherwise they will be nuked by OpenScene()
if (m_LevelInfos == null)
m_LevelInfos = BuildTools.LoadLevelInfos();
m_ScrollPos = GUILayout.BeginScrollView(m_ScrollPos);
GUILayout.Label("Project", EditorStyles.boldLabel);
GUILayout.TextArea(Application.dataPath.BeforeLast("Assets"));
DrawLevelSelect();
GUILayout.Space(10.0f);
DrawBuildTools();
GUILayout.Space(10.0f);
DrawQuickStart();
GUILayout.EndScrollView();
Profiler.EndSample();
}
void DrawLevelSelect()
{
Profiler.BeginSample("DrawLevelSelect");
GUILayout.Label("Levels", EditorStyles.boldLabel);
GUILayout.BeginVertical(EditorStyles.textArea);
GUILayout.BeginHorizontal();
GUILayout.Label("Bootstrapper", GUILayout.ExpandWidth(true));
GUILayout.BeginHorizontal(GUILayout.Width(100));
if (GUILayout.Button("Open"))
{
if (UnityEditor.SceneManagement.EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo())
{
var scene = UnityEditor.EditorBuildSettings.scenes[0];
UnityEditor.SceneManagement.EditorSceneManager.OpenScene(scene.path, UnityEditor.SceneManagement.OpenSceneMode.Single);
}
}
GUILayout.EndHorizontal();
GUILayout.EndHorizontal();
LevelInfo openLevel = null;
foreach (var levelInfo in m_LevelInfos)
{
GUILayout.BeginHorizontal();
GUILayout.Label(levelInfo.name, GUILayout.ExpandWidth(true));
GUILayout.BeginHorizontal(GUILayout.Width(100));
if (GUILayout.Button("Open"))
{
if (UnityEditor.SceneManagement.EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo())
{
openLevel = levelInfo;
}
}
if (GUILayout.Button("Serve"))
{
RunBuild("+serve " + levelInfo.name + " -batchmode -nographics");
}
GUILayout.EndHorizontal();
GUILayout.EndHorizontal();
}
GUILayout.EndVertical();
Profiler.EndSample();
if (openLevel != null)
{
UnityEditor.SceneManagement.EditorSceneManager.OpenScene(AssetDatabase.GetAssetPath(openLevel.main_scene), UnityEditor.SceneManagement.OpenSceneMode.Single);
}
}
static string GetBuildPath(BuildTarget buildTarget)
{
if (buildTarget == BuildTarget.PS4)
return "AutoBuildPS4";
else
return "AutoBuild";
}
static string GetBuildExeName(BuildTarget buildTarget)
{
if (buildTarget == BuildTarget.PS4)
return "AutoBuild";
else
return "AutoBuild.exe";
}
static string GetBuildExe(BuildTarget buildTarget)
{
if (buildTarget == BuildTarget.PS4)
return "AutoBuild/AutoBuild.bat";
else
return "AutoBuild.exe";
}
static string GetBundlePath(BuildTarget buildTarget)
{
// On PS4 we copy to "Assets/StreamingAssets" later
return GetBuildPath(buildTarget);
}
static bool s_SingleLevelBuilding = false;
void DrawBuildTools()
{
var action = BuildAction.None;
if (GUILayout.Button("Update Registry"))
{
Debug.Log("Updating registries...");
BundledResourceBuilder.UpdateRegistries(false);
Debug.Log("Updating registries done");
}
GUILayout.Label("Bundles (" + PrettyPrintTimeStamp(TimeLastBuildBundles()) + ")", EditorStyles.boldLabel);
var buildBundledLevels = false;
var buildBundledAssets = false;
var forceBuildBundles = false;
List<LevelInfo> buildOnlyLevels = null;
s_SingleLevelBuilding = EditorGUILayout.Toggle("Single level building", s_SingleLevelBuilding);
if (s_SingleLevelBuilding)
{
GUILayout.BeginVertical();
foreach (var l in m_LevelInfos)
{
if (GUILayout.Button("Build only: " + l.name + " [force]"))
{
buildBundledLevels = true;
forceBuildBundles = true;
buildOnlyLevels = new List<LevelInfo>();
buildOnlyLevels.Add(l);
break;
}
}
GUILayout.EndHorizontal();
}
GUILayout.BeginHorizontal();
if (GUILayout.Button("Levels [force]"))
{
buildBundledLevels = true;
forceBuildBundles = true;
}
if (GUILayout.Button("Assets [force]"))
{
buildBundledAssets = true;
forceBuildBundles = true;
}
if (GUILayout.Button("All [force]"))
{
buildBundledLevels = true;
buildBundledAssets = true;
forceBuildBundles = true;
}
GUILayout.EndHorizontal();
var buildTarget = EditorUserBuildSettings.activeBuildTarget; // BuildTarget.StandaloneWindows64
if (buildBundledLevels || buildBundledAssets)
{
BuildTools.BuildBundles(GetBundlePath(buildTarget), buildTarget, buildBundledAssets, buildBundledLevels, forceBuildBundles, buildOnlyLevels);
if (buildTarget == BuildTarget.PS4)
{
// Copy the asset bundles into the PS4 game folder too
var bundlePathSrc = GetBundlePath(buildTarget) + "/" + SimpleBundleManager.assetBundleFolder;
var bundlePathDst = GetBuildPath(buildTarget) + "/" + GetBuildExeName(buildTarget) + "/Media/StreamingAssets/" + SimpleBundleManager.assetBundleFolder;
BuildTools.CopyDirectory(bundlePathSrc, bundlePathDst);
}
GUIUtility.ExitGUI();
}
GUILayout.Space(10.0f);
GUILayout.Label("Game (" + PrettyPrintTimeStamp(TimeLastBuildGame()) + ")", EditorStyles.boldLabel);
GUILayout.Space(1.0f);
GUILayout.Label("Building for: " + buildTarget.ToString() + " use normal build window to change.", GUILayout.ExpandWidth(false));
GUILayout.BeginHorizontal();
m_BuildDevelopment = EditorGUILayout.Toggle("Development build", m_BuildDevelopment);
GUI.enabled = m_BuildDevelopment;
m_ConnectProfiler = EditorGUILayout.Toggle("Connect profiler", m_ConnectProfiler);
GUI.enabled = true;
GUILayout.EndHorizontal();
m_IL2CPP = EditorGUILayout.Toggle("IL2CPP", m_IL2CPP);
m_AllowDebugging = EditorGUILayout.Toggle("Allow debugging", m_AllowDebugging);
var m_RunArguments = EditorPrefTextField("Arguments", "RunArguments");
GUILayout.BeginHorizontal();
var buildGame = false;
var buildOnlyScripts = false;
if (GUILayout.Button("Build game"))
{
buildGame = true;
}
if (GUILayout.Button("Build ONLY scripts"))
{
buildOnlyScripts = true;
}
if (buildGame || buildOnlyScripts)
{
StopAll();
var buildOptions = m_AllowDebugging ? BuildOptions.AllowDebugging : BuildOptions.None;
if (buildOnlyScripts)
buildOptions |= BuildOptions.BuildScriptsOnly;
if (m_BuildDevelopment)
{
buildOptions |= BuildOptions.Development;
if (m_ConnectProfiler)
buildOptions |= BuildOptions.ConnectWithProfiler;
}
BuildTools.BuildGame(GetBuildPath(buildTarget), GetBuildExeName(buildTarget), buildTarget, buildOptions, "AutoBuild", m_IL2CPP);
if (action == BuildAction.BuildAndRun)
RunBuild("");
GUIUtility.ExitGUI(); // prevent warnings from gui about unmatched layouts
}
if (GUILayout.Button("Run"))
{
RunBuild(m_RunArguments);
GUIUtility.ExitGUI(); // prevent warnings from gui about unmatched layouts
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
var path = Application.dataPath.BeforeLast("Assets") + GetBuildPath(buildTarget);
var windowsPath = path.Replace("/", "\\");
if (GUILayout.Button("Open build folder"))
{
if (Directory.Exists(windowsPath))
{
var p = new System.Diagnostics.Process();
p.StartInfo = new System.Diagnostics.ProcessStartInfo("explorer.exe", windowsPath);
p.Start();
}
else
{
EditorUtility.DisplayDialog("Folder missing", string.Format("Folder {0} doesn't exist yet", windowsPath), "Ok");
}
}
GUILayout.EndHorizontal();
}
void DrawQuickStart()
{
Profiler.BeginSample("DrawQuickStartMenu");
GUILayout.BeginVertical();
var defaultGUIBackgrounColor = GUI.backgroundColor;
if (m_LevelInfos.Count == 0)
{
GUILayout.Label("Quick Start Disabled. No scenes defined");
return;
}
GUILayout.Label("Quick Start", EditorStyles.boldLabel);
var entryCount = quickstartData.mode != QuickstartMode.Singleplayer ? quickstartData.clientCount + 1 : 1;
quickstartData.levelIndex = Math.Min(quickstartData.levelIndex, m_LevelInfos.Count - 1);
var levelInfo = m_LevelInfos[quickstartData.levelIndex];
// Make sure we have enough entries
var minEntryCount = math.max(entryCount, 2);
while (minEntryCount > quickstartData.entries.Count())
quickstartData.entries.Add(new QuickstartEntry());
var str = m_LevelInfos[quickstartData.levelIndex].name + " - ";
str += "Server";
if (quickstartData.editorRole == EditorRole.Server)
{
str += "(Editor)";
}
else
{
str += quickstartData.entries[0].headless ? "(Headless)" : "";
}
str += " & " + quickstartData.clientCount + " clients";
if (quickstartData.editorRole == EditorRole.Client)
{
str += "(1 in editor)";
}
GUILayout.Label(str, EditorStyles.boldLabel);
// Quick start buttons
GUILayout.BeginHorizontal();
{
GUI.backgroundColor = Color.green;
if (GUILayout.Button("Start"))
{
for (var i = 0; i < entryCount; i++)
{
StartEntry(quickstartData.entries[i], levelInfo.name, quickstartData.defaultArguments);
}
}
GUI.backgroundColor = defaultGUIBackgrounColor;
GUI.backgroundColor = Color.red;
if (GUILayout.Button("Stop All"))
{
StopAll();
}
GUI.backgroundColor = defaultGUIBackgrounColor;
}
GUILayout.EndHorizontal();
// Settings
EditorGUI.BeginChangeCheck();
quickstartData.mode = (QuickstartMode)EditorGUILayout.EnumPopup("Mode", quickstartData.mode);
var levelNames = m_LevelInfos.Select(item => item.name).ToArray();
quickstartData.levelIndex = EditorGUILayout.Popup("Level", quickstartData.levelIndex, levelNames);
GUI.enabled = quickstartData.mode != QuickstartMode.Singleplayer;
quickstartData.clientCount = EditorGUILayout.IntField("Clients", quickstartData.clientCount);
quickstartData.headlessServer = EditorGUILayout.Toggle("Headless server", quickstartData.headlessServer);
GUI.enabled = true;
quickstartData.editorRole = (EditorRole)EditorGUILayout.EnumPopup("Use Editor as", quickstartData.editorRole);
quickstartData.defaultArguments = EditorGUILayout.TextField("Default args", quickstartData.defaultArguments);
quickstartData.entries[0].gameLoopMode = quickstartData.mode == QuickstartMode.Singleplayer ? GameLoopMode.Preview : GameLoopMode.Serve;
quickstartData.entries[0].headless = quickstartData.headlessServer;
quickstartData.entries[0].runInEditor = quickstartData.editorRole == EditorRole.Server || quickstartData.editorRole == EditorRole.Mixed;
quickstartData.entries[1].runInEditor = quickstartData.editorRole == EditorRole.Client || quickstartData.editorRole == EditorRole.Mixed;
for (var i = 1; i < entryCount; i++)
{
quickstartData.entries[i].gameLoopMode = GameLoopMode.Client;
quickstartData.entries[i].headless = false;
}
// Make sure only one run in editor
var runInEditorCount = 0;
for (var i = 0; i < entryCount; i++)
{
if (quickstartData.entries[i].runInEditor)
{
runInEditorCount++;
quickstartData.entries[i].runInEditor = runInEditorCount <= 1;
}
}
// Draw entries
GUILayout.Label("Started processes:");
for (var i = 0; i < entryCount; i++)
{
var entry = quickstartData.entries[i];
GUILayout.BeginVertical();
{
GUILayout.BeginHorizontal();
{
GUILayout.Space(10);
GUILayout.Label(entry.runInEditor ? "Editor" : "S.Alone", GUILayout.Width(50));
EditorGUILayout.SelectableLabel(entry.GetArguments(levelInfo.name, quickstartData.defaultArguments), EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
}
GUILayout.EndHorizontal();
}
GUILayout.EndVertical();
}
if (EditorGUI.EndChangeCheck())
{
var json = JsonUtility.ToJson(quickstartData);
EditorPrefs.SetString(quickStartDataKey, json);
}
GUILayout.EndVertical();
Profiler.EndSample();
}
static void StartEntry(QuickstartEntry entry, string levelname, string defaultArguments)
{
var args = entry.GetArguments(levelname, defaultArguments);
if (entry.runInEditor)
EditorLevelManager.StartGameInEditor(args);
else
{
RunBuild(args);
}
}
static string PrettyPrintTimeStamp(DateTime time)
{
var span = DateTime.Now - time;
if (span.TotalMinutes < 60)
return span.Minutes + " mins ago";
if (DateTime.Now.Date == time.Date)
return time.ToShortTimeString() + " today";
if (DateTime.Now.Date.AddDays(-1) == time.Date)
return time.ToShortTimeString() + " yesterday";
return "" + time;
}
static void StopAll()
{
KillAllProcesses();
EditorApplication.isPlaying = false;
}
static string EditorPrefTextField(string label, string editorPrefKey)
{
var str = EditorPrefs.GetString(editorPrefKey);
str = EditorGUILayout.TextField(label, str);
str = str.Trim();
EditorPrefs.SetString(editorPrefKey, str);
return str;
}
static void RunBuild(string args)
{
var buildTarget = EditorUserBuildSettings.activeBuildTarget;
var buildPath = GetBuildPath(buildTarget);
var buildExe = GetBuildExe(buildTarget);
Debug.Log("Starting " + buildExe + " in " + buildPath);
var process = new System.Diagnostics.Process();
process.StartInfo.UseShellExecute = false;
process.StartInfo.FileName = Application.dataPath + "/../" + buildPath + "/" + buildExe; // mogensh: for some reason we now need to specify project path
process.StartInfo.Arguments = args;
process.StartInfo.WorkingDirectory = buildPath;
process.Start();
}
static bool IsPlaying()
{
if (Application.isPlaying)
return true;
var buildExe = GetBuildExe(EditorUserBuildSettings.activeBuildTarget);
var processName = Path.GetFileNameWithoutExtension(buildExe);
var processes = System.Diagnostics.Process.GetProcesses();
foreach (var process in processes)
{
if (process.HasExited)
continue;
if (process.ProcessName == processName)
{
return true;
}
}
return false;
}
static void KillAllProcesses()
{
var buildExe = GetBuildExe(EditorUserBuildSettings.activeBuildTarget);
var processName = Path.GetFileNameWithoutExtension(buildExe);
var processes = System.Diagnostics.Process.GetProcesses();
foreach (var process in processes)
{
if (process.HasExited)
continue;
try
{
if (process.ProcessName != null && process.ProcessName == processName)
{
process.Kill();
}
}
catch (InvalidOperationException)
{
}
}
}
static DateTime TimeLastBuildBundles()
{
return Directory.GetLastWriteTime(GetBundlePath(EditorUserBuildSettings.activeBuildTarget) + "/" + SimpleBundleManager.assetBundleFolder);
}
static DateTime TimeLastBuildGame()
{
return Directory.GetLastWriteTime(GetBuildPath(EditorUserBuildSettings.activeBuildTarget));
}
bool m_BuildDevelopment = false;
bool m_ConnectProfiler = false;
bool m_IL2CPP = false;
bool m_AllowDebugging = false;
Vector2 m_ScrollPos;
List<LevelInfo> m_LevelInfos;
QuickstartData quickstartData;
}
public class BuildWindowProgress : EditorWindow
{
static List<string> logs = new List<string>();
static GUIStyle style;
public static void Open(string heading)
{
BuildWindowProgress window = GetWindow<BuildWindowProgress>(false);
window.position = new Rect(200, 200, 800, 500);// new Rect(Screen.width / 2, Screen.height / 2, 800, 350);
window.ShowPopup();
window.heading = heading;
logs = new List<string>();
Application.logMessageReceived += Msg;
}
private static void Msg(string condition, string stackTrace, LogType type)
{
logs.Add(condition);
}
public string heading = "Heading";
private void OnDestroy()
{
Application.logMessageReceived -= Msg;
}
Vector2 scroll;
int lastLogCount = 0;
string text = "";
void OnGUI()
{
if (style == null)
{
style = new GUIStyle(GUI.skin.label);
Font f = AssetDatabase.LoadAssetAtPath<Font>("Assets/Fonts/RobotoMono-Medium.ttf");
if (f != null)
{
style.font = f;
style.fontSize = 10;
}
style.richText = true;
}
if(lastLogCount != logs.Count)
{
scroll = new Vector2(0, 100000);
if (logs.Count > 1000)
logs = logs.GetRange(logs.Count - 1000, logs.Count);
lastLogCount = logs.Count;
text = string.Join("\n", logs);
}
EditorGUILayout.LabelField(heading, EditorStyles.boldLabel);
GUILayout.Space(20);
scroll = GUILayout.BeginScrollView(scroll);
GUILayout.Label(text, style);
GUILayout.EndScrollView();
}
}