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

473 行
17 KiB

using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEditor;
using System.Threading;
public class BuildTools
{
public static void CopyDirectory(string SourcePath, string DestinationPath)
{
//Create all of the directories
foreach (string dirPath in Directory.GetDirectories(SourcePath, "*",
SearchOption.AllDirectories))
Directory.CreateDirectory(dirPath.Replace(SourcePath, DestinationPath));
//Copy all the files & Replaces any files with the same name
foreach (string newPath in Directory.GetFiles(SourcePath, "*.*",
SearchOption.AllDirectories))
File.Copy(newPath, newPath.Replace(SourcePath, DestinationPath), true);
}
public static UnityEditor.Build.Reporting.BuildReport BuildGame(string buildPath, string exeName, BuildTarget target,
BuildOptions opts, string buildId, bool il2cpp)
{
var levels = new string[]
{
"Assets/Scenes/bootstrapper.unity",
"Assets/Scenes/empty.unity"
};
var exePathName = buildPath + "/" + exeName;
Debug.Log("Building: " + exePathName);
Directory.CreateDirectory(buildPath);
// Set all files to be writeable (As Unity 2017.1 sets them to read only)
string fullBuildPath = Directory.GetCurrentDirectory() + "/" + buildPath;
string[] fileNames = Directory.GetFiles(fullBuildPath, "*.*", SearchOption.AllDirectories);
//Contentpipeline compile player scripts
foreach (var fileName in fileNames)
{
FileAttributes attributes = File.GetAttributes(fileName);
attributes &= ~FileAttributes.ReadOnly;
File.SetAttributes(fileName, attributes);
}
string bundlePathSrc = buildPath + "/" + SimpleBundleManager.assetBundleFolder;
string bundlePathDst = "Assets/StreamingAssets/" + SimpleBundleManager.assetBundleFolder;
if (target == BuildTarget.PS4)
{
if (!Directory.Exists(bundlePathSrc))
{
EditorUtility.DisplayDialog("No bundles found", "No Asset Bundles found. Please build them first",
"Ok");
return null;
}
CopyDirectory(bundlePathSrc, bundlePathDst);
}
var monoDirs = Directory.GetDirectories(fullBuildPath).Where(s => s.Contains("MonoBleedingEdge"));
var il2cppDirs = Directory.GetDirectories(fullBuildPath).Where(s => s.Contains("BackUpThisFolder_ButDontShipItWithYourGame"));
var clearFolder = (il2cpp && monoDirs.Count() > 0) || (!il2cpp && il2cppDirs.Count() > 0);
if (clearFolder)
{
Debug.Log(" deleting old folders ..");
foreach(var file in Directory.GetFiles(fullBuildPath))
File.Delete(file);
foreach(var dir in monoDirs)
Directory.Delete(dir,true);
foreach(var dir in il2cppDirs)
Directory.Delete(dir,true);
foreach(var dir in Directory.GetDirectories(fullBuildPath).Where(s => s.EndsWith("_Data")))
Directory.Delete(dir,true);
}
if (il2cpp)
{
UnityEditor.PlayerSettings.SetScriptingBackend(BuildTargetGroup.Standalone, ScriptingImplementation.IL2CPP);
UnityEditor.PlayerSettings.SetIl2CppCompilerConfiguration(BuildTargetGroup.Standalone, Il2CppCompilerConfiguration.Release);
}
else
{
UnityEditor.PlayerSettings.SetScriptingBackend(BuildTargetGroup.Standalone, ScriptingImplementation.Mono2x);
}
/// Colossal hack to work around build postprocessing expecting everything to be writable in the unity
/// installation, but if people have unity in p4 it will be readonly.
var editorHome = EditorApplication.applicationPath.BeforeLast("/") + "/Data/PlaybackEngines/windowsstandalonesupport";
Debug.Log("Checking for read/only files in standalone players");
if (Directory.Exists(editorHome))
{
var files = Directory.GetFiles(editorHome, "*.*", SearchOption.AllDirectories);
foreach(var f in files)
{
var attr = File.GetAttributes(f);
if((attr & FileAttributes.ReadOnly) != 0)
{
attr = attr & ~FileAttributes.ReadOnly;
Debug.Log("Setting " + f + " to read/write");
File.SetAttributes(f, attr);
}
}
}
Debug.Log("Done.");
Environment.SetEnvironmentVariable("BUILD_ID", buildId, EnvironmentVariableTarget.Process);
var result = BuildPipeline.BuildPlayer(levels, exePathName, target, opts);
Environment.SetEnvironmentVariable("BUILD_ID", "", EnvironmentVariableTarget.Process);
if (target == BuildTarget.PS4)
{
Directory.Delete(bundlePathDst, true);
}
Debug.Log(" ==== Build Done =====");
var stepCount = result.steps.Count();
Debug.Log(" Steps:"+ stepCount);
for(var i=0;i<stepCount;i++)
{
var step = result.steps[i];
Debug.Log("-- " + (i+1) + "/" + stepCount + " " + step.name + " " + step.duration.Seconds + "s --");
foreach (var msg in step.messages)
Debug.Log(msg.content);
}
return result;
}
public static List<LevelInfo> LoadLevelInfos()
{
return LoadAssetsOfType<LevelInfo>();
}
static void AddKeys(Dictionary<string, int> dictionary, string[] keys)
{
foreach (var key in keys)
{
if (dictionary.ContainsKey(key))
{
dictionary[key]++;
}
else
{
dictionary[key] = 1;
}
}
}
public static List<T> LoadAssetsOfType<T>() where T : UnityEngine.Object
{
var result = new List<T>();
var assets = AssetDatabase.FindAssets("t:" + typeof(T).Name);
foreach (var a in assets)
{
var path = AssetDatabase.GUIDToAssetPath(a);
result.Add(AssetDatabase.LoadAssetAtPath<T>(path));
}
return result;
}
static AssetBundleBuild MakeSceneBundleBuild(UnityEngine.Object mainScene, string name)
{
var build = new AssetBundleBuild();
build.assetBundleName = name;
build.assetBundleVariant = "";
var path = AssetDatabase.GetAssetPath(mainScene);
var scenes = new List<string>();
scenes.Add(path.ToLower());
if (EditorLevelManager.IsLayeredLevel(path))
{
foreach (var l in EditorLevelManager.GetLevelLayers(path))
{
scenes.Add(l.ToLower());
}
}
build.assetNames = scenes.ToArray();
return build;
}
static AssetBundleBuild MakeAssetBundleBuild(List<string> assets, string name)
{
var build = new AssetBundleBuild();
build.assetBundleName = name;
build.assetBundleVariant = "";
build.assetNames = assets.ToArray();
return build;
}
static List<string> FindSharedDependencies(List<AssetBundleBuild> builds)
{
var dependenciesCount = new Dictionary<string, int>();
foreach (var build in builds)
{
foreach (var asset in build.assetNames)
{
var dependencies = AssetDatabase.GetDependencies(asset, true);
AddKeys(dependenciesCount, dependencies);
}
}
var shared = new List<string>();
foreach (var dependency in dependenciesCount)
{
if (dependency.Key.EndsWith(".unity"))
continue;
if (dependency.Value > 1)
shared.Add(dependency.Key);
}
return shared;
}
public static void BuildBundles(string bundlePath, BuildTarget target, bool buildBundledAssets, bool buildBundledLevels, bool force = false, List<LevelInfo> buildOnlyLevels = null)
{
Debug.Log("Scene cooking started");
var path = bundlePath + "/" + SimpleBundleManager.assetBundleFolder;
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
BuildAssetBundleOptions assetBundleOptions = BuildAssetBundleOptions.UncompressedAssetBundle;
if (force)
{
Debug.Log("Forcing rebuild");
assetBundleOptions |= BuildAssetBundleOptions.ForceRebuildAssetBundle;
}
if (buildBundledLevels)
BuildLevelBundles(path, target, assetBundleOptions, buildOnlyLevels);
if (buildBundledAssets)
BundledResourceBuilder.BuildBundles(path, target, assetBundleOptions);
}
public static void BuildLevelBundles(string path, BuildTarget target, BuildAssetBundleOptions assetBundleOptions, List<LevelInfo> buildOnlyLevels = null)
{
var builds = new List<AssetBundleBuild>();
foreach (var levelInfo in LoadLevelInfos())
{
if (buildOnlyLevels != null && !buildOnlyLevels.Contains(levelInfo))
continue;
Debug.Log(" - adding level: " + AssetDatabase.GetAssetPath(levelInfo.main_scene));
var build = MakeSceneBundleBuild(levelInfo.main_scene, levelInfo.name);
builds.Add(build);
}
BuildPipeline.BuildAssetBundles(path, builds.ToArray(), assetBundleOptions, EditorUserBuildSettings.activeBuildTarget);
Debug.Log("Scene cooking done");
}
static string GetBuildName()
{
var buildNumber = System.Environment.GetEnvironmentVariable("BUILD_NUMBER");
if (buildNumber == null)
{
buildNumber = "Dev";
}
var changeSet = System.Environment.GetEnvironmentVariable("P4_CHANGELIST");
if (changeSet == null)
{
changeSet = "0";
}
var now = System.DateTime.Now;
var name = now.ToString("yyyyMMdd") + "." + buildNumber + "." + changeSet;
return name;
}
static string GetBuildPath(BuildTarget target, string buildName)
{
return "Builds/" + target.ToString() + "/" + Application.productName + "_" + target.ToString() + "_" + buildName;
}
[MenuItem("Assets/ResirializeAssets")]
public static void ReserializeProject()
{
if (Selection.assetGUIDs.Length == 0)
return;
List<string> paths = new List<string>();
foreach (var g in Selection.assetGUIDs)
paths.Add(AssetDatabase.GUIDToAssetPath(g));
if (EditorUtility.DisplayDialog("Reserialize " + paths.Count + " assets", "Do you want to reserialize " + paths.Count + " assets?", "Yes, I do!"))
{
foreach(var p in paths)
{
var a = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(p);
EditorUtility.SetDirty(a);
}
AssetDatabase.SaveAssets();
}
}
[MenuItem("FPS Sample/BuildSystem/Win64/OpenBuildFolder")]
public static void OpenBuildFolder()
{
var target = BuildTarget.StandaloneWindows64;
var buildName = GetBuildName();
var buildPath = GetBuildPath(target, buildName);
if (Directory.Exists(buildPath))
{
Debug.Log("Opening " + buildPath);
var p = new System.Diagnostics.Process();
p.StartInfo = new System.Diagnostics.ProcessStartInfo("explorer.exe", Path.GetFullPath(buildPath));
p.Start();
}
}
[MenuItem("FPS Sample/BuildSystem/Win64/Deploy")]
public static void Deploy()
{
Debug.Log("Window64 Deploying...");
var target = BuildTarget.StandaloneWindows64;
var buildName = GetBuildName();
var buildPath = GetBuildPath(target, buildName);
//string executableName = Application.productName + ".exe";
var platform = target.ToString();
var clientApi = "b5176262e35ba4aa8280a68aae7b0492";
// TODO: Figure out if it's possible to initialize cloud / unityconnect here instead
var projectId = CloudProjectSettings.projectId;
var orgId = CloudProjectSettings.organizationId;
var projectName = CloudProjectSettings.projectId;
var accessToken = CloudProjectSettings.accessToken;
var deploy = new ConnectedGames.Build.DeployTools(OnProgressUpdate, clientApi, projectId, orgId, projectName, accessToken);
var dstPath = buildName + ".zip";
Debug.Log("Starting upload src=" + buildPath + " platform=" + platform + " isClient=N/A" +
" clientApi=" + clientApi + " projectId=" + projectId + " orgId=" + orgId +
" projectName=" + projectName + " accessToken=" + accessToken);
deploy.CompressAndUpload(buildPath, buildPath+"/"+dstPath, platform, buildName);
while (!deploy.Done)
{
deploy.UpdateLoop();
Thread.Sleep(100);
}
}
private static void OnProgressUpdate(string fileName, double progress)
{
Debug.Log(fileName + ":" + progress);
return;
}
[MenuItem("FPS Sample/BuildSystem/Win64/PostProcess")]
public static void PostProcess()
{
Debug.Log("Window64 build postprocessing...");
var target = BuildTarget.StandaloneWindows64;
var buildName = GetBuildName();
var buildPath = GetBuildPath(target, buildName);
string executableName = Application.productName + ".exe";
if (!Directory.Exists(buildPath) || !File.Exists(buildPath + "/" + Application.productName + ".exe"))
{
Debug.Log("No build here: " + buildPath);
}
Debug.Log("Writing config files");
// Build server bat
var serverBat = new string[]
{
"REM start game server on level_01",
executableName + " -nographics -batchmode +serve level_01 +game.modename assault"
};
File.WriteAllLines(buildPath + "/server.bat", serverBat);
Debug.Log(" server.bat");
// Build empty user.cfg
File.WriteAllLines(buildPath + "/user.cfg", new string[] { });
Debug.Log(" user.cfg");
// Build game.cfg
var gameCfg = new string[]
{
"client",
"load level_menu"
};
File.WriteAllLines(buildPath + "/game.cfg", gameCfg);
Debug.Log(" game.cfg");
Debug.Log("Window64 build postprocessing done.");
}
[MenuItem("FPS Sample/BuildSystem/Win64/CreateBuildWindows64")]
public static void CreateBuildWindows64()
{
CreateBuildWindows64(false);
}
[MenuItem("FPS Sample/BuildSystem/Win64/CreateBuildWindows64-IL2CPP")]
public static void CreateBuildWindows64IL2CPP()
{
CreateBuildWindows64(true);
}
static void CreateBuildWindows64(bool useIL2CPP)
{
Debug.Log("Window64 build started. (" + (useIL2CPP ? "IL2CPP" : "Mono") + ")");
var target = BuildTarget.StandaloneWindows64;
var buildName = GetBuildName();
var buildPath = GetBuildPath(target, buildName);
string executableName = Application.productName + ".exe";
Directory.CreateDirectory(buildPath);
BuildBundles(buildPath, target, true, true, true);
var res = BuildGame(buildPath, executableName, target, BuildOptions.None, buildName, useIL2CPP);
if (!res)
throw new Exception("BuildPipeline.BuildPlayer failed");
if (res.summary.result != UnityEditor.Build.Reporting.BuildResult.Succeeded)
throw new Exception("BuildPipeline.BuildPlayer failed: " + res.ToString());
Debug.Log("Window64 build completed...");
PostProcess();
}
[MenuItem("FPS Sample/BuildSystem/PS4/CreateBuildPS4")]
public static void CreateBuildPS4()
{
var target = BuildTarget.PS4;
var buildName = GetBuildName();
var buildPath = GetBuildPath(target, buildName);
string executableName = Application.productName;
Directory.CreateDirectory(buildPath);
BuildBundles(buildPath, target, true, true, true);
var res = BuildGame(buildPath, executableName, target, BuildOptions.None, buildName, false);
if (!res)
throw new Exception("BuildPipeline.BuildPlayer failed");
if (res.summary.result != UnityEditor.Build.Reporting.BuildResult.Succeeded)
throw new Exception("BuildPipeline.BuildPlayer failed: " + res.ToString());
}
[MenuItem("FPS Sample/BuildSystem/Linux64/CreateBuildLinux64")]
public static void CreateBuildLinux64()
{
var target = BuildTarget.StandaloneLinux64;
var buildName = GetBuildName();
var buildPath = GetBuildPath(target, buildName);
string executableName = Application.productName;
Directory.CreateDirectory(buildPath);
BuildBundles(buildPath, target, true, true, true);
var res = BuildGame(buildPath, executableName, target, BuildOptions.EnableHeadlessMode, buildName, false);
if (!res)
throw new Exception("BuildPipeline.BuildPlayer failed");
if (res.summary.result != UnityEditor.Build.Reporting.BuildResult.Succeeded)
throw new Exception("BuildPipeline.BuildPlayer failed: " + res.ToString());
}
}