重置2.0.0(beta)

重置2.0.0(beta)
This commit is contained in:
ALEXTANG
2022-08-25 22:53:10 +08:00
parent f287775e18
commit cc908c2b99
905 changed files with 21204 additions and 81789 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a0c24f23d48a91647bf83b825da86e1d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9df4e841d128615429b14a4d4499748e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fe5a4e2b6631946439ff7cee706d0890
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,534 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using TEngine.Editor;
using TEngine.Runtime;
using UnityEditor;
using UnityEngine;
using UnityEditor.Build.Reporting;
using UnityEditor.Callbacks;
using UnityEditor.Compilation;
using Assembly = System.Reflection.Assembly;
using Debug = UnityEngine.Debug;
namespace TEngineCore.Editor
{
public class Builder : IBuilder
{
internal static readonly Builder Instance = new Builder();
/// <summary>
/// 数据部分
/// </summary>
internal BuilderEditor CurBuilderData;
/// <summary>
/// AB构建器
/// </summary>
internal AssetbundleBuilder AssetbundleBuilder = new AssetbundleBuilder();
/// <summary>
/// 设置当前的BuidlerConfigData
/// </summary>
/// <param name="platform"></param>
/// <param name="configName"></param>
public void SetBuilderConfig(BuilderUtility.PlatformType platform, string configName, string configPath = "")
{
CurBuilderData = BuilderUtility.LoadConfig(platform, configName, configPath);
if (CurBuilderData == null)
{
TLogger.LogError("未找到配置config:" + configName);
Process.GetCurrentProcess().Kill();
}
CurBuilderData.platform = platform;
}
/// <summary>
/// 设置当前的BuidlerConfigData
/// </summary>
/// <param name="platform"></param>
/// <param name="configName"></param>
public void SetBuilderConfig(BuilderEditor tmpBuilder)
{
CurBuilderData = tmpBuilder;
}
/// <summary>
/// 切换平台
/// </summary>
/// <param name="platform"></param>
public void SwitchPlatform(BuilderUtility.PlatformType platform)
{
if (CurBuilderData == null)
{
TLogger.LogError("未设置BuilderData请先调用接口SetBuilderConfig");
return;
}
CurBuilderData.platform = platform;
switch (CurBuilderData.platform)
{
case BuilderUtility.PlatformType.Windows:
{
EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTargetGroup.Standalone,
BuildTarget.StandaloneWindows64);
PlayerSettings.SetScriptingBackend(BuildTargetGroup.Standalone, ScriptingImplementation.Mono2x);
PlayerSettings.stripEngineCode = false;
PlayerSettings.SetManagedStrippingLevel(BuildTargetGroup.Standalone, ManagedStrippingLevel.Disabled);
PlayerSettings.SetApplicationIdentifier(BuildTargetGroup.Standalone, CurBuilderData.bundleIdentifier);
}
break;
case BuilderUtility.PlatformType.Android:
{
EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTargetGroup.Android, BuildTarget.Android);
EditorUserBuildSettings.androidBuildSystem = AndroidBuildSystem.Gradle;
PlayerSettings.Android.bundleVersionCode = 1;
PlayerSettings.SetScriptingBackend(BuildTargetGroup.Android,
CurBuilderData.scriptingBackend == BuilderUtility.ScriptBackend.Mono ? ScriptingImplementation.Mono2x : ScriptingImplementation.IL2CPP);
Debug.Log("=============CurBuilderData.scriptingBackend" + CurBuilderData.scriptingBackend);
PlayerSettings.SetApplicationIdentifier(BuildTargetGroup.Android, CurBuilderData.bundleIdentifier);
PlayerSettings.SetApiCompatibilityLevel(BuildTargetGroup.Android, ApiCompatibilityLevel.NET_4_6);
}
break;
case BuilderUtility.PlatformType.iOS:
{
EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTargetGroup.iOS, BuildTarget.iOS);
PlayerSettings.iOS.appleEnableAutomaticSigning = false;
PlayerSettings.iOS.scriptCallOptimization = ScriptCallOptimizationLevel.SlowAndSafe;
PlayerSettings.iOS.targetOSVersionString = "8.0";
PlayerSettings.iOS.buildNumber = "1";
PlayerSettings.SetScriptingBackend(BuildTargetGroup.iOS, ScriptingImplementation.IL2CPP);
PlayerSettings.stripEngineCode = false;
PlayerSettings.SetManagedStrippingLevel(BuildTargetGroup.iOS, ManagedStrippingLevel.Disabled);
PlayerSettings.SetApplicationIdentifier(BuildTargetGroup.iOS, CurBuilderData.bundleIdentifier);
PlayerSettings.SetApiCompatibilityLevel(BuildTargetGroup.iOS, ApiCompatibilityLevel.NET_4_6);
}
break;
}
}
/// <summary>
/// 构建包体
/// </summary>
/// <param name="isDirect">是否直接出包</param>
internal void Build(bool isDirect = false, BuildOptions option = BuildOptions.None)
{
var platform = CurBuilderData.platform;
SwitchPlatform(platform);
DoBuild(isDirect, option);
}
/// <summary>
/// 执行完整构建流程
/// </summary>
/// <param name="isDirectBuild"></param>
/// <param name="option"></param>
void DoBuild(bool isDirectBuild, BuildOptions option)
{
var buildStart = DateTime.Now;
if (!isDirectBuild)
{
if (!BuildAssetBundle())
return;
}
else
{
Instance.AssetbundleBuilder.CopyAssetBundles();
}
BuildPackage(option);
Debug.Log("Apk打包总耗时" + (DateTime.Now - buildStart).TotalMinutes.ToString("F2") + " 分钟");
}
/// <summary>
/// 构建AB包部分
/// </summary>
/// <returns></returns>
public bool BuildAssetBundle()
{
if (CurBuilderData == null)
{
TLogger.LogError("未设置BuilderData请先调用接口SetBuilderConfig");
return false;
}
var start = DateTime.Now;
BuilderInjectorHandler(BuilderInjectorMoment.BeforeCollect_AssetBundle);
if (!Instance.AssetbundleBuilder.CollectAssetBundles(CurBuilderData.builderBundlePolicy, CurBuilderData.bundleConfig))
return false;
if (CurBuilderData.bCollectShaderVariant)
{
Debug.Log("CollectShaderVariant");
var list = ShaderVariantCollector.ClollectSharderAndVariant();
string abName = ShaderVariantCollector.GetShaderVariantAbName();
Dictionary<string, string> additionalRes = new Dictionary<string, string>();
foreach (var sv in list)
{
if (!additionalRes.ContainsKey(sv))
additionalRes.Add(sv, abName);
else
Debug.LogError("重复的变体路径:" + sv);
}
list.Clear();
Instance.AssetbundleBuilder.InsertAdditionalTopRes(additionalRes, false);
}
Instance.AssetbundleBuilder.SetAndCheckAssetDependencies();
BuilderInjectorHandler(BuilderInjectorMoment.BeforeBuild_AssetBundle);
Instance.AssetbundleBuilder.BuildAssetBundlesAfterCollect(CurBuilderData.buildType == BuilderUtility.BuildType.Development, CurBuilderData.bIncrementBuildAB);
BuilderInjectorHandler(BuilderInjectorMoment.AfterBuild_AssetBundle);
Debug.Log("AB打包总耗时" + (DateTime.Now - start).TotalMinutes.ToString("F2") + " 分钟");
return true;
}
/// <summary>
/// 输出apk或安卓工程
/// </summary>
/// <param name="option"></param>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public void BuildPackage(BuildOptions option = BuildOptions.None)
{
if (CurBuilderData == null)
{
TLogger.LogError("未设置BuilderData请先调用接口SetBuilderConfig");
return;
}
CurBuilderData.ApplyArgs("");
PlayerSettings.productName = CurBuilderData.productName;
PlayerSettings.bundleVersion = CurBuilderData.bundleVersion + "." + CurBuilderData._bBaseVersion;
if (EditorUserBuildSettings.development)
option |= BuildOptions.Development;
if (EditorUserBuildSettings.connectProfiler)
option |= BuildOptions.ConnectWithProfiler;
if (EditorUserBuildSettings.buildWithDeepProfilingSupport)
option |= BuildOptions.EnableDeepProfilingSupport;
string applicationName = string.Empty;
BuildTarget target = BuildTarget.NoTarget;
switch (CurBuilderData.platform)
{
case BuilderUtility.PlatformType.Android:
applicationName = CurBuilderData.bExportAndroidProject ? $"{CurBuilderData.productName}" : $"{CurBuilderData.productName}.apk";
target = BuildTarget.Android;
break;
case BuilderUtility.PlatformType.iOS:
applicationName = $"{CurBuilderData.productName}.ipa";
target = BuildTarget.iOS;
break;
case BuilderUtility.PlatformType.Windows:
applicationName = $"{CurBuilderData.productName}.exe";
target = BuildTarget.StandaloneWindows64;
break;
default:
UnityEngine.Debug.LogError("Not supported target platform");
return;
}
SetAppStoreUrl();
LoaderUtilities.DeleteFolder(FileSystem.ResourceRoot);
BuilderInjectorHandler(BuilderInjectorMoment.BeforeBuild_Apk);
BuilderInjectorHandler(BuilderInjectorMoment.BeforeBuild_FirstZip);
MakeFirstZip();
BuilderInjectorHandler(BuilderInjectorMoment.AfterBuild_FirstZip);
CopyRawBytes();
string export = CurBuilderData.ProjectExportPath;
if (string.IsNullOrEmpty(export))
export = $"{FileSystem.BuildPath}/{(CurBuilderData.platform).ToString()}/{applicationName}";
BuildReport result = BuildPipeline.BuildPlayer(new string[] { EditorBuildSettings.scenes[0].path },
export, target, option);
switch (result.summary.result)
{
case BuildResult.Unknown:
TLogger.LogInfo("Build Package FAIL! 未知错误!");
break;
case BuildResult.Succeeded:
TLogger.LogInfo("Build Package OK!");
break;
case BuildResult.Failed:
TLogger.LogInfo("Build Package FAIL!");
break;
case BuildResult.Cancelled:
TLogger.LogInfo("Build Package Cancelled!");
break;
default:
throw new ArgumentOutOfRangeException();
}
if (Application.isBatchMode && result.summary.result != BuildResult.Succeeded)
{
Process.GetCurrentProcess().Kill();
}
BuilderInjectorHandler(BuilderInjectorMoment.AfterBuild_Apk);
}
/// <summary>
/// Builder流程插入处理器
/// </summary>
/// <param name="moment"></param>
internal void BuilderInjectorHandler(BuilderInjectorMoment moment)
{
try
{
var compilationAssemblies = CompilationPipeline.GetAssemblies();
List<Assembly> assemblies = new List<Assembly>();
foreach (var assembly in compilationAssemblies)
{
assemblies.Add(Assembly.LoadFrom(assembly.outputPath));
}
foreach (var assembly in assemblies)
{
var types = assembly.GetTypes();
foreach (var type in types)
{
MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static |
BindingFlags.Public | BindingFlags.IgnoreCase);
foreach (var method in methods)
{
foreach (var att in method.GetCustomAttributes(false))
{
if (att is TEngineBuilderInjectorAttribute a)
{
if (!assembly.FullName.Contains("Editor"))
{
Debug.LogError($"{type.Name}.cs不在Editor中已跳过");
continue;
}
if (type.IsSubclassOf(typeof(UnityEngine.Object)))
{
Debug.LogError(
$"{type.Name}.cs中函数{method.Name}标记了BuilderInjectorHandler请勿在mono脚本上调用已跳过");
continue;
}
if (!a.IsInMoment(moment))
continue;
if (method.IsStatic)
{
method.Invoke(null, null);
}
else
{
var obj = Activator.CreateInstance(type);
method.Invoke(obj, null);
}
Debug.Log($"已完成执行插入方法:{method.Name}");
}
}
}
}
}
}
catch (Exception e)
{
Debug.LogError($"BuilderInjectorHandler_{moment.ToString()}:" + e);
}
}
/// <summary>
/// 拷贝RawBytes文件部分
/// </summary>
private static void CopyRawBytes()
{
//复制到StreamingAsset下
string target = $"{string.Format("{0}/RawBytes", FileSystem.ResourceRootInStreamAsset)}";
if (Directory.Exists(target))
Directory.Delete(target, true);
Directory.CreateDirectory(target);
LoaderUtilities.CopyDirectory(AssetConfig.AssetRootPath + "/" + "RawBytes", target);
LoaderUtilities.ClearMeta(target);
AssetDatabase.Refresh();
}
[PostProcessBuild(1)]
static void OnPostprocessBuild(BuildTarget target, string pathToBuiltProject)
{
Debug.Log($"target: {target.ToString()}");
Debug.Log($"pathToBuiltProject: {pathToBuiltProject}");
Debug.Log($"productName: {PlayerSettings.productName}");
Debug.Log($"version:{GameConfig.Instance.GameBundleVersion}");
string versionStr = GameConfig.Instance.GameBundleVersion.Replace(".", "");
long versionLong = long.Parse(versionStr);
Debug.Log($"versionStr:{versionStr}");
GameConfig.Instance.WriteBaseResVersion(GameConfig.Instance.ResId);
if (target == BuildTarget.Android)
{
string explore = BuilderUtility.GetArgumentValue("ExploreAndroidProject");
if (Instance.CurBuilderData.scriptingBackend == BuilderUtility.ScriptBackend.Mono && !string.IsNullOrEmpty(explore) && explore == "true")
{
byte[] versionByte = new byte[8];
for (int i = 0; i < versionByte.Length; ++i)
versionByte[i] = (byte)((versionLong >> (i * 8)) & 0xff);
#if UNITY_2019_4_OR_NEWER
string dllPath =
$"{pathToBuiltProject}/unityLibrary/src/main/assets/bin/Data/Managed/Assembly-CSharp.dll";
#else
string dllPath =
$"{pathToBuiltProject}/{PlayerSettings.productName}/src/main/assets/bin/Data/Managed/Assembly-CSharp.dll";
#endif
if (File.Exists(dllPath))
{
Debug.Log("Encrypt Assembly-CSharp.dll Start");
byte[] bytes = File.ReadAllBytes(dllPath);
for (int i = 0; i < 901; ++i)
bytes[i] ^= 0x31;
for (int i = 0; i < bytes.Length; i += 2)
bytes[i] ^= bytes[bytes.Length - i - 1];
using (FileStream dllStream = File.OpenWrite(dllPath))
{
dllStream.WriteByte((byte)'L');
dllStream.WriteByte((byte)'U');
dllStream.WriteByte((byte)'A');
dllStream.WriteByte((byte)'C');
foreach (var t in versionByte)
dllStream.WriteByte(t);
dllStream.Write(bytes, 0, bytes.Length);
}
Debug.Log("Encrypt Assembly-CSharp.dll Success");
Debug.Log("Encrypt libmonobdwgc-2.0.so Start !!");
Debug.Log($"Current is : {(EditorUserBuildSettings.development ? "development" : "release")}");
#if UNITY_2019_4_OR_NEWER
string armv7ASoPath =
$"{pathToBuiltProject}/unityLibrary/src/main/jniLibs/armeabi-v7a/libmonobdwgc-2.0.so";
string x86SoPath = $"{pathToBuiltProject}/unityLibrary/src/main/jniLibs/x86/libmonobdwgc-2.0.so";
#else
string armv7ASoPath =
$"{pathToBuiltProject}/{PlayerSettings.productName}/src/main/jniLibs/armeabi-v7a/libmonobdwgc-2.0.so";
string x86SoPath = $"{pathToBuiltProject}/{PlayerSettings.productName}/src/main/jniLibs/x86/libmonobdwgc-2.0.so";
#endif
#if UNITY_2019_4_OR_NEWER
AppendDll(dllPath, $"{pathToBuiltProject}/unityLibrary/src/main/assets/");
#else
AppendDll(dllPath, $"{pathToBuiltProject}/{PlayerSettings.productName}/src/main/assets/");
#endif
}
else
{
Debug.LogError(dllPath + " Not Found!!");
}
}
}
string[] dirList = { $"{Environment.CurrentDirectory}/Library/il2cpp_android_armeabi-v7a/il2cpp_cache", $"{Environment.CurrentDirectory}/Library/il2cpp_android_arm64-v8a/il2cpp_cache" };
foreach (var dir in dirList)
{
if (Directory.Exists(dir))
{
DirectoryInfo root = new DirectoryInfo(dir);
DirectoryInfo[] dics = root.GetDirectories();
foreach (var childDir in dics)
{
if (childDir.Name.Contains("linkresult_"))
{
DirectoryInfo directory = new DirectoryInfo(childDir.FullName);
FileInfo[] files = directory.GetFiles();
for (int i = 0; i < files.Length; i++)
{
if (files[i].Name == "libil2cpp.sym.so")
{
string newPath = null;
if (files[i].FullName.Contains("armeabi-v7a"))
newPath = $"{Environment.CurrentDirectory}/SymbolsTemp/armeabi-v7a";
else if (files[i].FullName.Contains("arm64-v8a"))
newPath = $"{Environment.CurrentDirectory}/SymbolsTemp/arm64-v8a";
if (!Directory.Exists(newPath))
{
Directory.CreateDirectory(newPath);
}
File.Copy(files[i].FullName, $"{newPath}/libil2cpp.sym.so", true);
}
}
}
}
}
else
{
//TLogger.LogWarning($"文件夹不存在:{dir}");
}
}
}
/// <summary>
/// 加塞一下dll
/// </summary>
/// <param name="dll_path"></param>
/// <param name="des_path"></param>
static void AppendDll(string dll_path, string des_path)
{
//备份到本地目录
File.Copy(dll_path, $"{FileSystem.BuildPath}/{"Script.bin"}", true);
//备份到streamAsset目录
File.Copy(dll_path, $"{des_path}/TEngineResRoot/{"Script.bin"}", true);
}
/// <summary>
/// 设置跳转地址
/// </summary>
internal void SetAppStoreUrl()
{
#if UNITY_IOS
BuilderUtility.SetAppURL(CurBuilderData.appUrl);
#endif
}
/// <summary>
/// 是否首包压缩
/// </summary>
internal void MakeFirstZip()
{
string outputPath = $"{Application.streamingAssetsPath}/{"First.zip"}";
if (File.Exists(outputPath))
{
File.Delete(outputPath);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a94ae73de7a025f428761ee662f37b87
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,88 @@
using System;
using TEngine.Runtime;
using UnityEditor;
namespace TEngine.Editor
{
/// <summary>
/// 打包流程插入Builder流程插入处理器
/// </summary>
[AttributeUsage(AttributeTargets.All)]
public class TEngineBuilderInjectorAttribute : System.Attribute
{
private int _moment = -1;
public int Moment
{
get => _moment;
set => _moment = value;
}
/// <summary>
/// 插入Builder流程
/// </summary>
/// <param name="monments"></param>
public TEngineBuilderInjectorAttribute(params BuilderInjectorMoment[] monments)
{
if (monments == null || monments.Length == 0)
return;
Moment = 0;
foreach (var builderInjectorMoment in monments)
{
Moment += 1 << (int)builderInjectorMoment;
}
}
public bool IsInMoment(BuilderInjectorMoment moment)
{
return ((1 << (int)moment) & Moment) > 0;
}
}
public enum BuilderInjectorMoment
{
/// <summary>
/// 收集AB包之前
/// </summary>
BeforeCollect_AssetBundle,
/// <summary>
/// 打AB包之前
/// </summary>
BeforeBuild_AssetBundle,
/// <summary>
/// 打AB包之后
/// </summary>
AfterBuild_AssetBundle,
/// <summary>
/// 打APK之前
/// </summary>
BeforeBuild_Apk,
/// <summary>
/// 打APK之后
/// </summary>
AfterBuild_Apk,
/// <summary>
/// 打APK之后
/// </summary>
AfterBuild_FirstZip,
/// <summary>
/// 打APK之前
/// </summary>
BeforeBuild_FirstZip
}
public static class CusInjectorDemoEditor
{
[TEngineBuilderInjector(BuilderInjectorMoment.AfterBuild_AssetBundle)]
public static void TestInjector()
{
UnityEngine.Debug.Log($"productName: {PlayerSettings.productName}");
UnityEngine.Debug.Log($"version:{GameConfig.Instance.GameBundleVersion}");
string versionStr = GameConfig.Instance.GameBundleVersion.Replace(".", "");
long versionLong = long.Parse(versionStr);
UnityEngine.Debug.Log($"versionStr:{versionStr}");
UnityEngine.Debug.LogError("BuilderInjectorMoment.AfterBuild_AssetBundle");
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 653a3ac97936eb646b84bf6ec13eaa86
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,724 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using UnityEditor;
using UnityEngine;
using Debug = UnityEngine.Debug;
using TEngine.Runtime;
namespace TEngineCore.Editor
{
public class BuilderUtility
{
public enum PlatformType
{
Windows,
OSX,
Android,
iOS
}
public enum BuildType
{
Editor,
Release,
Development
}
public enum AssetSourceType
{
GameSource,
ArtSource
}
[Serializable]
public enum EnableLogLevel
{
Info,
Assert,
Warning,
Error,
Exception
}
[Serializable]
public enum ScriptBackend
{
Mono,
IL2CPP,
}
//Bundle配置策略
[Serializable]
public enum BundlePolicy
{
[Tooltip("目录下所有文件都会都会分配一个ab名")]
SingleFile,
[Tooltip("目录下所有文件都以当前目录名为ab名")]
Directory,
[Tooltip("(只检索第一层子目录)\n当前目录下的第一层子目录文件都会以该目录其子目录命名。\n若第一层有单文件则该目录+single命名。")]
ChildDirectory,
[Tooltip("当前目录下的文件会采取其已定义好的ab名作为AB名否则会采用单文件策略命名")]
CustomAbName,
}
//打包Builder策略
[Serializable]
public enum BuilderBundlePolicy
{
SingleFile,
Directory,
Configuration,
}
//配置位置
private const string CONFIG_FOLDER_PATH = FileSystem.BuildPath;
[MenuItem("Assets/Create/[TEngine] ", priority = -10)]
private static void CreateBuilderConfig()
{
if (Selection.activeObject == null)
{
Debug.LogError("请选择目标文件夹");
return;
}
#if UNITY_EDITOR_OSX
string filter = $"{CONFIG_FOLDER_PATH}/IOS";
#elif UNITY_EDITOR_WIN
string filter = $"{CONFIG_FOLDER_PATH}/Win";
#else
string filter = $"{CONFIG_FOLDER_PATH}/Android";
#endif
string targetDirPath = AssetDatabase.GetAssetPath(Selection.activeObject);
if (!targetDirPath.StartsWith(filter))
{
Debug.LogError($"请在{filter}下新建打包配置\n------------------也可使用菜单栏“TEngine/创建AB配置”");
return;
}
if (!Directory.Exists(targetDirPath))
Directory.CreateDirectory(targetDirPath);
string defaultName = "Default";
string suffix = ".asset";
var path = $"{targetDirPath}/{defaultName}{suffix}";
int index = 0;
int safeCount = 50;
while (File.Exists(path) && safeCount-- > 0)
{
path = $"{targetDirPath}/{defaultName}_{++index}{suffix}";
}
if (File.Exists(path))
{
Debug.LogError($"已存在超过50的配置请整理或选择另外文件夹下创建");
return;
}
path = AssetDatabase.GenerateUniqueAssetPath(path);
var builder = ScriptableObject.CreateInstance<BuilderEditor>();
AssetDatabase.CreateAsset(builder, path);
EditorGUIUtility.PingObject(builder);
AssetDatabase.OpenAsset(builder);
}
internal static void SetMacroDefines(ref string macroDefines, BuildType buildType, bool updateMacros = true)
{
//逻辑上需最后一个调用
SetBuildTypeMacroDefines(ref macroDefines, buildType, false);
if (updateMacros)
UpdateMacros(macroDefines);
}
internal static void SetBuildTypeMacroDefines(ref string macroDefines, BuildType buildType, bool updateMacros = true)
{
EditorUserBuildSettings.development = buildType == BuildType.Development;
ModifyMacroDefine(ref macroDefines, "HOT_FIX", true);
switch (buildType)
{
case BuildType.Editor:
ClearMacros(ref macroDefines, false);
break;
case BuildType.Release:
ModifyMacroDefine(ref macroDefines, "RELEASE_BUILD", true);
ModifyMacroDefine(ref macroDefines, "_DEVELOPMENT_BUILD_", false);
ModifyMacroDefine(ref macroDefines, "ASSETBUNDLE_ENABLE", true);
break;
case BuildType.Development:
ModifyMacroDefine(ref macroDefines, "_DEVELOPMENT_BUILD_", true);
ModifyMacroDefine(ref macroDefines, "RELEASE_BUILD", false);
ModifyMacroDefine(ref macroDefines, "ASSETBUNDLE_ENABLE", true);
break;
}
if (updateMacros)
{
UpdateMacros(macroDefines);
}
}
/// <summary>
/// 设置bugly宏定义
/// </summary>
/// <param name="enable"></param>
internal static void EnableGMSymbols(ref string macro, bool enable, bool immediateUpdate = true)
{
ModifyMacroDefine(ref macro, "ENABLE_GM", enable);
if (immediateUpdate)
UpdateMacros(macro);
}
/// <summary>
/// 设置是否忽略首包解压
/// </summary>
/// <param name="enable"></param>
internal static void IgnorFirstZip(ref string macro, bool enable, bool immediateUpdate = true)
{
ModifyMacroDefine(ref macro, "IGNOR_FIRST_ZIP", enable);
if (immediateUpdate)
UpdateMacros(macro);
}
/// <summary>
/// 设置应用商店地址
/// <param name = "param"></param>
/// </summary>
internal static void SetAppURL(string param)
{
try
{
var stream = new FileStream($"{Application.dataPath}/Resources/{FileSystem.ArtResourcePath}.txt", FileMode.OpenOrCreate);
var writer = new StreamWriter(stream);
writer.Write(param);
writer.Flush();
writer.Dispose();
writer.Close();
}
catch (Exception e)
{
UnityEngine.Debug.LogException(e);
throw;
}
}
/// <summary>
/// 清除宏
/// </summary>
/// <param name="macroDefines"></param>
internal static void ClearMacros(ref string macroDefines, bool immediateUpdate = true)
{
ModifyMacroDefine(ref macroDefines, "ENABLE_MONO", false);
ModifyMacroDefine(ref macroDefines, "RELEASE_BUILD", false);
ModifyMacroDefine(ref macroDefines, "_DEVELOPMENT_BUILD_", false);
ModifyMacroDefine(ref macroDefines, "IGNOR_FIRST_ZIP", false);
ModifyMacroDefine(ref macroDefines, "HOT_FIX", false);
ModifyMacroDefine(ref macroDefines, "USE_LUA_PAK_FILE", false);
ModifyMacroDefine(ref macroDefines, "USE_LUA_DISCRETE_FILE", false);
ModifyMacroDefine(ref macroDefines, "ASSETBUNDLE_ENABLE", false);
ModifyMacroDefine(ref macroDefines, "MONO_ENCRYPT", false);
ModifyMacroDefine(ref macroDefines, "ENABLE_GM", false);
ModifyMacroDefine(ref macroDefines, "IGNOR_FIRST_ZIP", false);
if (immediateUpdate)
UpdateMacros(macroDefines);
}
public static void RefreshBackendMacro(ref string macro, ScriptBackend backend, bool immediateUpdate = true)
{
switch (backend)
{
case ScriptBackend.Mono:
ModifyMacroDefine(ref macro, "ENABLE_MONO", true);
break;
case ScriptBackend.IL2CPP:
ModifyMacroDefine(ref macro, "ENABLE_MONO", false);
break;
}
if (immediateUpdate)
UpdateMacros(macro);
}
public static void RefreshLogLevelMacro(ref string macro, EnableLogLevel logLevel, bool immediateUpdate = true)
{
switch (logLevel)
{
case EnableLogLevel.Info:
ModifyMacroDefine(ref macro, "ENABLE_LOG_INFO", true);
ModifyMacroDefine(ref macro, "ENABLE_LOG_ASSERT", true);
ModifyMacroDefine(ref macro, "ENABLE_LOG_WARNING", true);
ModifyMacroDefine(ref macro, "ENABLE_LOG_ERROR", true);
ModifyMacroDefine(ref macro, "ENABLE_LOG_EXCEPTION", true);
break;
case EnableLogLevel.Assert:
ModifyMacroDefine(ref macro, "ENABLE_LOG_INFO", false);
ModifyMacroDefine(ref macro, "ENABLE_LOG_ASSERT", true);
ModifyMacroDefine(ref macro, "ENABLE_LOG_WARNING", true);
ModifyMacroDefine(ref macro, "ENABLE_LOG_ERROR", true);
ModifyMacroDefine(ref macro, "ENABLE_LOG_EXCEPTION", true);
break;
case EnableLogLevel.Warning:
ModifyMacroDefine(ref macro, "ENABLE_LOG_INFO", false);
ModifyMacroDefine(ref macro, "ENABLE_LOG_ASSERT", false);
ModifyMacroDefine(ref macro, "ENABLE_LOG_WARNING", true);
ModifyMacroDefine(ref macro, "ENABLE_LOG_ERROR", true);
ModifyMacroDefine(ref macro, "ENABLE_LOG_EXCEPTION", true);
break;
case EnableLogLevel.Error:
ModifyMacroDefine(ref macro, "ENABLE_LOG_INFO", false);
ModifyMacroDefine(ref macro, "ENABLE_LOG_ASSERT", false);
ModifyMacroDefine(ref macro, "ENABLE_LOG_WARNING", false);
ModifyMacroDefine(ref macro, "ENABLE_LOG_ERROR", true);
ModifyMacroDefine(ref macro, "ENABLE_LOG_EXCEPTION", true);
break;
case EnableLogLevel.Exception:
ModifyMacroDefine(ref macro, "ENABLE_LOG_INFO", false);
ModifyMacroDefine(ref macro, "ENABLE_LOG_ASSERT", false);
ModifyMacroDefine(ref macro, "ENABLE_LOG_WARNING", false);
ModifyMacroDefine(ref macro, "ENABLE_LOG_ERROR", false);
ModifyMacroDefine(ref macro, "ENABLE_LOG_EXCEPTION", true);
break;
}
if (immediateUpdate)
UpdateMacros(macro);
}
/// <summary>
/// 更新projectsetting宏定义
/// </summary>
/// <param name="bMacroChanged"></param>
internal static void UpdateMacros(string macroDefines)
{
string _macroDefines = String.Empty;
switch (EditorUserBuildSettings.activeBuildTarget)
{
case BuildTarget.StandaloneWindows64:
case BuildTarget.StandaloneWindows:
_macroDefines = PlayerSettings.GetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone);
EditorUserBuildSettings.selectedBuildTargetGroup = BuildTargetGroup.Standalone;
break;
case BuildTarget.Android:
_macroDefines = PlayerSettings.GetScriptingDefineSymbolsForGroup(BuildTargetGroup.Android);
EditorUserBuildSettings.selectedBuildTargetGroup = BuildTargetGroup.Android;
break;
case BuildTarget.iOS:
_macroDefines = PlayerSettings.GetScriptingDefineSymbolsForGroup(BuildTargetGroup.iOS);
EditorUserBuildSettings.selectedBuildTargetGroup = BuildTargetGroup.iOS;
break;
}
if (!_macroDefines.Equals(macroDefines))
PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, macroDefines);
}
internal static void ModifyMacroDefine(ref string macroDefines, string macro, bool flag)
{
if (flag)
{
if (macroDefines.IndexOf(macro, StringComparison.Ordinal) < 0)
macroDefines = macroDefines.Length > 0 ? $"{macroDefines};{macro}" : macro;
}
else
{
int index = macroDefines.IndexOf(macro, StringComparison.Ordinal);
if (index >= 0)
{
macroDefines = macroDefines.Remove(index, macro.Length);
index = Mathf.Max(index - 1, 0);
if (macroDefines.Length > 0 && macroDefines[index] == ';')
macroDefines = macroDefines.Remove(index, 1);
}
}
}
internal static BuilderEditor LoadConfig(PlatformType target, string config, string configPath)
{
if (string.IsNullOrEmpty(configPath))
{
configPath = CONFIG_FOLDER_PATH;
}
string path;
switch (target)
{
case PlatformType.Android:
path = $"{configPath}/Android/{config}.asset";
break;
case PlatformType.iOS:
path = $"{configPath}/iOS/{config}.asset";
break;
case PlatformType.Windows:
path = $"{configPath}/Win/{config}.asset";
break;
default:
path = $"{configPath}/New Builder.asset";
break;
}
string defualtPath;
switch (target)
{
case PlatformType.Android:
defualtPath = $"{configPath}/Android_Release.asset";
break;
case PlatformType.Windows:
defualtPath = $"{configPath}/Win_Release.asset";
break;
default:
defualtPath = $"{configPath}/iOS_Release.asset";
break;
}
if (!File.Exists(path))
{
path = defualtPath;
}
Debug.Log("当前使用配置:" + path);
return AssetDatabase.LoadAssetAtPath<BuilderEditor>(path);
}
/// <summary>
/// 提取控制台参数
/// </summary>
/// <param name="keyString"></param>
/// <returns></returns>
public static string GetArgumentValue(string keyString)
{
var args = Environment.GetCommandLineArgs();
keyString = $"-{keyString}";
for (int i = 0; i < args.Length; i++)
{
if (args[i].Equals(keyString))
{
i++;
return i < args.Length ? args[i] : null;
}
}
return null;
}
/// <summary>
/// 从序列化对象中获取数据
/// </summary>
/// <param name="serializedObject"></param>
/// <param name="argv"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static T GetArgvsFromSerializedObject<T>(SerializedObject serializedObject, string argv)
{
object result = null;
if (typeof(T) == typeof(Boolean))
{
if (serializedObject.FindProperty(argv) != null)
{
result = (object)serializedObject.FindProperty(argv).boolValue;
}
}
else if (typeof(T) == typeof(int))
{
if (serializedObject.FindProperty(argv) != null)
{
result = (object)serializedObject.FindProperty(argv).intValue;
}
}
else if (typeof(T) == typeof(string))
{
if (serializedObject.FindProperty(argv) != null)
{
result = (object)serializedObject.FindProperty(argv).stringValue;
}
}
else if (typeof(T) == typeof(BuilderBundlePolicy))
{
if (serializedObject.FindProperty(argv) != null)
{
result = (object)serializedObject.FindProperty(argv).enumValueIndex;
}
}
else if (typeof(T) == typeof(BundlePolicyConfig))
{
if (serializedObject.FindProperty(argv) != null)
{
result = (object)serializedObject.FindProperty(argv).objectReferenceValue;
}
}
else if (typeof(T) == typeof(EnableLogLevel))
{
if (serializedObject.FindProperty(argv) != null)
{
result = (object)serializedObject.FindProperty(argv).enumValueIndex;
}
}
return (T)result;
}
/// <summary>
/// 策略快查
/// </summary>
/// <param name="bundleBundlePolicy"></param>
/// <param name="assetBundleConfig"></param>
/// <returns></returns>
internal static bool PolicyEasyCheck(BuilderBundlePolicy bundleBundlePolicy, List<BundleConfigItem> assetBundleConfig)
{
if (bundleBundlePolicy != BuilderBundlePolicy.Configuration)
{
Debug.Log("非策略模式,资源已全部覆盖");
return true;
}
if (assetBundleConfig == null || assetBundleConfig.Count == 0)
{
Debug.LogError("不正常的策略:策略为空");
return false;
}
EditorUtility.DisplayProgressBar("正在进行资源检查", "快速检查中", 0.7f);
bool result = true;
HashSet<string> filter = new HashSet<string>();
HashSet<string> customDirs = new HashSet<string>();
string suffix = Application.dataPath.Substring(0, Application.dataPath.Length - 6);
foreach (var dir in assetBundleConfig)
{
string path = AssetDatabase.GetAssetPath(dir.mObject);
var policy = (BundlePolicy)(dir.buildType);
if (policy == BundlePolicy.CustomAbName)
customDirs.Add(suffix + path);
filter.Add(suffix + path);
}
Check(new DirectoryInfo(Application.dataPath + "/Game/AssetSource/GameResources/"));
//针对自定义AB名
foreach (string dir in customDirs)
{
using (FileTree fileTree =
FileTree.CreateWithExcludeFilter(dir, new[] { ".meta", ".unity", ".DS_Store" }))
{
List<FileInfo> files = fileTree.GetAllFiles();
for (int i = 0; i < files.Count; ++i)
{
if (string.IsNullOrEmpty(AssetImporter.GetAtPath(files[i].GetAssetPath()).assetBundleName))
{
result = false;
Debug.LogError("自定义AB名策略发现未命名的资源" + files[i].GetAssetPath());
}
}
}
}
if (result)
Debug.Log("策略已完全覆盖");
EditorUtility.ClearProgressBar();
return result;
void Check(FileSystemInfo info)
{
if (filter.Contains(info.FullName.Replace("\\", "/")))
return;
if (!info.Exists) return;
DirectoryInfo dir = info as DirectoryInfo;
//不是目录
if (dir == null) return;
if (dir.Name.Equals("RawBytes")) return;
FileSystemInfo[] files = dir.GetFileSystemInfos();
for (int i = 0; i < files.Length; i++)
{
FileInfo file = files[i] as FileInfo;
//是文件
if (file != null && !file.FullName.EndsWith(".meta"))
{
Debug.LogError("发现无任何策略的单文件!:" + file.FullName);
result = false;
}
else
Check(files[i]);
}
}
}
/// <summary>
/// 策略快查
/// </summary>
/// <returns></returns>
internal static bool PolicyEasyCheck(BuilderBundlePolicy bundleBundlePolicy, BundlePolicyConfig assetBundleConfig = null)
{
if (assetBundleConfig == null || assetBundleConfig.directoryBuild == null || assetBundleConfig.directoryBuild.Count == 0)
{
if (bundleBundlePolicy == BuilderBundlePolicy.Configuration)
{
Debug.LogError("不正常的策略:策略为空");
return false;
}
else
{
return true;
}
}
var targets = assetBundleConfig.directoryBuild;
return PolicyEasyCheck(bundleBundlePolicy, targets);
}
/// <summary>
/// 将数据导出成CSV
/// </summary>
/// <param name="name"></param>
/// <param name="content"></param>
public static void ExportCsv(string name, StringBuilder content)
{
string suffix = Application.dataPath.Substring(0, Application.dataPath.Length - 6);
string logFilePath = suffix + "/BuildBundleInfo/" + name;
if (File.Exists(logFilePath))
File.Delete(logFilePath);
if (!Directory.Exists(suffix + "/BuildBundleInfo"))
Directory.CreateDirectory(suffix + "/BuildBundleInfo");
using (var writer = System.IO.File.CreateText(logFilePath))
{
writer.Write(Encoding.UTF8.GetString(Encoding.Default.GetBytes(content.ToString())));
writer.Close();
}
string str = string.Empty;
using (StreamReader sr = new StreamReader(logFilePath, Encoding.UTF8))
{
str = sr.ReadToEnd();
sr.Close();
}
//以UTF-8带BOM格式重新写入文件
Encoding newEncoding = new UTF8Encoding(true);
using (StreamWriter sw = new StreamWriter(logFilePath, false, newEncoding))
{
sw.Write(str);
sw.Close();
}
EditorUtility.ClearProgressBar();
UnityEngine.Debug.Log(logFilePath + $" 导出完成.");
}
/// <summary>
/// 清理路径下数据
/// </summary>
internal static void ClearAllByPath(string path = null, bool deleteZip = false)
{
if (string.IsNullOrEmpty(path))
{
path = FileSystem.ResourceRootInStreamAsset;
}
if (Directory.Exists(path))
Directory.Delete(path, true);
if (File.Exists($"{path}.meta"))
File.Delete($"{path}.meta");
AssetDatabase.Refresh();
//if (deleteZip)
//{
// LoaderUtilities.DeleteFile(FileSystem.FirstZipSteamAssetsPath);
//}
}
public static void RunExternalCommand(string cmd, string args, string workDir = ".")
{
Process p = new Process();
p.StartInfo.FileName = cmd;
p.StartInfo.Arguments = args;
//p.StartInfo.UseShellExecute = false;
//p.StartInfo.RedirectStandardInput = false;
//p.StartInfo.RedirectStandardOutput = true;
//p.StartInfo.RedirectStandardError = true;
//p.StartInfo.CreateNoWindow = false;
p.StartInfo.WorkingDirectory = workDir;
p.Start();
//Debug.Log(p.StandardOutput.ReadToEnd());
p.WaitForExit();
p.Close();
}
public static void RunPythonExternalCommand(string cmd, string args, string workDir = ".")
{
Process p = new Process();
p.StartInfo.FileName = "python";
p.StartInfo.Arguments = cmd + " " + args;
//p.StartInfo.UseShellExecute = false;
//p.StartInfo.RedirectStandardInput = false;
//p.StartInfo.RedirectStandardOutput = true;
//p.StartInfo.RedirectStandardError = true;
//p.StartInfo.CreateNoWindow = false;
p.StartInfo.WorkingDirectory = workDir;
p.Start();
//Debug.Log(p.StandardOutput.ReadToEnd());
p.WaitForExit();
p.Close();
}
internal static uint GetIntHash(string str)
{
int h = 0;
int len = str.Length;
if (len > 0)
{
int off = 0;
for (int i = 0; i < len; i++)
{
char c = str[off++];
h = 31 * h + c;
}
}
return (uint)h;
}
internal static string GetStringHash(string str)
{
return GetIntHash(str).ToString();
}
}
public static class FileInfoExtension
{
public static string GetAssetPath(this FileInfo fileInfo)
{
string uniPath = fileInfo.FullName.Replace('\\', '/');
return uniPath.Substring(uniPath.IndexOf("Assets/"));
}
public static string NameNoExtension(this FileInfo fileInfo)
{
return fileInfo.Name.Substring(0, fileInfo.Name.LastIndexOf('.'));
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2ecb34ed98b96f14b956d6d9206c5d5a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,220 @@
using System.Collections.Generic;
using System.IO;
using TEngine.Runtime;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
using UnityEngine.Scripting.APIUpdating;
using Object = UnityEngine.Object;
namespace TEngineCore.Editor
{
[CreateAssetMenu]
[MovedFrom(true, null, null, "MakeBuildConfig")]
public class BundlePolicyConfig : ScriptableObject
{
[SerializeField]
public List<BundleConfigItem> directoryBuild;
[SerializeField]
public List<BuildFilter> filters;
private string _path = TEngine.Constant.Setting.AssetRootPath;
public void OnEnable()
{
if (filters == null || filters.Count == 0)
{
List<BuildFilter> list_filter = new List<BuildFilter>();
BuildFilter filter = new BuildFilter { filterPath = TEngine.Constant.Setting.AssetFilterPath };
list_filter.Add(filter);
filters = list_filter;
}
if (directoryBuild == null || directoryBuild.Count == 0)
{
var dir = new DirectoryInfo(_path);
var directorise = dir.GetDirectories();
List<BundleConfigItem> temp = new List<BundleConfigItem>();
for (int i = 0; i < directorise.Length; i++)
{
if (directorise[i].Name.EndsWith(".meta"))
{
continue;
}
BundleConfigItem item = new BundleConfigItem();
item.mObject = AssetDatabase.LoadAssetAtPath<Object>(_path + directorise[i].Name);
item.buildType = 1;
temp.Add(item);
}
this.directoryBuild = temp;
}
}
}
[System.Serializable]
public class BundleConfigItem
{
[SerializeField]
public Object mObject;
[SerializeField]
public int buildType;
}
[System.Serializable]
public class BuildFilter
{
[SerializeField]
public string filterPath;
}
[CustomEditor(typeof(BundlePolicyConfig)), CanEditMultipleObjects]
public class BundlePolicyConfigInspector : UnityEditor.Editor
{
private ReorderableList _reorderableList;
private SerializedProperty prop;
private ReorderableList _filterableList;
private SerializedProperty filter;
private BundlePolicyConfig Configuration { get { return target as BundlePolicyConfig; } }
private void OnEnable()
{
prop = serializedObject.FindProperty("directoryBuild");
_reorderableList = new ReorderableList(serializedObject, prop);
_reorderableList.elementHeight = 30;
_reorderableList.drawElementCallback =
(rect, index, isActive, isFocused) =>
{
var element = prop.GetArrayElementAtIndex(index);
rect.height -= 4;
rect.y += 3;
EditorGUI.PropertyField(rect, element);
};
var defaultColor = GUI.backgroundColor;
_reorderableList.drawElementBackgroundCallback = (rect, index, isActive, isFocused) =>
{
};
_reorderableList.drawHeaderCallback = (rect) =>
EditorGUI.LabelField(rect, prop.displayName);
//过滤规则
filter = serializedObject.FindProperty("filters");
_filterableList = new ReorderableList(serializedObject, filter);
_filterableList.elementHeight = 30;
_filterableList.drawElementCallback =
(rect, index, isActive, isFocused) =>
{
var element = filter.GetArrayElementAtIndex(index);
rect.height -= 4;
rect.y += 3;
EditorGUI.PropertyField(rect, element);
};
_filterableList.drawElementBackgroundCallback = (rect, index, isActive, isFocused) =>
{
};
_filterableList.drawHeaderCallback = (rect) =>
EditorGUI.LabelField(rect, filter.displayName);
}
public override void OnInspectorGUI()
{
serializedObject.Update();
GUILayout.Label("打包配置");
_reorderableList.DoLayoutList();
GUILayout.Label("美术资源检测路径");
_filterableList.DoLayoutList();
//剔除错误目录
if (_reorderableList.serializedProperty?.serializedObject != null)
{
var s = _reorderableList.serializedProperty.serializedObject.targetObject;
if (s != null && s is BundlePolicyConfig bundleConfig)
{
List<BundleConfigItem> tDNodes = new List<BundleConfigItem>();
foreach (var cur in bundleConfig.directoryBuild)
{
var path = AssetDatabase.GetAssetPath(cur.mObject);
if (!path.StartsWith(FileSystem.GameResourcePath))
{
Debug.LogError("剔除!不合法的路径:" + path + "当前只支持GameResources下路径");
tDNodes.Add(cur);
}
else if (path.StartsWith(FileSystem.ArtResourcePath))
{
Debug.LogWarning(path + "该路径不会加入Bundle配置");
tDNodes.Add(cur);
}
}
foreach (var node in tDNodes)
{
bundleConfig.directoryBuild.Remove(node);
}
}
}
serializedObject.ApplyModifiedProperties();
}
}
[CustomPropertyDrawer(typeof(BundleConfigItem))]
public class CharacterDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
using (new EditorGUI.PropertyScope(position, label, property))
{
EditorGUIUtility.labelWidth = 50;
position.height = EditorGUIUtility.singleLineHeight;
Rect iconRect = new Rect(position)
{
width = 300,
height = 20
};
Rect nameRect = new Rect(position)
{
width = position.width - 300,
x = position.x + 300,
};
SerializedProperty Object = property.FindPropertyRelative("mObject");
SerializedProperty BulidType = property.FindPropertyRelative("buildType");
Object.objectReferenceValue = EditorGUI.ObjectField(iconRect, Object.objectReferenceValue, typeof(Object), false);
BulidType.intValue = (int)(BuilderUtility.BundlePolicy)EditorGUI.EnumPopup(nameRect, (BuilderUtility.BundlePolicy)BulidType.intValue);
}
}
}
[CustomPropertyDrawer(typeof(BuildFilter))]
public class BuildFilterDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
using (new EditorGUI.PropertyScope(position, label, property))
{
EditorGUIUtility.labelWidth = 50;
position.height = EditorGUIUtility.singleLineHeight;
Rect textRect = new Rect(position)
{
width = 400,
height = 20
};
SerializedProperty filterPath = property.FindPropertyRelative("filterPath");
filterPath.stringValue = EditorGUI.TextField(textRect, filterPath.stringValue);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8476ab2f16f7faf4cad514ee5ff937b1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f51ddcdee52d8634095dc2379dc26c70
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,893 @@
using System;
using System.IO;
using TEngine;
using TEngine.Runtime;
using UnityEditor;
using UnityEngine;
using UnityEngine.Serialization;
using Object = UnityEngine.Object;
namespace TEngineCore.Editor
{
public class BuilderEditor : SoEditorBase
{
#region
[BuilderEditor("全局宏展示:", ContentType.Label, EditorContent.HorizontalStart + ",FlowA:disPlayType:1")]
internal string symbolLaebl;
private ContentLayout _symbolLaeblLayout = new ContentLayout(GUILayout.Height(100), GUILayout.MaxWidth(70));
[SerializeField]
[BuilderEditor("", ContentType.ScrollLabel, "FlowA:disPlayType:1")]
private string symbolWin;
private ContentLayout _symbolWinLayout = new ContentLayout(GUILayout.MaxHeight(100), GUILayout.ExpandWidth(true));
[SerializeField]
[BuilderEditor("↓↑", ContentType.Button, EditorContent.HorizontalEnd + ",FlowA:disPlayType:1,CB:SwitchSymbolWinLayout")]
private int _switchSymbolWinL;
private ContentLayout _switchSymbolWinLLayout = new ContentLayout(GUILayout.Height(100), GUILayout.MaxWidth(25));
#endregion
#region
//[BuilderEditor("使用说明", ContentType.Button, ",CB:DoHelper")]
//private int helper;
//private ContentLayout _helperLayout = new ContentLayout(GUILayout.Width(100));
[SerializeField]
[BuilderEditor("打包模式切换", ContentType.Enum)]
internal BuilderDisplayType disPlayType = BuilderDisplayType.;
private ContentLayout _disPlayTypeLayout = new ContentLayout();
#endregion
#region
[BuilderEditor("普通模式", ContentType.Label, "FlowA:disPlayType:0")]
internal string titleEasy;
private ContentLayout _titleEasyLayout = new ContentLayout(null);
[BuilderEditor("专业模式", ContentType.Label, "FlowA:disPlayType:1")]
internal string titleDev;
private ContentLayout _titleDevLayout = new ContentLayout(null);
[BuilderEditor("30", ContentType.Space)]
internal char space1;
[BuilderEditor("参数", ContentType.Label, "")]
internal string titleArgs;
private ContentLayout _titleArgsLayout = new ContentLayout(null);
[SerializeField]
[BuilderEditor("检测到未应用的操作!!请应用参数", ContentType.Label, "FlowA:hasSave:0&autoUdate:0")]
internal bool hasSave = false;
private ContentLayout _hasSaveLayout = new ContentLayout(null);
[BuilderEditor("0", ContentType.Space, EditorContent.HorizontalStart)]
internal char spaceautoUdate1;
[SerializeField]
[BuilderEditor("参数自动应用", ContentType.Toggle, "CB:SwitchApplyArgs")]
internal bool autoUdate = false;
[BuilderEditor("应用参数", ContentType.Button, "FlowA:autoUdate:0,CB:ApplyArgs")]
internal string autoUdateBtn;
[BuilderEditor("0", ContentType.Space, EditorContent.HorizontalEnd)]
internal char spaceautoUdate2;
#endregion
#region
[SerializeField]
[BuilderEditor("目标平台", ContentType.Enum, "CB:SwitchPlatform")]
internal BuilderUtility.PlatformType platform = BuilderUtility.PlatformType.Android;
private ContentLayout _platformLayout = new ContentLayout(null);
[SerializeField]
[BuilderEditor("打包模式", ContentType.Enum, "CB:SwitchBuildType")]
internal BuilderUtility.BuildType buildType = BuilderUtility.BuildType.Release;
[FormerlySerializedAs("_enableLogLevel")]
[SerializeField]
[BuilderEditor("Log输出等级仅针对Release版本", ContentType.Enum, "FlowA:disPlayType:1,CB:SwitchLogLevel")]
internal BuilderUtility.EnableLogLevel enableLogLevel = BuilderUtility.EnableLogLevel.Warning;
[FormerlySerializedAs("_scriptingBackend")]
[SerializeField]
[BuilderEditor("编译类型", ContentType.Enum, "FlowA:disPlayType:1&platform:0r1r2,CB:SwitchScriptingBackend")]
internal BuilderUtility.ScriptBackend scriptingBackend = BuilderUtility.ScriptBackend.Mono;
#endregion
#region
[FormerlySerializedAs("_bCleanAssetBundleCache")]
[SerializeField]
[BuilderEditor("增量构建AssetBundle", ContentType.Toggle, "FlowA:buildType:0r2&disPlayType:1")]
internal bool bIncrementBuildAB = false;
[FormerlySerializedAs("_bEnableProfiler")]
[SerializeField]
[BuilderEditor("Enable Profiler", ContentType.Toggle, "FlowA:buildType:2&disPlayType:1,CB:EnableProfiler")]
internal bool bEnableProfiler = false;
#if UNITY_2019_1_OR_NEWER
[FormerlySerializedAs("_bEnableDeepProfiler")]
[SerializeField] [BuilderEditor("Enable DeepProfiling Support", ContentType.Toggle, "FlowA:buildType:2&disPlayType:1,CB:EnableDeepProfiler")]
internal bool bEnableDeepProfiler = false;
#endif
[FormerlySerializedAs("_bExportAndroidProject")]
[SerializeField]
[BuilderEditor("导出Android工程", ContentType.Toggle, "FlowA:buildType:1r2&disPlayType:1,CB:EnableExportAndroidProject")]
internal bool bExportAndroidProject = false;
[FormerlySerializedAs("_bCollectShaderVariant")]
[SerializeField]
[BuilderEditor("单独打包Shader", ContentType.Toggle, "FlowA:disPlayType:1")]
internal bool bCollectShaderVariant = false;
[SerializeField]
[BuilderEditor("启动GM", ContentType.Toggle, "FlowA:disPlayType:1,CB:EnableGM")]
private bool bEnableGM = false;
//[SerializeField]
//[BuilderEditor("剔除首包解压", ContentType.Toggle, "FlowA:disPlayType:1,CB:EnableIgnoreFirstZip")]
//internal bool bIgnoreFirstZip = false;
//[FormerlySerializedAs("_bIgnorHotFix")]
//[SerializeField]
//[BuilderEditor("忽略热更", ContentType.Toggle, "FlowA:disPlayType:1,CB:IgnoreHotFix")]
//private bool bIgnoreHotFix = false;
//private ContentLayout _bIgnoreHotFixLayout = new ContentLayout();
//[FormerlySerializedAs("_bUseDlc")]
//[SerializeField]
//[BuilderEditor("启用大小包模式", ContentType.Toggle, "FlowA:disPlayType:1,CB:UseDlc")]
//internal bool bUseDlc = false;
//private ContentLayout _bUseDlcLayout = new ContentLayout();
#endregion
#region
[SerializeField]
[BuilderEditor("开启加密", ContentType.Toggle, "FlowA:disPlayType:1")]
internal bool bEnableEncrypt = true;
//[SerializeField]
//[BuilderEditor(" \u2022 Lua加密", ContentType.Toggle, "FlowA:bEnableEncrypt:1,FlowS:bEnableEncrypt:1")]
//internal bool bEnableLuaEncrypt = true;
[SerializeField]
[BuilderEditor(" \u2022AB加密偏移(0:不加密):", ContentType.TextField, "FlowA:bEnableEncrypt:1,FlowS:bEnableEncrypt:1")]
internal string abEncryptOffset = "0";
private ContentLayout _abEncryptOffsetLayout = new ContentLayout(GUILayout.ExpandWidth(true));
#endregion
#region
[FormerlySerializedAs("_builderPolicy")]
[SerializeField]
[BuilderEditor("AssetBundle策略", ContentType.Enum, "")]
internal BuilderUtility.BuilderBundlePolicy builderBundlePolicy = BuilderUtility.BuilderBundlePolicy.Directory;
[FormerlySerializedAs("_assetBundleConfig")]
[SerializeField]
[BuilderEditor("AssetBundle配置", ContentType.Obj, ",FlowA:builderBundlePolicy:2")]
internal BundlePolicyConfig bundleConfig;
[FormerlySerializedAs("_bundleIdentifier")]
[SerializeField]
[BuilderEditor("包名", ContentType.TextField, "FlowA:disPlayType:1,CB:ChangeBundleIdentifier")]
internal string bundleIdentifier = "com.TEngine.TEngineDemo";
[FormerlySerializedAs("_productName")]
[SerializeField]
[BuilderEditor("产品名", ContentType.TextField, "FlowA:disPlayType:1,CB:ChangeProductName")]
internal string productName = "TEngine";
[FormerlySerializedAs("_bundleVersion")]
[SerializeField]
[BuilderEditor("APP版本号", ContentType.TextField, "FlowA:disPlayType:1,CB:ChangeBundleVersion")]
internal string bundleVersion = "1.0";
[FormerlySerializedAs("_ABVersion")]
[SerializeField]
[BuilderEditor("资源版本号", ContentType.TextField, "FlowA:disPlayType:1,CB:ChangeABVersion")]
internal string ABVersion = "0";
[BuilderEditor("15", ContentType.Space)]
private char space12;
#endregion
#region
[BuilderEditor("Copy And Encrpt DLL", ContentType.Button, ",CB:CopyDLL,FlowA:disPlayType:1")]
private int copydll;
[BuilderEditor("Build AssetBundle", ContentType.Button, ",CB:BuildAssetBundle,FlowA:disPlayType:1")]
private int buildAssetBundle;
[BuilderEditor("Gen Md5(生成MD5)", ContentType.Button, ",CB:GenMd5,FlowA:disPlayType:1")]
private int genMd5;
[BuilderEditor("Build", ContentType.Button, "CB:BuildApk")]
private int build;
[BuilderEditor("直接出包跳过ab环节", ContentType.Button, "CB:DirectBuildApk,FlowA:disPlayType:1")]
private int directBuild;
#endregion
/// <summary>
/// 工程导出目录
/// </summary>
[SerializeField] internal string ProjectExportPath;
[SerializeField] internal string _bBaseVersion = "0";
[SerializeField] internal string _bResVersion = "0";
/// <summary>
/// 用于额外补充风格\必要的初始化
/// </summary>
public override void AdditionalInit()
{
SwitchSymbolWinLayout("Const");
_symbolWinLayout.SetStyles(EditorStyles.helpBox);
_symbolWinLayout.EditorStyles.fontSize = 12;
_titleEasyLayout.EditorStyles = new GUIStyle() { fontSize = 50 };
_titleEasyLayout.EditorStyles.normal.textColor = Color.white;
_titleDevLayout.EditorStyles = new GUIStyle() { fontSize = 50 };
_titleDevLayout.EditorStyles.normal.textColor = Color.white;
//参数Label
_titleArgsLayout.EditorStyles = new GUIStyle("boldLabel");
_titleArgsLayout.EditorStyles.fontSize = 12;
//未保存的警告
_hasSaveLayout.EditorStyles = new GUIStyle("boldLabel");
_hasSaveLayout.EditorStyles.normal.textColor = Color.red;
_hasSaveLayout.EditorStyles.fontSize = 12;
//目标平台
_platformLayout.EditorStyles = new GUIStyle("MiniPopup");
_platformLayout.EditorStyles.normal.textColor = new Color(30 / 255f, 144 / 255f, 255 / 255f);
//_bIgnoreHotFixLayout.EditorStyles = EditorStyles.radioButton;
//_bUseDlcLayout.EditorStyles = EditorStyles.radioButton;
}
public override void OnAnyThingChange(string args)
{
hasSave = false;
}
/// <summary>
/// 使用说明
/// </summary>
/// <param name="args"></param>
private void DoHelper(string args)
{
//打开本地MD
var scriptObj = MonoScript.FromScriptableObject(this);
var path = AssetDatabase.GetAssetPath(scriptObj).Replace("\\", "/");
path = path.Substring(0, path.LastIndexOf("/", StringComparison.Ordinal)) + "/BuilderUserHelper.md";
EditorUtility.OpenWithDefaultApp(path);
}
/// <summary>
/// 切换平台
/// </summary>
/// <param name="args"></param>
internal void SwitchPlatform(string args)
{
if (BuilderUtility.PlatformType.TryParse<BuilderUtility.PlatformType>(args, out var plat))
{
switch (plat)
{
case BuilderUtility.PlatformType.Windows:
EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTargetGroup.Standalone,
BuildTarget.StandaloneWindows);
break;
case BuilderUtility.PlatformType.OSX:
EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTargetGroup.Standalone,
BuildTarget.StandaloneOSX);
break;
case BuilderUtility.PlatformType.Android:
EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTargetGroup.Android, BuildTarget.Android);
break;
case BuilderUtility.PlatformType.iOS:
EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTargetGroup.iOS, BuildTarget.iOS);
break;
}
}
LoadMacroConfigures();
}
/// <summary>
/// 切换日志等级
/// </summary>
/// <param name="args"></param>
private void SwitchLogLevel(string args)
{
if (!autoUdate)
return;
LoadMacroConfigures();
if (BuilderUtility.EnableLogLevel.TryParse<BuilderUtility.EnableLogLevel>(args, out var plat))
{
BuilderUtility.RefreshLogLevelMacro(ref _macroDefines, plat);
}
}
private void SwitchScriptingBackend(string args)
{
if (!autoUdate)
return;
LoadMacroConfigures();
if (BuilderUtility.ScriptBackend.TryParse<BuilderUtility.ScriptBackend>(args, out var scriptBackend))
{
BuilderUtility.RefreshBackendMacro(ref _macroDefines, scriptBackend);
}
}
/// <summary>
/// 更改打包方式Editor、Android、IOS
/// </summary>
/// <param name="args"></param>
internal void SwitchBuildType(string args)
{
if (!autoUdate)
return;
LoadMacroConfigures();
if (BuilderUtility.BuildType.TryParse<BuilderUtility.BuildType>(args, out var plat))
{
BuilderUtility.SetBuildTypeMacroDefines(ref _macroDefines, plat);
}
}
private void EnableProfiler(string args)
{
if (!autoUdate)
return;
bool enable = args.Equals("1");
EditorUserBuildSettings.connectProfiler = enable;
}
private void EnableDeepProfiler(string args)
{
if (!autoUdate)
return;
bool enable = args.Equals("1");
#if UNITY_2019_1_OR_NEWER
EditorUserBuildSettings.buildWithDeepProfilingSupport = enable;
#endif
}
private void EnableExportAndroidProject(string args)
{
if (!autoUdate)
return;
bool enable = args.Equals("1");
EditorUserBuildSettings.exportAsGoogleAndroidProject = enable;
}
private void EnableGM(string args)
{
if (!autoUdate)
return;
LoadMacroConfigures();
BuilderUtility.EnableGMSymbols(ref _macroDefines, args.Equals("1"));
}
/// <summary>
/// 忽略首包解压
/// </summary>
/// <param name="args"></param>
private void EnableIgnoreFirstZip(string args)
{
if (!autoUdate)
return;
LoadMacroConfigures();
BuilderUtility.IgnorFirstZip(ref _macroDefines, args.Equals("1"));
}
/// <summary>
/// 策略快查
/// </summary>
/// <param name="args"></param>
private void DoCheckPolicy(string args)
{
BuilderUtility.PolicyEasyCheck(builderBundlePolicy, bundleConfig);
}
/// <summary>
/// 更改包名
/// </summary>
/// <param name="args"></param>
private void ChangeBundleIdentifier(string args)
{
if (!autoUdate)
return;
PlayerSettings.SetApplicationIdentifier(EditorUserBuildSettings.selectedBuildTargetGroup, args);
}
/// <summary>
/// 更改产品名
/// </summary>
/// <param name="args"></param>
private void ChangeProductName(string args)
{
if (!autoUdate)
return;
PlayerSettings.productName = args;
}
/// <summary>
/// 更改版本号
/// </summary>
/// <param name="args"></param>
private void ChangeBundleVersion(string args)
{
if (!autoUdate)
return;
PlayerSettings.bundleVersion = args;
}
/// <summary>
/// 更改AB版本号
/// </summary>
/// <param name="args"></param>
private void ChangeABVersion(string args)
{
if (!autoUdate)
return;
GameConfig.Instance.WriteResVersion(args);
}
/// <summary>
/// 应用商店地址
/// </summary>
/// <param name="args"></param>
private void ChangeAppUrl(string args)
{
BuilderUtility.SetAppURL(args);
}
private void SwitchApplyArgs(string args)
{
if (args.Equals("1"))
ApplyArgs("");
}
/// <summary>
/// 统一应用参数
/// </summary>
internal void ApplyArgs(string args)
{
LoadMacroConfigures();
BuilderUtility.ClearMacros(ref _macroDefines, false);
//分平台关闭开关
switch (buildType)
{
case BuilderUtility.BuildType.Editor:
break;
case BuilderUtility.BuildType.Release:
{
//关闭Profiler
bEnableProfiler = false;
#if UNITY_2019_1_OR_NEWER
bEnableDeepProfiler = false;
#endif
//禁止增量打包
bIncrementBuildAB = false;
}
break;
case BuilderUtility.BuildType.Development:
break;
}
if (!bExportAndroidProject)
{
EditorUserBuildSettings.exportAsGoogleAndroidProject = false;
}
//加密部分
if (!bEnableEncrypt)
{
//bEnableLuaEncrypt = false;
}
//宏部分
BuilderUtility.RefreshLogLevelMacro(ref _macroDefines, enableLogLevel, false);
BuilderUtility.RefreshBackendMacro(ref _macroDefines, scriptingBackend, false);
BuilderUtility.EnableGMSymbols(ref _macroDefines, bEnableGM, false);
//必须最后一个调用
BuilderUtility.SetMacroDefines(ref _macroDefines, buildType, false);
//Setting部分
EditorUserBuildSettings.connectProfiler = bEnableProfiler;
EditorUserBuildSettings.exportAsGoogleAndroidProject = bExportAndroidProject;
#if UNITY_2019_1_OR_NEWER
EditorUserBuildSettings.buildWithDeepProfilingSupport = bEnableDeepProfiler;
#endif
//参数部分
PlayerSettings.SetApplicationIdentifier(EditorUserBuildSettings.selectedBuildTargetGroup, bundleIdentifier);
PlayerSettings.productName = productName;
PlayerSettings.bundleVersion = bundleVersion;
BuilderUtility.UpdateMacros(_macroDefines);
hasSave = true;
Save();
AssetDatabase.Refresh();
}
private void GenMd5(string args)
{
if (EditorApplication.isCompiling)
{
EditorUtility.DisplayDialog("Build AssetBundle", "请等待编译完成", "ok");
return;
}
TEngineEditorUtil.GenMd5List();
GUIUtility.ExitGUI();
AssetDatabase.Refresh();
}
private void BuildAssetBundle(string args)
{
if (EditorApplication.isCompiling)
{
EditorUtility.DisplayDialog("Build AssetBundle", "请等待编译完成", "ok");
return;
}
ApplyArgs("");
if (!BuilderUtility.PolicyEasyCheck(builderBundlePolicy, bundleConfig))
{
if (!EditorUtility.DisplayDialog("资源检查警告", "发现策略未覆盖到的资源,是否继续", "继续", "退出打包"))
{
return;
}
}
Builder.Instance.SetBuilderConfig(this);
Builder.Instance.BuildAssetBundle();
GUIUtility.ExitGUI();
}
public static void CopyFilefolder(string sourceFilePath, string targetFilePath)
{
string[] files = Directory.GetFiles(sourceFilePath);
string fileName;
string destFile;
if (!Directory.Exists(targetFilePath))
{
Directory.CreateDirectory(targetFilePath);
}
foreach (string s in files)
{
fileName = Path.GetFileName(s);
destFile = Path.Combine(targetFilePath, fileName);
File.Copy(s, destFile, true);
}
string[] filefolders = Directory.GetFiles(sourceFilePath);
DirectoryInfo dirinfo = new DirectoryInfo(sourceFilePath);
DirectoryInfo[] subFileFolder = dirinfo.GetDirectories();
for (int j = 0; j < subFileFolder.Length; j++)
{
string subSourcePath = sourceFilePath + "\\" + subFileFolder[j].ToString();
string subTargetPath = targetFilePath + "\\" + subFileFolder[j].ToString();
CopyFilefolder(subSourcePath, subTargetPath);
}
}
public bool FileRename(string sourceFile, string destinationPath, string destinationFileName)
{
FileInfo tempFileInfo;
FileInfo tempBakFileInfo;
DirectoryInfo tempDirectoryInfo;
tempFileInfo = new FileInfo(sourceFile);
tempDirectoryInfo = new DirectoryInfo(destinationPath);
tempBakFileInfo = new FileInfo(destinationPath + "\\" + destinationFileName);
try
{
if (!tempDirectoryInfo.Exists)
{
tempDirectoryInfo.Create();
}
if (tempBakFileInfo.Exists)
{
tempBakFileInfo.Delete();
}
tempFileInfo.MoveTo(destinationPath + "\\" + destinationFileName);
return true;
}
catch (Exception ex)
{
TLogger.LogError(ex.Message);
return false;
}
}
private void CopyDLL(string args)
{
if (EditorApplication.isCompiling)
{
EditorUtility.DisplayDialog("Build AssetBundle", "请等待编译完成", "ok");
return;
}
ApplyArgs("");
if (File.Exists(Application.dataPath + $"\\TResources\\DLL\\{Constant.Setting.HotFixedDllName}.dll.bytes"))
{
TLogger.LogWarning("存在DLL的bytes 执行删除操作");
FileInfo del = new FileInfo(Application.dataPath + $"\\TResources\\DLL\\{Constant.Setting.HotFixedDllName}.dll.bytes");
del.Delete();
}
else
{
TLogger.LogInfo("不存在DLL的bytes 直接拷贝加密");
}
FileInfo fi = new FileInfo(Application.dataPath + $"/DLL/{Constant.Setting.HotFixedDllName}.dll");
fi.CopyTo(Path.Combine(Path.GetDirectoryName(Application.dataPath + "\\TResources\\DLL\\"), $"{Constant.Setting.HotFixedDllName}.dll.bytes"));
TLogger.LogInfoSuccessd("拷贝加密DLL的bytes成功");
AssetDatabase.Refresh();
GUIUtility.ExitGUI();
}
private void BuildApk(string args)
{
if (EditorApplication.isCompiling)
{
EditorUtility.DisplayDialog("Build Apk", "请等待编译完成", "ok");
return;
}
Save();
if (bundleVersion.Split('.').Length != 2)
{
Debug.LogError("版本号需要两位(*.*");
return;
}
if (buildType == (BuilderUtility.BuildType)BuilderUtility.BuildType.Editor)
{
Debug.LogError("编辑器模式不支持打包,请看描述");
}
else
{
if (!BuilderUtility.PolicyEasyCheck(builderBundlePolicy, bundleConfig))
{
if (!EditorUtility.DisplayDialog("资源检查警告", "发现策略未覆盖到的资源,是否继续", "继续", "退出打包"))
{
return;
}
}
ApplyArgs("");
Builder.Instance.SetBuilderConfig(this);
Builder.Instance.Build(false);
}
GUIUtility.ExitGUI();
}
/// <summary>
/// 直接打APK包
/// </summary>
/// <param name="args"></param>
private void DirectBuildApk(string args)
{
if (EditorApplication.isCompiling)
{
EditorUtility.DisplayDialog("Direct Build Apk", "请等待编译完成", "ok");
return;
}
Save();
if (bundleVersion.Split('.').Length != 2)
{
Debug.LogError("版本号需要两位(*.*");
return;
}
if (buildType == (BuilderUtility.BuildType)BuilderUtility.BuildType.Editor)
{
Debug.LogError("编辑器模式不支持打包,请看描述");
}
else
{
if (!Directory.Exists(FileSystem.AssetBundleBuildPath) ||
Directory.GetFileSystemEntries(FileSystem.AssetBundleBuildPath).Length <= 0)
{
Debug.LogWarning("未打包assetbundle资源");
}
ApplyArgs("");
Builder.Instance.SetBuilderConfig(this);
Builder.Instance.Build(true);
}
GUIUtility.ExitGUI();
}
internal void SwitchSymbolWinLayout(string args)
{
if (!args.Equals("Const"))
{
if (_switchSymbolWinL == 0)
{
_switchSymbolWinL = 1;
}
else
{
_switchSymbolWinL = 0;
}
}
if (_switchSymbolWinL == 1)
symbolWin = _macroDefines.Replace(";", "------");
else
symbolWin = _macroDefines.Replace(";", "\n");
}
/// <summary>
/// 保存数据
/// </summary>
internal void Save()
{
EditorUtility.SetDirty(this);
AssetDatabase.SaveAssets();
}
[MenuItem("TEngine/A B 配置|Create Asset Bundle Config", priority = 1500)]
internal static void OpenBuilder()
{
bool isExists = false;
string[] assetGuids = AssetDatabase.FindAssets("t:BuilderEditor");
for (int i = 0, len = assetGuids.Length; i < len; i++)
{
string assetPath = AssetDatabase.GUIDToAssetPath(assetGuids[i]);
if (assetPath.EndsWith(".asset"))
{
Type type = AssetDatabase.GetMainAssetTypeAtPath(assetPath);
if (type == typeof(BuilderEditor) && !assetPath.StartsWith("Packages/"))
{
isExists = true;
string directory = assetPath.Substring(0, assetPath.LastIndexOf('/'));
EditorGUIUtility.PingObject(AssetDatabase.LoadAssetAtPath<Object>(directory));
break;
}
}
}
if (!isExists)
{
if (!EditorUtility.DisplayDialog("创建打包配置定位失败", "是否自动创建默认为Release", "继续", "退出"))
return;
string androidDir = "Assets/BuildConfig/Editor" + "/Android";
if (!Directory.Exists(androidDir))
Directory.CreateDirectory(androidDir);
string iosDir = "Assets/BuildConfig/Editor" + "/iOS";
if (!Directory.Exists(iosDir))
Directory.CreateDirectory(iosDir);
AssetDatabase.Refresh();
string path = androidDir + "/Android_Release.asset";
int index = 1;
while (File.Exists(path))
{
path = androidDir + $"/Android_Release_{index++}.asset";
}
BuilderEditor builder = CreateInstance<BuilderEditor>();
AssetDatabase.CreateAsset(builder, path);
EditorGUIUtility.PingObject(builder);
AssetDatabase.OpenAsset(builder);
}
}
internal void OnInit()
{
LoadMacroConfigures();
if (autoUdate)
MacroInit();
}
/// <summary>
/// 宏初始化
/// </summary>
internal void MacroInit()
{
ApplyArgs("");
}
#region
private static string _macroDefines = "";
internal static void LoadMacroConfigures()
{
switch (EditorUserBuildSettings.activeBuildTarget)
{
case BuildTarget.StandaloneWindows64:
case BuildTarget.StandaloneWindows:
_macroDefines = PlayerSettings.GetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone);
EditorUserBuildSettings.selectedBuildTargetGroup = BuildTargetGroup.Standalone;
break;
case BuildTarget.StandaloneOSX:
_macroDefines = PlayerSettings.GetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone);
EditorUserBuildSettings.selectedBuildTargetGroup = BuildTargetGroup.Standalone;
break;
case BuildTarget.Android:
_macroDefines = PlayerSettings.GetScriptingDefineSymbolsForGroup(BuildTargetGroup.Android);
EditorUserBuildSettings.selectedBuildTargetGroup = BuildTargetGroup.Android;
break;
case BuildTarget.iOS:
_macroDefines = PlayerSettings.GetScriptingDefineSymbolsForGroup(BuildTargetGroup.iOS);
EditorUserBuildSettings.selectedBuildTargetGroup = BuildTargetGroup.iOS;
break;
}
}
#endregion
internal enum BuilderDisplayType
{
,
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9b3e95ad218591b42a781ca61fd7e22e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,83 @@
using System;
using UnityEngine;
namespace TEngineCore.Editor
{
internal class BuilderEditorAttribute : Attribute
{
public EditorContent Content;
public BuilderEditorAttribute(string title)
{
Content = new EditorContent();
Content.Title = title;
}
public BuilderEditorAttribute(string title, ContentType component, string extra)
{
Content = new EditorContent();
Content.Title = title;
Content.Component = component;
Content.Extra = extra;
}
public BuilderEditorAttribute(string title, ContentType component)
{
Content = new EditorContent();
Content.Title = title;
Content.Component = component;
Content.Extra = "";
}
}
internal enum ContentType
{
Space,
Button,
Label,
SelectableLabel,
Enum,
Toggle,
TextField,
Obj,
ScrollLabel,
}
internal class EditorContent
{
public const string HorizontalStart = "HorizontalStart";
public const string HorizontalEnd = "HorizontalEnd";
public string Title;
public ContentType Component;
public string Extra;
public GUIStyle EditorStyles = default;
public GUILayoutOption[] LayoutOptions = null;
public string FieldName;
public Type Type;
public string[] EnumOptions;
public bool IsShow = true;
public Vector3 ScrollPos;
}
internal class ContentLayout
{
[NonSerialized]
public GUIStyle EditorStyles = default;
[NonSerialized]
public GUILayoutOption[] LayoutOptions = null;
public ContentLayout(params GUILayoutOption[] layoutOptions)
{
LayoutOptions = layoutOptions;
}
public void SetStyles(GUIStyle style)
{
EditorStyles = style;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6bfaf12499eab7344925c43fff46ed19
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,18 @@
using UnityEditor;
namespace TEngineCore.Editor
{
[CustomEditor(typeof(BuilderEditor))]
public class BuilderInspector : InspectorBase
{
private BuilderEditor _target;
protected override void OnEnable()
{
base.OnEnable();
_target = target as BuilderEditor;
if (_target != null)
_target.OnInit();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dcbf4b4324520da4eb6d97c19cb3b80d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,453 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using TEngineCore.Editor;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;
namespace TEngineCore.Editor
{
public class InspectorBase : UnityEditor.Editor
{
private Dictionary<string, SerializedProperty> _serializedProperties = new Dictionary<string, SerializedProperty>();
List<EditorContent> coms = new List<EditorContent>();
private Dictionary<string, EditorContent> _comDic = new Dictionary<string, EditorContent>();
private SoEditorBase _baseTarget;
private bool _hasDispose;
protected virtual void OnEnable()
{
SetTarget((SoEditorBase)serializedObject.targetObject);
}
protected void SetTarget(SoEditorBase target)
{
_baseTarget = target;
}
void Init()
{
_baseTarget.AdditionalInit();
var type = _baseTarget.GetType().GetTypeInfo();
_serializedProperties = new Dictionary<string, SerializedProperty>();
ContentLayout layout = null;
foreach (var member in type.DeclaredMembers)
{
var fie = type.GetDeclaredField(member.Name);
if (fie != null)
{
if (fie.FieldType.IsAssignableFrom(typeof(ContentLayout)))
{
layout = (ContentLayout)fie.GetValue(_baseTarget);
coms[coms.Count - 1].EditorStyles = layout.EditorStyles;
coms[coms.Count - 1].LayoutOptions = layout.LayoutOptions;
layout = null;
}
foreach (var att in member.GetCustomAttributes(false))
{
if (att is BuilderEditorAttribute a)
{
if (a.Content != null)
{
_serializedProperties.Add(member.Name, serializedObject.FindProperty(member.Name));
coms.Add(new EditorContent()
{
Title = a.Content.Title,
Component = a.Content.Component,
Extra = a.Content.Extra,
FieldName = member.Name,
});
coms[coms.Count - 1].Type = type.GetDeclaredField(member.Name).FieldType;
if (coms[coms.Count - 1].Type.IsSubclassOf(typeof(Enum)))
{
List<string> enums = new List<string>();
var box = coms[coms.Count - 1].Type.GetTypeInfo().DeclaredMembers;
bool skipFirst = true;
foreach (var field in box)
{
if (skipFirst)
{
skipFirst = false;
continue;
}
enums.Add(field.Name);
}
coms[coms.Count - 1].EnumOptions = enums.ToArray();
continue;
}
if (!coms[coms.Count - 1].Type.IsSubclassOf(typeof(Enum)) && a.Content.Component == ContentType.Enum)
{
Debug.LogError(coms[coms.Count - 1].FieldName + "的类型非枚举! 已剔除");
coms.RemoveAt(coms.Count - 1);
}
}
}
}
}
}
foreach (var com in coms)
{
_comDic.Add(com.FieldName, com);
}
}
private int saveCount = 0;
private const int saveCriticalPoint = 1000;
private bool needSave = false;
private bool canDraw = false;
public override void OnInspectorGUI()
{
serializedObject.Update();
if (coms == null || coms.Count == 0)
{
Init();
return;
}
if (CheckIfDispose())
return;
needSave = false;
for (int i = 0; i < coms.Count; ++i)
{
var extra = coms[i].Extra.Split(',');
bool canShow = true;
string cbName = null;
for (int j = 0; j < extra.Length; ++j)
{
if (extra[j].StartsWith("Hori"))
continue;
if (extra[j].StartsWith("CB:"))
{
cbName = extra[j].Substring(3);
}
else if (extra[j].StartsWith("Flow"))
{
if (!canShow)
continue;
bool isFlowShow = extra[j].StartsWith("FlowS");
extra[j] = extra[j].Substring(6);
if (extra[j].Contains("|"))
{
var parents = extra[j].Split('|');
foreach (var parentInfo in parents)
{
canShow = CheckIfCanShowByParentInfo(parentInfo, isFlowShow);
if (canShow)
break;
}
}
else
{
var parents = extra[j].Split('&');
foreach (var parentInfo in parents)
{
canShow = CheckIfCanShowByParentInfo(parentInfo, isFlowShow);
if (!canShow)
break;
}
}
}
}
coms[i].IsShow = canShow;
if (UnityEngine.Event.current.type == EventType.Layout)
{
canDraw = true;
}
if (!canDraw)
return;
bool horizontalStart = extra.Length > 0 && extra[0].Equals(EditorContent.HorizontalStart);
bool horizontalEnd = extra.Length > 0 && extra[0].Equals(EditorContent.HorizontalEnd);
if (horizontalStart)
EditorGUILayout.BeginHorizontal();
if (canShow)
{
DisplayComponets(i, cbName, ref needSave);
}
if (horizontalEnd)
EditorGUILayout.EndHorizontal();
if (canShow)
GUILayout.Space(5);
}
saveCount++;
if (needSave)
{
FunctionCall("OnAnyThingChange", "");
serializedObject.ApplyModifiedProperties();
AssetDatabase.SaveAssets();
}
}
private void DisplayComponets(int i, string cbName, ref bool needSave)
{
switch (coms[i].Component)
{
case ContentType.Space:
float space = (float)Convert.ToDouble(coms[i].Title);
GUILayout.Space(space);
break;
case ContentType.Label:
string valueLabel = coms[i].Title;
if (string.IsNullOrEmpty(valueLabel))
{
valueLabel = _serializedProperties[coms[i].FieldName].stringValue;
}
if (coms[i].EditorStyles != null && coms[i].LayoutOptions != null)
GUILayout.Label(valueLabel, coms[i].EditorStyles, coms[i].LayoutOptions);
else if (coms[i].EditorStyles != null)
GUILayout.Label(valueLabel, coms[i].EditorStyles);
else if (coms[i].LayoutOptions != null)
GUILayout.Label(valueLabel, coms[i].LayoutOptions);
else
GUILayout.Label(valueLabel);
break;
case ContentType.SelectableLabel:
string valueSLabel = coms[i].Title;
if (string.IsNullOrEmpty(valueSLabel))
{
valueSLabel = _serializedProperties[coms[i].FieldName].stringValue;
}
if (coms[i].EditorStyles != null && coms[i].LayoutOptions != null)
EditorGUILayout.SelectableLabel(valueSLabel, coms[i].EditorStyles, coms[i].LayoutOptions);
else if (coms[i].EditorStyles != null)
EditorGUILayout.SelectableLabel(valueSLabel, coms[i].EditorStyles);
else if (coms[i].LayoutOptions != null)
EditorGUILayout.SelectableLabel(valueSLabel, coms[i].LayoutOptions);
else
EditorGUILayout.SelectableLabel(valueSLabel);
break;
case ContentType.Button:
bool click = false;
if (coms[i].EditorStyles != null && coms[i].LayoutOptions != null)
click = GUILayout.Button(coms[i].Title, coms[i].EditorStyles, coms[i].LayoutOptions);
else if (coms[i].EditorStyles != null)
click = GUILayout.Button(coms[i].Title, coms[i].EditorStyles);
else if (coms[i].LayoutOptions != null)
click = GUILayout.Button(coms[i].Title, coms[i].LayoutOptions);
else
click = GUILayout.Button(coms[i].Title);
if (click)
{
//通知
if (!string.IsNullOrEmpty(cbName))
FunctionCall(cbName, "Button");
}
break;
case ContentType.Enum:
EditorGUI.BeginChangeCheck();
if (coms[i].EditorStyles != null && coms[i].LayoutOptions != null)
_serializedProperties[coms[i].FieldName].intValue = EditorGUILayout.Popup(coms[i].Title, _serializedProperties[coms[i].FieldName].intValue, coms[i].EnumOptions, coms[i].EditorStyles, coms[i].LayoutOptions);
else if (coms[i].EditorStyles != null)
_serializedProperties[coms[i].FieldName].intValue = EditorGUILayout.Popup(coms[i].Title, _serializedProperties[coms[i].FieldName].intValue, coms[i].EnumOptions, coms[i].EditorStyles);
else if (coms[i].LayoutOptions != null)
_serializedProperties[coms[i].FieldName].intValue = EditorGUILayout.Popup(coms[i].Title, _serializedProperties[coms[i].FieldName].intValue, coms[i].EnumOptions, coms[i].LayoutOptions);
else
_serializedProperties[coms[i].FieldName].intValue = EditorGUILayout.Popup(coms[i].Title, _serializedProperties[coms[i].FieldName].intValue, coms[i].EnumOptions);
if (EditorGUI.EndChangeCheck())
{
serializedObject.ApplyModifiedProperties();
needSave = true;
//通知
if (!string.IsNullOrEmpty(cbName))
FunctionCall(cbName, coms[i].EnumOptions[_serializedProperties[coms[i].FieldName].enumValueIndex]);
}
break;
case ContentType.Toggle:
EditorGUI.BeginChangeCheck();
if (coms[i].EditorStyles != null && coms[i].LayoutOptions != null)
_serializedProperties[coms[i].FieldName].boolValue = EditorGUILayout.Toggle(coms[i].Title, _serializedProperties[coms[i].FieldName].boolValue, coms[i].EditorStyles, coms[i].LayoutOptions);
else if (coms[i].EditorStyles != null)
_serializedProperties[coms[i].FieldName].boolValue = EditorGUILayout.Toggle(coms[i].Title, _serializedProperties[coms[i].FieldName].boolValue, coms[i].EditorStyles);
else if (coms[i].LayoutOptions != null)
_serializedProperties[coms[i].FieldName].boolValue = EditorGUILayout.Toggle(coms[i].Title, _serializedProperties[coms[i].FieldName].boolValue, coms[i].LayoutOptions);
else
_serializedProperties[coms[i].FieldName].boolValue = EditorGUILayout.Toggle(coms[i].Title, _serializedProperties[coms[i].FieldName].boolValue);
if (EditorGUI.EndChangeCheck())
{
serializedObject.ApplyModifiedProperties();
needSave = true;
//通知
if (!string.IsNullOrEmpty(cbName))
FunctionCall(cbName, _serializedProperties[coms[i].FieldName].boolValue ? "1" : "0");
}
break;
case ContentType.TextField:
EditorGUI.BeginChangeCheck();
if (coms[i].EditorStyles != null && coms[i].LayoutOptions != null)
_serializedProperties[coms[i].FieldName].stringValue = EditorGUILayout.TextField(coms[i].Title, _serializedProperties[coms[i].FieldName].stringValue, coms[i].EditorStyles, coms[i].LayoutOptions);
else if (coms[i].EditorStyles != null)
_serializedProperties[coms[i].FieldName].stringValue = EditorGUILayout.TextField(coms[i].Title, _serializedProperties[coms[i].FieldName].stringValue, coms[i].EditorStyles);
else if (coms[i].LayoutOptions != null)
_serializedProperties[coms[i].FieldName].stringValue = EditorGUILayout.TextField(coms[i].Title, _serializedProperties[coms[i].FieldName].stringValue, coms[i].LayoutOptions);
else
_serializedProperties[coms[i].FieldName].stringValue = EditorGUILayout.TextField(coms[i].Title, _serializedProperties[coms[i].FieldName].stringValue);
if (EditorGUI.EndChangeCheck())
{
serializedObject.ApplyModifiedProperties();
needSave = true;
//通知
if (!string.IsNullOrEmpty(cbName))
FunctionCall(cbName, _serializedProperties[coms[i].FieldName].stringValue);
}
break;
case ContentType.Obj:
EditorGUI.BeginChangeCheck();
Object obj = null;
if (_serializedProperties[coms[i].FieldName] != null)
obj = _serializedProperties[coms[i].FieldName].objectReferenceValue;
if (coms[i].LayoutOptions != null)
_serializedProperties[coms[i].FieldName].objectReferenceValue =
EditorGUILayout.ObjectField(coms[i].Title, _serializedProperties[coms[i].FieldName].objectReferenceValue, coms[i].Type, false, coms[i].LayoutOptions);
else
obj = EditorGUILayout.ObjectField(coms[i].Title, obj, coms[i].Type, false);
if (obj != null)
{
_serializedProperties[coms[i].FieldName].objectReferenceValue = obj;
}
if (EditorGUI.EndChangeCheck())
{
serializedObject.ApplyModifiedProperties();
needSave = true;
//通知
if (!string.IsNullOrEmpty(cbName))
FunctionCall(cbName, _serializedProperties[coms[i].FieldName].stringValue);
}
break;
case ContentType.ScrollLabel:
string valueSCLabel = coms[i].Title;
if (string.IsNullOrEmpty(valueSCLabel))
{
valueSCLabel = _serializedProperties[coms[i].FieldName].stringValue;
}
if (coms[i].LayoutOptions != null)
coms[i].ScrollPos = GUILayout.BeginScrollView(coms[i].ScrollPos, false, false, coms[i].LayoutOptions);
else
coms[i].ScrollPos = GUILayout.BeginScrollView(coms[i].ScrollPos, false, false);
if (coms[i].EditorStyles != null && coms[i].LayoutOptions != null)
{
GUILayout.Label(valueSCLabel, coms[i].EditorStyles, coms[i].LayoutOptions);
}
else if (coms[i].EditorStyles != null)
GUILayout.Label(valueSCLabel, coms[i].EditorStyles);
else if (coms[i].LayoutOptions != null)
GUILayout.Label(valueSCLabel, coms[i].LayoutOptions);
else
GUILayout.Label(valueSCLabel);
GUILayout.EndScrollView();
break;
default:
Debug.LogError("请勿使用不支持的类型,字段:" + coms[i].FieldName);
break;
}
}
private void FunctionCall(string cbName, string parameter)
{
if (_baseTarget != null)
{
var ty = _baseTarget.GetType();
var method = ty.GetTypeInfo().GetDeclaredMethod(cbName);
if (method != null)
{
var parameters = new object[] { parameter };
method.Invoke(_baseTarget, parameters);
}
}
}
private bool CheckIfCanShowByParentInfo(string parentInfo, bool checkShow)
{
if (CheckIfDispose())
return false;
var p2v = parentInfo.Split(':');
if (p2v.Length < 2)
{
Debug.LogError("不规范的输入,参数后需包含 :值’ :" + parentInfo);
return false;
}
if (_comDic.TryGetValue(p2v[0], out var parent))
{
if (!checkShow)
{
var or = p2v[1].Split('r');
foreach (var index in or)
{
if (index.Equals(_serializedProperties[p2v[0]].intValue.ToString()))
return true;
}
}
else
{
return parent.IsShow;
}
}
return false;
}
bool CheckIfDispose()
{
if (_hasDispose)
{
Selection.activeObject = null;
}
return _hasDispose;
}
private void OnValidate()
{
serializedObject.ApplyModifiedProperties();
AssetDatabase.SaveAssets();
}
void OnDisable()
{
_hasDispose = true;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 69c90344d16f40941a5f3d724c6ffa4f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,16 @@
using UnityEngine;
namespace TEngineCore.Editor
{
public class SoEditorBase : ScriptableObject
{
public virtual void AdditionalInit()
{
}
public virtual void OnAnyThingChange(string args)
{
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e2c6ea7ddafc411449933609a046e5df
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,247 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEngine;
namespace TEngineCore.Editor
{
public class FileTree : IDisposable
{
private bool _disposed = false;
private string[] _extIncludeFilter;
private string[] _extExcludeFilter;
private List<FileInfo> _files = new List<FileInfo>();
private DirectoryInfo[] _subFolders;
private DirectoryInfo _directoryInfo;
private FileTree[] _subTrees;
private bool _isLeafFolder;
private FileTree _parent;
public string Name
{
get
{
return _directoryInfo.Name;
}
}
public void Dispose()
{
if (!_disposed)
{
_disposed = true;
if (null != _subTrees)
{
for (var i = 0; i < _subTrees.Length; ++i)
{
_subTrees[i].Dispose();
}
_subTrees = null;
}
}
}
~FileTree()
{
Dispose();
}
private FileTree() { }
public static FileTree CreateWithIncludeFilter(string rootPath, string[] includeFilter = null)
{
FileTree fileTree = new FileTree();
fileTree._extIncludeFilter = includeFilter;
fileTree._directoryInfo = new DirectoryInfo(rootPath);
fileTree.MakeSubTree();
return fileTree;
}
public static FileTree CreateWithExcludeFilter(string rootPath, string[] excludeFilter = null)
{
FileTree fileTree = new FileTree();
fileTree._extExcludeFilter = excludeFilter;
fileTree._directoryInfo = new DirectoryInfo(rootPath);
fileTree.MakeSubTree();
return fileTree;
}
public static FileTree CreateWithIncludeFilter(DirectoryInfo directoryInfo, string[] includeFilter = null)
{
if (directoryInfo != null)
{
FileTree fileTree = new FileTree();
fileTree._extIncludeFilter = includeFilter;
fileTree._directoryInfo = directoryInfo;
fileTree.MakeSubTree();
return fileTree;
}
else
{
Debug.LogError("Create FileTree with null DirectoryInfo");
return null;
}
}
public static FileTree CreateWithExcludeFilter(DirectoryInfo directoryInfo, string[] excludeFilter = null)
{
if (directoryInfo != null)
{
FileTree fileTree = new FileTree();
fileTree._extExcludeFilter = excludeFilter;
fileTree._directoryInfo = directoryInfo;
fileTree.MakeSubTree();
return fileTree;
}
else
{
Debug.LogError("Create FileTree with null DirectoryInfo");
return null;
}
}
private void FiltFiles(FileInfo[] vFiles)
{
_files.Clear();
FileInfo curFileInfo;
for (var i = 0; i < vFiles.Length; ++i)
{
curFileInfo = vFiles[i];
if (_extIncludeFilter != null)
{
for (int f = 0; f < _extIncludeFilter.Length; ++f)
{
if (_extIncludeFilter[f] == curFileInfo.Extension)
{
_files.Add(curFileInfo);
break;
}
}
}
else if (_extExcludeFilter != null)
{
bool filtered = false;
for (int f = 0; f < _extExcludeFilter.Length; ++f)
{
if (_extExcludeFilter[f] == curFileInfo.Extension)
filtered = true;
}
if (!filtered) this._files.Add(curFileInfo);
}
else
{
_files.Add(curFileInfo);
}
}
}
private void MakeSubTree()
{
FiltFiles(_directoryInfo.GetFiles());
_subFolders = _directoryInfo.GetDirectories();
_isLeafFolder = (_subFolders == null || _subFolders.Length == 0);
if (!_isLeafFolder)
{
_subTrees = new FileTree[_subFolders.Length];
for (var i = 0; i < _subFolders.Length; ++i)
{
var subDir = _subFolders[i];
if (_extIncludeFilter != null)
_subTrees[i] = CreateWithIncludeFilter(subDir, _extIncludeFilter);
else
_subTrees[i] = CreateWithExcludeFilter(subDir, _extExcludeFilter);
_subTrees[i]._parent = this;
}
}
}
public List<FileTree> GetTotalLeafFolders(List<FileTree> ret = null)
{
if (null == ret)
{
ret = new List<FileTree>();
}
if (_isLeafFolder)
{
ret.Add(this);
}
else
{
for (var i = 0; i < _subTrees.Length; ++i)
{
_subTrees[i].GetTotalLeafFolders(ret);
}
}
return ret;
}
public List<FileInfo> GetAllFiles(List<FileInfo> ret = null)
{
if (ret == null)
ret = new List<FileInfo>();
ret.AddRange(this._files);
if (!_isLeafFolder)
{
for (int i = 0; i < _subTrees.Length; ++i)
{
_subTrees[i].GetAllFiles(ret);
}
}
return ret;
}
/// <summary>
/// 测试接口,用来查看信息是否正确
/// </summary>
public StringBuilder GetInfo(bool printSubFolder = false, StringBuilder builder = null)
{
if (null == builder)
{
builder = new StringBuilder(1024 * 8);
}
builder.AppendLine("文件夹:" + _directoryInfo.FullName);
if (_files.Count > 0)
{
builder.AppendLine("子文件数量:" + _files.Count);
for (var i = 0; i < _files.Count; ++i)
{
builder.AppendLine("\t第" + (i + 1) + "个文件: " + _files[i].FullName);
}
}
if (_isLeafFolder)
{
builder.Append("\t\t isLeafFolder");
}
else
{
builder.AppendLine("子文件夹数量:" + _subFolders.Length);
for (var i = 0; i < _subFolders.Length; ++i)
{
builder.AppendLine("\t第" + (i + 1) + "个文件夹: " + _subFolders[i].FullName);
}
if (printSubFolder)
{
for (var i = 0; i < _subTrees.Length; ++i)
{
_subTrees[i].GetInfo(true, builder);
}
}
}
builder.AppendLine();
return builder;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 61234e3fa3cae86479930f7c7849cc42
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,15 @@
using UnityEditor;
namespace TEngineCore.Editor
{
internal interface IBuilder
{
void SetBuilderConfig(BuilderUtility.PlatformType target, string configName, string configPath = "");
void SetBuilderConfig(BuilderEditor tmpBuilder);
void SwitchPlatform(BuilderUtility.PlatformType target);
bool BuildAssetBundle();
void BuildPackage(BuildOptions option = BuildOptions.None);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ba1b14df4e3810e4fb2c5362717ce5ed
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: faf0505604af6d54fa306ce917376347
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,38 @@
using System.Collections;
using UnityEditor;
namespace TEngineCore.Editor
{
public static class EditorCoroutineExtensions
{
public static EditorCoroutines.EditorCoroutine StartCoroutine(this EditorWindow thisRef, IEnumerator coroutine)
{
return EditorCoroutines.StartCoroutine(coroutine, thisRef);
}
public static EditorCoroutines.EditorCoroutine StartCoroutine(this EditorWindow thisRef, string methodName)
{
return EditorCoroutines.StartCoroutine(methodName, thisRef);
}
public static EditorCoroutines.EditorCoroutine StartCoroutine(this EditorWindow thisRef, string methodName, object value)
{
return EditorCoroutines.StartCoroutine(methodName, value, thisRef);
}
public static void StopCoroutine(this EditorWindow thisRef, IEnumerator coroutine)
{
EditorCoroutines.StopCoroutine(coroutine, thisRef);
}
public static void StopCoroutine(this EditorWindow thisRef, string methodName)
{
EditorCoroutines.StopCoroutine(methodName, thisRef);
}
public static void StopAllCoroutines(this EditorWindow thisRef)
{
EditorCoroutines.StopAllCoroutines(thisRef);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f555afdd2ccdf224cabc9150f8d1c7e5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,435 @@
using UnityEngine;
using System.Collections;
using UnityEditor;
using System.Collections.Generic;
using System;
using System.Reflection;
using UnityEngine.Networking;
namespace TEngineCore.Editor
{
public class EditorCoroutines
{
public class EditorCoroutine
{
public ICoroutineYield currentYield = new YieldDefault();
public IEnumerator routine;
public string routineUniqueHash;
public string ownerUniqueHash;
public string MethodName = "";
public int ownerHash;
public string ownerType;
public bool finished = false;
public EditorCoroutine(IEnumerator routine, int ownerHash, string ownerType)
{
this.routine = routine;
this.ownerHash = ownerHash;
this.ownerType = ownerType;
ownerUniqueHash = ownerHash + "_" + ownerType;
if (routine != null)
{
string[] split = routine.ToString().Split('<', '>');
if (split.Length == 3)
{
this.MethodName = split[1];
}
}
routineUniqueHash = ownerHash + "_" + ownerType + "_" + MethodName;
}
public EditorCoroutine(string methodName, int ownerHash, string ownerType)
{
MethodName = methodName;
this.ownerHash = ownerHash;
this.ownerType = ownerType;
ownerUniqueHash = ownerHash + "_" + ownerType;
routineUniqueHash = ownerHash + "_" + ownerType + "_" + MethodName;
}
}
public interface ICoroutineYield
{
bool IsDone(float deltaTime);
}
struct YieldDefault : ICoroutineYield
{
public bool IsDone(float deltaTime)
{
return true;
}
}
struct YieldWaitForSeconds : ICoroutineYield
{
public float timeLeft;
public bool IsDone(float deltaTime)
{
timeLeft -= deltaTime;
return timeLeft < 0;
}
}
struct YieldCustomYieldInstruction : ICoroutineYield
{
public CustomYieldInstruction customYield;
public bool IsDone(float deltaTime)
{
return !customYield.keepWaiting;
}
}
struct YieldUnityWebRequest : ICoroutineYield
{
public UnityWebRequest unityWebRequest;
public bool IsDone(float deltaTime)
{
return unityWebRequest.isDone;
}
}
struct YieldAsync : ICoroutineYield
{
public AsyncOperation asyncOperation;
public bool IsDone(float deltaTime)
{
return asyncOperation.isDone;
}
}
struct YieldNestedCoroutine : ICoroutineYield
{
public EditorCoroutine coroutine;
public bool IsDone(float deltaTime)
{
return coroutine.finished;
}
}
static EditorCoroutines instance = null;
Dictionary<string, List<EditorCoroutine>> coroutineDict = new Dictionary<string, List<EditorCoroutine>>();
List<List<EditorCoroutine>> tempCoroutineList = new List<List<EditorCoroutine>>();
Dictionary<string, Dictionary<string, EditorCoroutine>> coroutineOwnerDict =
new Dictionary<string, Dictionary<string, EditorCoroutine>>();
DateTime previousTimeSinceStartup;
public static int Count
{
get
{
return instance != null ? instance.coroutineDict.Count : 0;
}
}
/// <summary>Starts a coroutine.</summary>
/// <param name="routine">The coroutine to start.</param>
/// <param name="thisReference">Reference to the instance of the class containing the method.</param>
public static EditorCoroutine StartCoroutine(IEnumerator routine, object thisReference)
{
CreateInstanceIfNeeded();
return instance.GoStartCoroutine(routine, thisReference);
}
/// <summary>Starts a coroutine.</summary>
/// <param name="methodName">The name of the coroutine method to start.</param>
/// <param name="thisReference">Reference to the instance of the class containing the method.</param>
public static EditorCoroutine StartCoroutine(string methodName, object thisReference)
{
return StartCoroutine(methodName, null, thisReference);
}
/// <summary>Starts a coroutine.</summary>
/// <param name="methodName">The name of the coroutine method to start.</param>
/// <param name="value">The parameter to pass to the coroutine.</param>
/// <param name="thisReference">Reference to the instance of the class containing the method.</param>
public static EditorCoroutine StartCoroutine(string methodName, object value, object thisReference)
{
MethodInfo methodInfo = thisReference.GetType()
.GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
if (methodInfo == null)
{
Debug.LogError("Coroutine '" + methodName + "' couldn't be started, the method doesn't exist!");
}
object returnValue;
if (value == null)
{
returnValue = methodInfo.Invoke(thisReference, null);
}
else
{
returnValue = methodInfo.Invoke(thisReference, new object[] { value });
}
if (returnValue is IEnumerator)
{
CreateInstanceIfNeeded();
return instance.GoStartCoroutine((IEnumerator)returnValue, thisReference);
}
else
{
Debug.LogError("Coroutine '" + methodName + "' couldn't be started, the method doesn't return an IEnumerator!");
}
return null;
}
/// <summary>Stops all coroutines being the routine running on the passed instance.</summary>
/// <param name="routine"> The coroutine to stop.</param>
/// <param name="thisReference">Reference to the instance of the class containing the method.</param>
public static void StopCoroutine(IEnumerator routine, object thisReference)
{
CreateInstanceIfNeeded();
instance.GoStopCoroutine(routine, thisReference);
}
/// <summary>
/// Stops all coroutines named methodName running on the passed instance.</summary>
/// <param name="methodName"> The name of the coroutine method to stop.</param>
/// <param name="thisReference">Reference to the instance of the class containing the method.</param>
public static void StopCoroutine(string methodName, object thisReference)
{
CreateInstanceIfNeeded();
instance.GoStopCoroutine(methodName, thisReference);
}
/// <summary>
/// Stops all coroutines running on the passed instance.</summary>
/// <param name="thisReference">Reference to the instance of the class containing the method.</param>
public static void StopAllCoroutines(object thisReference)
{
CreateInstanceIfNeeded();
instance.GoStopAllCoroutines(thisReference, true);
}
public static void StopAllCoroutines()
{
if (instance != null)
{
instance.GoStopAllCoroutines(null, false);
}
}
static void CreateInstanceIfNeeded()
{
if (instance == null)
{
instance = new EditorCoroutines();
instance.Initialize();
}
}
void Initialize()
{
previousTimeSinceStartup = DateTime.Now;
EditorApplication.update += OnUpdate;
}
void GoStopCoroutine(IEnumerator routine, object thisReference)
{
GoStopActualRoutine(CreateCoroutine(routine, thisReference));
}
void GoStopCoroutine(string methodName, object thisReference)
{
GoStopActualRoutine(CreateCoroutineFromString(methodName, thisReference));
}
void GoStopActualRoutine(EditorCoroutine routine)
{
if (coroutineDict.ContainsKey(routine.routineUniqueHash))
{
coroutineOwnerDict[routine.ownerUniqueHash].Remove(routine.routineUniqueHash);
coroutineDict.Remove(routine.routineUniqueHash);
}
}
void GoStopAllCoroutines(object thisReference, bool onlyInstance = true)
{
if (thisReference != null && onlyInstance)
{
EditorCoroutine coroutine = CreateCoroutine(null, thisReference);
if (coroutineOwnerDict.ContainsKey(coroutine.ownerUniqueHash))
{
foreach (var couple in coroutineOwnerDict[coroutine.ownerUniqueHash])
{
coroutineDict.Remove(couple.Value.routineUniqueHash);
}
coroutineOwnerDict.Remove(coroutine.ownerUniqueHash);
}
}
else
{
coroutineDict.Clear();
}
}
EditorCoroutine GoStartCoroutine(IEnumerator routine, object thisReference)
{
if (routine == null)
{
Debug.LogException(new Exception("IEnumerator is null!"), null);
}
EditorCoroutine coroutine = CreateCoroutine(routine, thisReference);
GoStartCoroutine(coroutine);
return coroutine;
}
void GoStartCoroutine(EditorCoroutine coroutine)
{
if (!coroutineDict.ContainsKey(coroutine.routineUniqueHash))
{
List<EditorCoroutine> newCoroutineList = new List<EditorCoroutine>();
coroutineDict.Add(coroutine.routineUniqueHash, newCoroutineList);
}
coroutineDict[coroutine.routineUniqueHash].Add(coroutine);
if (!coroutineOwnerDict.ContainsKey(coroutine.ownerUniqueHash))
{
Dictionary<string, EditorCoroutine> newCoroutineDict = new Dictionary<string, EditorCoroutine>();
coroutineOwnerDict.Add(coroutine.ownerUniqueHash, newCoroutineDict);
}
// If the method from the same owner has been stored before, it doesn't have to be stored anymore,
// One reference is enough in order for "StopAllCoroutines" to work
if (!coroutineOwnerDict[coroutine.ownerUniqueHash].ContainsKey(coroutine.routineUniqueHash))
{
coroutineOwnerDict[coroutine.ownerUniqueHash].Add(coroutine.routineUniqueHash, coroutine);
}
MoveNext(coroutine);
}
EditorCoroutine CreateCoroutine(IEnumerator routine, object thisReference)
{
return new EditorCoroutine(routine, thisReference.GetHashCode(), thisReference.GetType().ToString());
}
EditorCoroutine CreateCoroutineFromString(string methodName, object thisReference)
{
return new EditorCoroutine(methodName, thisReference.GetHashCode(), thisReference.GetType().ToString());
}
void OnUpdate()
{
float deltaTime = (float)(DateTime.Now.Subtract(previousTimeSinceStartup).TotalMilliseconds / 1000.0f);
previousTimeSinceStartup = DateTime.Now;
if (coroutineDict.Count == 0)
{
return;
}
tempCoroutineList.Clear();
foreach (var pair in coroutineDict)
tempCoroutineList.Add(pair.Value);
for (var i = tempCoroutineList.Count - 1; i >= 0; i--)
{
List<EditorCoroutine> coroutines = tempCoroutineList[i];
for (int j = coroutines.Count - 1; j >= 0; j--)
{
EditorCoroutine coroutine = coroutines[j];
if (!coroutine.currentYield.IsDone(deltaTime))
{
continue;
}
if (!MoveNext(coroutine))
{
coroutines.RemoveAt(j);
coroutine.currentYield = null;
coroutine.finished = true;
}
if (coroutines.Count == 0)
{
coroutineDict.Remove(coroutine.ownerUniqueHash);
}
}
}
}
static bool MoveNext(EditorCoroutine coroutine)
{
if (coroutine.routine.MoveNext())
{
return Process(coroutine);
}
return false;
}
// returns false if no next, returns true if OK
static bool Process(EditorCoroutine coroutine)
{
object current = coroutine.routine.Current;
if (current == null)
{
coroutine.currentYield = new YieldDefault();
}
else if (current is WaitForSeconds)
{
float seconds = float.Parse(GetInstanceField(typeof(WaitForSeconds), current, "m_Seconds").ToString());
coroutine.currentYield = new YieldWaitForSeconds() { timeLeft = seconds };
}
else if (current is CustomYieldInstruction)
{
coroutine.currentYield = new YieldCustomYieldInstruction()
{
customYield = current as CustomYieldInstruction
};
}
else if (current is UnityWebRequest)
{
coroutine.currentYield = new YieldUnityWebRequest() { unityWebRequest = (UnityWebRequest)current };
}
else if (current is WaitForFixedUpdate || current is WaitForEndOfFrame)
{
coroutine.currentYield = new YieldDefault();
}
else if (current is AsyncOperation)
{
coroutine.currentYield = new YieldAsync { asyncOperation = (AsyncOperation)current };
}
else if (current is EditorCoroutine)
{
coroutine.currentYield = new YieldNestedCoroutine { coroutine = (EditorCoroutine)current };
}
else
{
if (!current.GetType().IsPrimitive)
{
Debug.LogException(
new Exception("<" + coroutine.MethodName + "> yielded an unknown or unsupported type! (" + current.GetType() + ")"),
null);
}
coroutine.currentYield = new YieldDefault();
}
return true;
}
static object GetInstanceField(Type type, object instance, string fieldName)
{
BindingFlags bindFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
FieldInfo field = type.GetField(fieldName, bindFlags);
return field.GetValue(instance);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5e908baad613dc042abacf50d0a7ec51
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,483 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.Rendering;
namespace TEngineCore.Editor
{
/// <summary>
/// 用于游戏中shader自动收集变体
/// </summary>
public static class ShaderVariantCollector
{
private static UnityEngine.Object _saveAddr;
private static readonly string _buildPath = "Build";
private static readonly string _shaderAssetBundleName = "shader_variant";
private static string _variantPath = $"Assets/VariantCollection.shadervariants";
/// <summary>
/// GUI部分
/// </summary>
[MenuItem("TEngine/Shader工具|Shader/变体收集")]
private static void Open()
{
var window = EditorWindow.GetWindow(typeof(CustomMessageBox), true, "收集变体") as CustomMessageBox;
if (window == null) return;
EditorUtility.ClearProgressBar();
EditorCoroutines.EditorCoroutine it = null;
window.Info = "收集项目中使用了的变体";
window.minSize = new Vector2(600, 150);
window.maxSize = new Vector2(600, 150);
window.Show();
window.OnClose = (button, returnValue) =>
{
EditorCoroutines.StopAllCoroutines();
EditorUtility.ClearProgressBar();
};
window.OnGUIFunc = () =>
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("保存地址:", GUILayout.Width(60));
_saveAddr = EditorGUILayout.ObjectField(_saveAddr, typeof(UnityEngine.Object), true, GUILayout.Width(450));
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("开始收集变体"))
{
string tempPath = AssetDatabase.GetAssetPath(_saveAddr);
if (string.IsNullOrEmpty(tempPath))
{
Debug.LogError("请选择路径");
}
else
{
GUI.enabled = it == null;
it = window.StartCoroutine(ShaderVariantsCollector());
}
}
GUILayout.EndHorizontal();
return 0;
};
}
/// <summary>
/// 收集变体
/// </summary>
/// <returns></returns>
public static IEnumerator ShaderVariantsCollector()
{
ClearCurrentShaderVariantCollection();
var shaderCount1 = GetCurrentShaderVariantCollectionShaderCount();
var shaderVariantCount1 = GetCurrentShaderVariantCollectionVariantCount();
Debug.LogErrorFormat("{0} shaders {1} total variants.", shaderCount1, shaderVariantCount1);
yield return null;
var scenePath = _GetScenes();
if (scenePath.Count <= 0)
{
yield break;
}
var allMaterials = _CollectAllMaterials();
Debug.LogFormat("========开始遍历场景收集变体========");
//int index = 0;
foreach (var item in scenePath)
{
EditorSceneManager.OpenScene(item);
yield return null;
_CreateProxyRenderers(allMaterials);
yield return null;
var shaderCount = GetCurrentShaderVariantCollectionShaderCount();
var shaderVariantCount = GetCurrentShaderVariantCollectionVariantCount();
Debug.LogFormat("{0} shaders {1} total variants.", shaderCount, shaderVariantCount);
yield return new WaitForEndOfFrame();
}
Debug.LogFormat("========完成变体收集========");
yield return new WaitForSeconds(1);
SaveShaderVariant();
}
public static void ShaderVariantsCollector(Action action = null)
{
ClearCurrentShaderVariantCollection();
var scenePath = _GetScenes();
if (scenePath.Count <= 0)
{
return;
}
var allMaterials = _CollectAllMaterials();
Debug.LogFormat("========开始遍历场景收集变体========");
int index = 0;
foreach (var item in scenePath)
{
EditorSceneManager.OpenScene(item);
_CreateProxyRenderers(allMaterials);
var shaderCount = GetCurrentShaderVariantCollectionShaderCount();
var shaderVariantCount = GetCurrentShaderVariantCollectionVariantCount();
Debug.LogFormat("{0} shaders {1} total variants.", shaderCount, shaderVariantCount);
EditorUtility.DisplayProgressBar("进度", item, index / scenePath.Count);
}
Debug.LogFormat("========完成变体收集========");
EditorUtility.ClearProgressBar();
SaveShaderVariant();
action?.Invoke();
}
static void _CreateProxyRenderers(List<string> materials)
{
int totalMaterials = materials.Count;
var camera = GameObject.FindObjectOfType<Camera>();
if (camera == null)
{
return;
}
float aspect = camera.aspect;
float height = Mathf.Sqrt(totalMaterials / aspect) + 1;
float width = Mathf.Sqrt(totalMaterials / aspect) * aspect + 1;
float halfHeight = Mathf.CeilToInt(height / 2f);
float halfWidth = Mathf.CeilToInt(width / 2f);
camera.orthographic = true;
camera.orthographicSize = halfHeight;
camera.transform.position = new Vector3(0f, 0f, -10f);
Selection.activeGameObject = camera.gameObject;
int xMax = (int)(width - 1);
int x = 0;
int y = 0;
for (int i = 0; i < materials.Count; i++)
{
var material = AssetDatabase.LoadAssetAtPath<Material>(materials[i]);
var position = new Vector3(x - halfWidth + 1f, y - halfHeight + 1f, 0f);
CreateSphere(material, position, x, y, i);
if (x == xMax)
{
x = 0;
y++;
}
else
{
x++;
}
}
}
private static void CreateSphere(Material material, Vector3 position, int x, int y, int index)
{
var go = GameObject.CreatePrimitive(PrimitiveType.Sphere);
go.GetComponent<Renderer>().material = material;
go.transform.position = position;
go.name = string.Format("Sphere_{0}|{1}_{2}|{3}", index, x, y, material.name);
}
/// <summary>
/// 收集所有的材质
/// </summary>
/// <returns></returns>
static List<string> _CollectAllMaterials()
{
List<string> material = new List<string>();
string[] temp = AssetDatabase.FindAssets("t:material");
for (int i = 0; i < temp.Length; i++)
{
material.Add(AssetDatabase.GUIDToAssetPath(temp[i]));
}
if (material.Count <= 0)
{
Debug.LogError("未收集到材质");
}
return material;
}
/// <summary>
/// 收集场景
/// </summary>
static List<string> _GetScenes()
{
List<string> scenes = new List<string>();
string[] temp = AssetDatabase.FindAssets("t:scene");
for (int i = 0; i < temp.Length; i++)
{
string path = AssetDatabase.GUIDToAssetPath(temp[i]);
if (path.StartsWith($"Assets/"))
{
scenes.Add(path);
}
}
if (scenes.Count <= 0)
{
Debug.LogError("未收集到场景信息");
}
return scenes;
}
/// <summary>
/// 重新导入shader防止shader修改了
/// </summary>
[UnityEditor.MenuItem("TEngine/Shader工具|Shader/重新导入shader资源")]
public static void ReimportAllShaderAssets()
{
try
{
var all = ShaderUtil.GetAllShaderInfo();
for (var i = 0; i < all.Length; ++i)
{
var shader = Shader.Find(all[i].name);
if (shader == null) continue;
if (EditorUtility.DisplayCancelableProgressBar("Reimport Shader...", shader.name, (float)i / all.Length))
{
break;
}
var assetPath = AssetDatabase.GetAssetPath(shader);
//路径为空或者是内置shader就不处理了
if (string.IsNullOrEmpty(assetPath) || _IsUnityDefaultResource(assetPath)) continue;
var importer = AssetImporter.GetAtPath(assetPath) as ShaderImporter;
if (importer == null) continue;
var before = AssetDatabase.GetDependencies(assetPath);
AssetDatabase.ImportAsset(assetPath, ImportAssetOptions.DontDownloadFromCacheServer);
var after = AssetDatabase.GetDependencies(assetPath);
if (!before.SequenceEqual(after))
{
Debug.LogWarningFormat("Reimport shader: {0} for error dependencies!", shader.name);
}
}
}
finally
{
EditorUtility.ClearProgressBar();
}
}
public static HashSet<string> ClollectSharderAndVariant()
{
var shaders = new HashSet<string>();
var all = ShaderUtil.GetAllShaderInfo();
for (int i = 0; i < all.Length; i++)
{
var shader = Shader.Find(all[i].name);
var assetPath = AssetDatabase.GetAssetPath(shader);
_CollectShaderDependencies(assetPath, shaders);
}
if (File.Exists(_variantPath))
{
//AssetImporter importer = AssetImporter.GetAtPath(_variantPath);
//importer.assetBundleName = _shaderAssetBundleName;
shaders.Add(_variantPath);
}
return shaders;
}
internal static string GetShaderVariantAbName()
{
return _shaderAssetBundleName;
}
/// <summary>
/// 打包shader将shader和变体集合打包到同一个ab文件里面
/// </summary>
public static void BuildAllShaderAssets()
{
HashSet<string> shaders = ClollectSharderAndVariant();
if (shaders.Count == 0)
{
Debug.LogError("No Shader asset in project");
return;
}
string buildPath = $"{Application.dataPath}/{_buildPath}";
if (!File.Exists(buildPath))
{
Directory.CreateDirectory(buildPath);
}
BuildTarget target = BuildTarget.NoTarget;
#if UNITY_EDITOR_WIN
target = BuildTarget.StandaloneWindows64;
#endif
_BuildShaderAssetBundle(shaders.ToList(), buildPath, target);
}
/// <summary>
/// 获取shader得引用关系
/// </summary>
static void _CollectShaderDependencies(string shaderAssetPath, HashSet<string> shaderPaths)
{
if (!string.IsNullOrEmpty(shaderAssetPath) && !_IsUnityDefaultResource(shaderAssetPath))
{
var deps = AssetDatabase.GetDependencies(shaderAssetPath);
for (int j = 0; j < deps.Length; j++)
{
if (!string.IsNullOrEmpty(deps[j]) && !_IsUnityDefaultResource(deps[j]) && deps[j].EndsWith(".shader"))
{
shaderPaths.Add(deps[j]);
}
}
}
}
static void _BuildShaderAssetBundle(List<string> shader, string outputPath, BuildTarget buildTarget)
{
outputPath = $"{outputPath}/{EditorUserBuildSettings.activeBuildTarget.ToString()}";
if (!Directory.Exists(outputPath))
{
Directory.CreateDirectory(outputPath);
}
var assetBuild = new AssetBundleBuild
{
assetBundleName = _shaderAssetBundleName,
assetBundleVariant = string.Empty,
assetNames = shader.ToArray()
};
var options = BuildAssetBundleOptions.DeterministicAssetBundle | BuildAssetBundleOptions.ChunkBasedCompression;
BuildPipeline.BuildAssetBundles(outputPath, new[] { assetBuild }, options, buildTarget);
}
/// <summary>
/// 保存收集到的变体
/// </summary>
public static void SaveShaderVariant()
{
if (File.Exists(_variantPath))
{
AssetDatabase.DeleteAsset(_variantPath);
}
SaveCurrentShaderVariantCollection(_variantPath);
AssetDatabase.Refresh();
AssetDatabase.SaveAssets();
EditorUtility.ClearProgressBar();
Debug.LogError("保存成功");
}
/// <summary>
/// 是不是unity内置资源
/// </summary>
/// <param name="path">资源路径</param>
/// <returns></returns>
static bool _IsUnityDefaultResource(String path)
{
return String.IsNullOrEmpty(path) == false &&
(path == "Resources/unity_builtin_extra" ||
path == "Library/unity default resources");
}
#region
static readonly object[] BoxedEmpty = new object[] { };
public static void SaveCurrentShaderVariantCollection(String path)
{
RflxStaticCall(
typeof(ShaderUtil),
"SaveCurrentShaderVariantCollection", new object[] { path });
}
internal static void ClearCurrentShaderVariantCollection()
{
RflxStaticCall(
typeof(ShaderUtil),
"ClearCurrentShaderVariantCollection", null);
}
internal static int GetCurrentShaderVariantCollectionShaderCount()
{
var shaderCount = RflxStaticCall(
typeof(ShaderUtil),
"GetCurrentShaderVariantCollectionShaderCount", null);
return (int)shaderCount;
}
internal static int GetCurrentShaderVariantCollectionVariantCount()
{
var shaderVariantCount = RflxStaticCall(
typeof(ShaderUtil),
"GetCurrentShaderVariantCollectionVariantCount", null);
return (int)shaderVariantCount;
}
internal static object RflxStaticCall(Type type, String funcName, object[] parameters = null)
{
if (type != null)
{
var f = type.GetMethod(funcName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
if (f != null)
{
var r = f.Invoke(null, parameters ?? BoxedEmpty);
return r;
}
}
Debug.LogErrorFormat("RflxStaticCall( \"{0}\", \"{1}\", {2} ) failed!",
type != null ? type.FullName : "null", funcName, parameters ?? BoxedEmpty);
return null;
}
#endregion
#region
public class CustomMessageBox : EditorWindow
{
public delegate void OnWindowClose(int button, int returnValue);
public string Info = string.Empty;
public Func<int> OnGUIFunc;
public OnWindowClose OnClose;
public string[] Buttons = null;
public int ReturnValue;
int _CloseButton = -1;
public void OnDestroy()
{
if (OnClose != null)
{
try
{
OnClose(_CloseButton, ReturnValue);
}
catch (Exception e)
{
Debug.LogException(e);
}
}
}
public void OnGUI()
{
GUILayout.Space(10);
if (!string.IsNullOrEmpty(Info))
{
EditorGUILayout.HelpBox(Info, MessageType.None);
}
GUILayout.Space(10);
if (OnGUIFunc != null)
{
ReturnValue = OnGUIFunc();
}
}
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 71016a50ab48c6d4191338880d87f40a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,176 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using TEngine.Runtime;
using UnityEngine;
namespace TEngineCore.Editor
{
[Serializable]
public struct fileMd5
{
public string fileName;
public string md5;
public long fileSize;
}
[Serializable]
public class Serialization<T>
{
[SerializeField]
List<T> _target;
public List<T> ToList()
{
return _target;
}
public Serialization(List<T> target)
{
this._target = target;
}
}
public class TEngineEditorUtil
{
public static void GenMd5List()
{
try
{
string source = FileSystem.ResourceRootInStreamAsset;
var files = Directory.GetFiles(source, "*", SearchOption.AllDirectories);
var fileNames = new List<string>();
var fileInfos = new Dictionary<string, long>();
foreach (var file in files)
{
if (file.EndsWith(".meta"))
{
continue;
}
if (file.Contains("Md5List.json"))
{
continue;
}
if (file.Contains("version.json"))
{
continue;
}
fileNames.Add(file.Substring(source.Length + 1));
fileInfos.Add(file, GetFileSize(file));
}
GeneralMd5CheckList(source, files, fileInfos, fileNames);
}
catch (Exception e)
{
TLogger.LogError(e.ToString());
throw;
}
}
/// <summary>
/// 获取文件大小
/// </summary>
/// <param name="sFullName"></param>
/// <returns></returns>
public static long GetFileSize(string sFullName)
{
long lSize = 0;
if (File.Exists(sFullName))
{
lSize = new FileInfo(sFullName).Length;
}
return lSize;
}
/// <summary>
/// 生成md5文件列表
/// </summary>
/// <param name="source">目录</param>
/// <param name="files">文件列表</param>
/// <param name="fileList">压缩的文件列表</param>
/// <param name="fileNames">文件名字列表</param>
private static void GeneralMd5CheckList(string source, string[] files, Dictionary<string, long> fileInfos, List<string> fileNames)
{
try
{
var md5List = new List<fileMd5>();
foreach (var fileInfo in fileInfos)
{
var file = fileInfo.Key;
if (file.EndsWith(".meta") || file.EndsWith(".DS_Store"))
{
continue;
}
var md5 = GetMd5Hash(file);
var fd5 = new fileMd5
{
fileName = file.Substring(source.Length + 1).Replace('\\', '/'),
md5 = md5,
fileSize = fileInfo.Value,
};
md5List.Add(fd5);
}
var configPath = $"{source}/{"Md5List.json"}";
var stream = new FileStream(configPath, FileMode.OpenOrCreate);
var writer = new StreamWriter(stream);
writer.Write(UnityEngine.JsonUtility.ToJson(new Serialization<fileMd5>(md5List)));
writer.Flush();
writer.Dispose();
writer.Close();
fileNames.Add("Md5List.json");
}
catch (Exception e)
{
TLogger.LogError(e.ToString());
throw;
}
TLogger.LogInfoSuccessd("Gen Md5 List Success");
}
/// <summary>
/// 获取文件的md5码
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
public static string GetMd5Hash(string fileName)
{
if (!File.Exists(fileName))
{
TLogger.LogWarning($"not exit file,path:{fileName}");
return string.Empty;
}
try
{
using (FileStream file = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
MD5 md5 = new MD5CryptoServiceProvider();
byte[] retVal = md5.ComputeHash(file);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < retVal.Length; i++)
{
sb.Append(retVal[i].ToString("x2"));
}
return sb.ToString();
}
}
catch (Exception ex)
{
TLogger.LogError("GetMD5Hash() fail,error:" + ex.Message);
return string.Empty;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4562ad10f9b80534cb0e84f8b4d73c06
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 28aedf920dcf7e4488b9e9d5c928dad0
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,53 @@
using System;
using System.IO;
using UnityEngine;
namespace TEngineCore.Editor
{
public class AssetbundleEncryption
{
public static void EncryptAssetBundlesByDirectory(string bundlePath, ulong offset)
{
if (offset < 1)
{
Debug.LogWarning("无法生效的偏移值:" + offset);
return;
}
if (string.IsNullOrEmpty(bundlePath))
{
Debug.LogError("bundlePath为空");
return;
}
if (!Directory.Exists(bundlePath))
{
Debug.LogError("不存在的AB包路径" + bundlePath);
return;
}
using (FileTree fileTree =
FileTree.CreateWithExcludeFilter(bundlePath, new[] { ".manifest", ".bin" }))
{
foreach (FileInfo file in fileTree.GetAllFiles())
{
byte[] fileData = File.ReadAllBytes(file.FullName);
ulong newLength = (ulong)fileData.Length + offset;
byte[] buffer = new byte[newLength];
Array.Copy(fileData, buffer, (int)offset);
Array.Copy(fileData, 0, buffer, (int)offset, fileData.Length);
FileStream fs = File.OpenWrite(file.FullName);
fs.Write(buffer, 0, (int)newLength);
fs.Close();
Debug.Log(file.FullName);
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 23a97b273f8c4c0438c5f1e173c94c25
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0321a804bac01c64fada18ff62de3c8b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,33 @@
using TEngine.Runtime;
using UnityEditor;
using UnityEngine;
namespace TEngine.Editor
{
[CustomEditor(typeof(AssetTag), true)]
public class AssetTagEditor : UnityEditor.Editor
{
private AssetTag _target;
private void OnEnable()
{
_target = (AssetTag)target;
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.TextField("AssetPath", _target.Path);
if (GUILayout.Button("定位资源", GUILayout.Width(68)))
{
UnityEngine.Object obj = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>($"{AssetConfig.AssetRootPath}/{_target.Path}");
if (obj)
{
EditorGUIUtility.PingObject(obj);
}
}
EditorGUILayout.EndHorizontal();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ef484fe7f7264394f945fc7c90d6c7c3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,90 @@
using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
namespace TEngine.Editor
{
public class TEngineEditor
{
#if UNITY_EDITOR
internal class EditorMenus
{
[MenuItem("TEngine/打开文档|Open TEngine Document", priority = 1500)]
public static void OpenTEngineDocument()
{
Application.OpenURL("http://1.12.241.46:5000/");
}
}
public static void LoadData(string filePath, ICollection<string> data)
{
try
{
FileStream fileStream = new FileStream(filePath, FileMode.OpenOrCreate);
StreamReader streamReader = new StreamReader(fileStream);
string content = streamReader.ReadLine();
while (!string.IsNullOrEmpty(content))
{
data.Add(content);
content = streamReader.ReadLine();
}
streamReader.Close();
}
catch (Exception ex)
{
Debug.LogError("读取文件失败:" + ex);
}
}
public static void SaveData(string filePath, ICollection<string> data)
{
try
{
FileStream fileStream = new FileStream(filePath, FileMode.Create);
StreamWriter streamWriter = new StreamWriter(fileStream);
foreach (var content in data)
{
streamWriter.WriteLine(content);
}
streamWriter.Flush();
streamWriter.Close();
AssetDatabase.Refresh();
}
catch (Exception ex)
{
Debug.LogError("写入文件失败:" + ex);
}
}
[MenuItem("Assets/导出Unity资源包", false, 20)]
static void ExportPackage()
{
if (Selection.objects.Length == 0)
{
return;
}
var assetPaths = new string[Selection.objects.Length];
for (var i = 0; i < assetPaths.Length; i++)
{
assetPaths[i] = AssetDatabase.GetAssetPath(Selection.objects[i]);
}
ExportPackage(assetPaths);
}
public static void ExportPackage(string[] assetPaths)
{
var path = EditorUtility.SaveFilePanel("导出Unity资源包", "", "", "unitypackage");
if (string.IsNullOrEmpty(path))
{
return;
}
assetPaths = AssetDatabase.GetDependencies(assetPaths);
AssetDatabase.ExportPackage(assetPaths, path, ExportPackageOptions.Interactive | ExportPackageOptions.Recurse | ExportPackageOptions.IncludeDependencies);
}
#endif
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ac81bad87e606294582d5fb4e1be4814
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 93fcab8ff2625fc49b46fb54b83af21b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 454f812e3f7fc7c469cc878382e5cf00
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,57 @@
using UnityEditor;
namespace TEngine.Editor
{
/// <summary>
/// 游戏框架 Inspector 抽象类。
/// </summary>
public abstract class TEngineInspector : UnityEditor.Editor
{
private bool m_IsCompiling = false;
/// <summary>
/// 绘制事件。
/// </summary>
public override void OnInspectorGUI()
{
if (m_IsCompiling && !EditorApplication.isCompiling)
{
m_IsCompiling = false;
OnCompileComplete();
}
else if (!m_IsCompiling && EditorApplication.isCompiling)
{
m_IsCompiling = true;
OnCompileStart();
}
}
/// <summary>
/// 编译开始事件。
/// </summary>
protected virtual void OnCompileStart()
{
}
/// <summary>
/// 编译完成事件。
/// </summary>
protected virtual void OnCompileComplete()
{
}
protected bool IsPrefabInHierarchy(UnityEngine.Object obj)
{
if (obj == null)
{
return false;
}
#if UNITY_2018_3_OR_NEWER
return PrefabUtility.GetPrefabAssetType(obj) != PrefabAssetType.Regular;
#else
return PrefabUtility.GetPrefabType(obj) != PrefabType.Prefab;
#endif
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 05b15d046064d604d8cbdb1c30c2617b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,61 @@
using TEngine.Runtime;
using UnityEditor;
using UnityEngine;
namespace TEngine.Editor
{
[CustomEditor(typeof(DebuggerComponent))]
internal sealed class DebuggerComponentInspector : TEngineInspector
{
private SerializedProperty m_Skin = null;
private SerializedProperty m_ActiveWindow = null;
private SerializedProperty m_ShowFullWindow = null;
private SerializedProperty m_ConsoleWindow = null;
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
serializedObject.Update();
DebuggerComponent t = (DebuggerComponent)target;
EditorGUILayout.PropertyField(m_Skin);
if (EditorApplication.isPlaying && IsPrefabInHierarchy(t.gameObject))
{
bool activeWindow = EditorGUILayout.Toggle("Active Window", t.ActiveWindow);
if (activeWindow != t.ActiveWindow)
{
t.ActiveWindow = activeWindow;
}
}
else
{
EditorGUILayout.PropertyField(m_ActiveWindow);
}
EditorGUILayout.PropertyField(m_ShowFullWindow);
if (EditorApplication.isPlaying)
{
if (GUILayout.Button("Reset Layout"))
{
t.ResetLayout();
}
}
EditorGUILayout.PropertyField(m_ConsoleWindow, true);
serializedObject.ApplyModifiedProperties();
}
private void OnEnable()
{
m_Skin = serializedObject.FindProperty("m_Skin");
m_ActiveWindow = serializedObject.FindProperty("m_ActiveWindow");
m_ShowFullWindow = serializedObject.FindProperty("m_ShowFullWindow");
m_ConsoleWindow = serializedObject.FindProperty("m_ConsoleWindow");
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9f2b744f6280e2e4eb581bb87ead911b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,44 @@
using UnityEditor;
using TEngine.Runtime;
namespace TEngine.Editor
{
[CustomEditor(typeof(FsmManager))]
internal sealed class FsmManagerInspector : TEngineInspector
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
if (!EditorApplication.isPlaying)
{
EditorGUILayout.HelpBox("Available during runtime only.", MessageType.Info);
return;
}
FsmManager t = (FsmManager)target;
if (IsPrefabInHierarchy(t.gameObject))
{
EditorGUILayout.LabelField("FSM Count", t.Count.ToString());
FsmBase[] fsms = t.GetAllFsms();
foreach (FsmBase fsm in fsms)
{
DrawFsm(fsm);
}
}
Repaint();
}
private void OnEnable()
{
}
private void DrawFsm(FsmBase fsm)
{
EditorGUILayout.LabelField(fsm.FullName, fsm.IsRunning ? Utility.Text.Format("{0}, {1} s", fsm.CurrentStateName, fsm.CurrentStateTime.ToString("F1")) : (fsm.IsDestroyed ? "Destroyed" : "Not Running"));
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f12b5f20a2a98574796ade4b3839099e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,144 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using TEngine.Runtime;
using UnityEditor;
using UnityEngine;
namespace TEngine.Editor
{
[CustomEditor(typeof(MemoryPoolComponent))]
internal sealed class MemoryPoolComponentInspector : TEngineInspector
{
private readonly Dictionary<string, List<MemoryPoolInfo>> m_MemoryPoolInfos = new Dictionary<string, List<MemoryPoolInfo>>(StringComparer.Ordinal);
private readonly HashSet<string> m_OpenedItems = new HashSet<string>();
private SerializedProperty m_EnableStrictCheck = null;
private bool m_ShowFullClassName = false;
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
serializedObject.Update();
MemoryPoolComponent t = (MemoryPoolComponent)target;
if (EditorApplication.isPlaying && IsPrefabInHierarchy(t.gameObject))
{
bool enableStrictCheck = EditorGUILayout.Toggle("Enable Strict Check", t.EnableStrictCheck);
if (enableStrictCheck != t.EnableStrictCheck)
{
t.EnableStrictCheck = enableStrictCheck;
}
EditorGUILayout.LabelField("Memory Pool Count", MemoryPool.Count.ToString());
m_ShowFullClassName = EditorGUILayout.Toggle("Show Full Class Name", m_ShowFullClassName);
m_MemoryPoolInfos.Clear();
MemoryPoolInfo[] memoryPoolInfos = MemoryPool.GetAllMemoryPoolInfos();
foreach (MemoryPoolInfo memoryPoolInfo in memoryPoolInfos)
{
string assemblyName = memoryPoolInfo.Type.Assembly.GetName().Name;
List<MemoryPoolInfo> results = null;
if (!m_MemoryPoolInfos.TryGetValue(assemblyName, out results))
{
results = new List<MemoryPoolInfo>();
m_MemoryPoolInfos.Add(assemblyName, results);
}
results.Add(memoryPoolInfo);
}
foreach (KeyValuePair<string, List<MemoryPoolInfo>> assemblyMemoryPoolInfo in m_MemoryPoolInfos)
{
bool lastState = m_OpenedItems.Contains(assemblyMemoryPoolInfo.Key);
bool currentState = EditorGUILayout.Foldout(lastState, assemblyMemoryPoolInfo.Key);
if (currentState != lastState)
{
if (currentState)
{
m_OpenedItems.Add(assemblyMemoryPoolInfo.Key);
}
else
{
m_OpenedItems.Remove(assemblyMemoryPoolInfo.Key);
}
}
if (currentState)
{
EditorGUILayout.BeginVertical("box");
{
EditorGUILayout.LabelField(m_ShowFullClassName ? "Full Class Name" : "Class Name", "Unused\tUsing\tAcquire\tRelease\tAdd\tRemove");
assemblyMemoryPoolInfo.Value.Sort(Comparison);
foreach (MemoryPoolInfo memoryPoolInfo in assemblyMemoryPoolInfo.Value)
{
DrawMemoryPoolInfo(memoryPoolInfo);
}
if (GUILayout.Button("Export CSV Data"))
{
string exportFileName = EditorUtility.SaveFilePanel("Export CSV Data", string.Empty, Utility.Text.Format("Memory Pool Data - {0}.csv", assemblyMemoryPoolInfo.Key), string.Empty);
if (!string.IsNullOrEmpty(exportFileName))
{
try
{
int index = 0;
string[] data = new string[assemblyMemoryPoolInfo.Value.Count + 1];
data[index++] = "Class Name,Full Class Name,Unused,Using,Acquire,Release,Add,Remove";
foreach (MemoryPoolInfo memoryPoolInfo in assemblyMemoryPoolInfo.Value)
{
data[index++] = Utility.Text.Format("{0},{1},{2},{3},{4},{5},{6},{7}", memoryPoolInfo.Type.Name, memoryPoolInfo.Type.FullName, memoryPoolInfo.UnusedMemoryCount.ToString(), memoryPoolInfo.UsingMemoryCount.ToString(), memoryPoolInfo.AcquireMemoryCount.ToString(), memoryPoolInfo.ReleaseMemoryCount.ToString(), memoryPoolInfo.AddMemoryCount.ToString(), memoryPoolInfo.RemoveMemoryCount.ToString());
}
File.WriteAllLines(exportFileName, data, Encoding.UTF8);
Debug.Log(Utility.Text.Format("Export memory pool CSV data to '{0}' success.", exportFileName));
}
catch (Exception exception)
{
Debug.LogError(Utility.Text.Format("Export memory pool CSV data to '{0}' failure, exception is '{1}'.", exportFileName, exception.ToString()));
}
}
}
}
EditorGUILayout.EndVertical();
EditorGUILayout.Separator();
}
}
}
else
{
EditorGUILayout.PropertyField(m_EnableStrictCheck);
}
serializedObject.ApplyModifiedProperties();
Repaint();
}
private void OnEnable()
{
m_EnableStrictCheck = serializedObject.FindProperty("m_EnableStrictCheck");
}
private void DrawMemoryPoolInfo(MemoryPoolInfo memoryPoolInfo)
{
EditorGUILayout.LabelField(m_ShowFullClassName ? memoryPoolInfo.Type.FullName : memoryPoolInfo.Type.Name, Utility.Text.Format("{0}\t{1}\t{2}\t{3}\t{4}\t{5}", memoryPoolInfo.UnusedMemoryCount.ToString(), memoryPoolInfo.UsingMemoryCount.ToString(), memoryPoolInfo.AcquireMemoryCount.ToString(), memoryPoolInfo.ReleaseMemoryCount.ToString(), memoryPoolInfo.AddMemoryCount.ToString(), memoryPoolInfo.RemoveMemoryCount.ToString()));
}
private int Comparison(MemoryPoolInfo a, MemoryPoolInfo b)
{
if (m_ShowFullClassName)
{
return a.Type.FullName.CompareTo(b.Type.FullName);
}
else
{
return a.Type.Name.CompareTo(b.Type.Name);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4c56705de5d84bd4b80ed19e50515179
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,164 @@
using System.Collections.Generic;
using System.Linq;
using TEngine.Runtime;
using UnityEditor;
using UnityEngine;
namespace TEngine.Editor
{
[CustomEditor(typeof(ProcedureComponent))]
internal sealed class ProcedureComponentInspector :TEngineInspector
{
private SerializedProperty m_AvailableProcedureTypeNames = null;
private SerializedProperty m_EntranceProcedureTypeName = null;
private string[] m_ProcedureTypeNames = null;
private List<string> m_CurrentAvailableProcedureTypeNames = null;
private int m_EntranceProcedureIndex = -1;
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
serializedObject.Update();
ProcedureComponent t = (ProcedureComponent)target;
if (string.IsNullOrEmpty(m_EntranceProcedureTypeName.stringValue))
{
EditorGUILayout.HelpBox("Entrance procedure is invalid.", MessageType.Error);
}
else if (EditorApplication.isPlaying)
{
EditorGUILayout.LabelField("Current Procedure", t.CurrentProcedure == null ? "None" : t.CurrentProcedure.GetType().ToString());
}
EditorGUI.BeginDisabledGroup(EditorApplication.isPlayingOrWillChangePlaymode);
{
GUILayout.Label("Available Procedures", EditorStyles.boldLabel);
if (m_ProcedureTypeNames.Length > 0)
{
EditorGUILayout.BeginVertical("box");
{
foreach (string procedureTypeName in m_ProcedureTypeNames)
{
bool selected = m_CurrentAvailableProcedureTypeNames.Contains(procedureTypeName);
if (selected != EditorGUILayout.ToggleLeft(procedureTypeName, selected))
{
if (!selected)
{
m_CurrentAvailableProcedureTypeNames.Add(procedureTypeName);
WriteAvailableProcedureTypeNames();
}
else if (procedureTypeName != m_EntranceProcedureTypeName.stringValue)
{
m_CurrentAvailableProcedureTypeNames.Remove(procedureTypeName);
WriteAvailableProcedureTypeNames();
}
}
}
}
EditorGUILayout.EndVertical();
}
else
{
EditorGUILayout.HelpBox("There is no available procedure.", MessageType.Warning);
}
if (m_CurrentAvailableProcedureTypeNames.Count > 0)
{
EditorGUILayout.Separator();
int selectedIndex = EditorGUILayout.Popup("Entrance Procedure", m_EntranceProcedureIndex, m_CurrentAvailableProcedureTypeNames.ToArray());
if (selectedIndex != m_EntranceProcedureIndex)
{
m_EntranceProcedureIndex = selectedIndex;
m_EntranceProcedureTypeName.stringValue = m_CurrentAvailableProcedureTypeNames[selectedIndex];
}
}
else
{
EditorGUILayout.HelpBox("Select available procedures first.", MessageType.Info);
}
}
EditorGUI.EndDisabledGroup();
serializedObject.ApplyModifiedProperties();
Repaint();
}
protected override void OnCompileComplete()
{
base.OnCompileComplete();
RefreshTypeNames();
}
private void OnEnable()
{
m_AvailableProcedureTypeNames = serializedObject.FindProperty("m_AvailableProcedureTypeNames");
m_EntranceProcedureTypeName = serializedObject.FindProperty("m_EntranceProcedureTypeName");
RefreshTypeNames();
}
private void RefreshTypeNames()
{
m_ProcedureTypeNames = Type.GetRuntimeTypeNames(typeof(ProcedureBase));
ReadAvailableProcedureTypeNames();
int oldCount = m_CurrentAvailableProcedureTypeNames.Count;
m_CurrentAvailableProcedureTypeNames = m_CurrentAvailableProcedureTypeNames.Where(x => m_ProcedureTypeNames.Contains(x)).ToList();
if (m_CurrentAvailableProcedureTypeNames.Count != oldCount)
{
WriteAvailableProcedureTypeNames();
}
else if (!string.IsNullOrEmpty(m_EntranceProcedureTypeName.stringValue))
{
m_EntranceProcedureIndex = m_CurrentAvailableProcedureTypeNames.IndexOf(m_EntranceProcedureTypeName.stringValue);
if (m_EntranceProcedureIndex < 0)
{
m_EntranceProcedureTypeName.stringValue = null;
}
}
serializedObject.ApplyModifiedProperties();
}
private void ReadAvailableProcedureTypeNames()
{
m_CurrentAvailableProcedureTypeNames = new List<string>();
int count = m_AvailableProcedureTypeNames.arraySize;
for (int i = 0; i < count; i++)
{
m_CurrentAvailableProcedureTypeNames.Add(m_AvailableProcedureTypeNames.GetArrayElementAtIndex(i).stringValue);
}
}
private void WriteAvailableProcedureTypeNames()
{
m_AvailableProcedureTypeNames.ClearArray();
if (m_CurrentAvailableProcedureTypeNames == null)
{
return;
}
m_CurrentAvailableProcedureTypeNames.Sort();
int count = m_CurrentAvailableProcedureTypeNames.Count;
for (int i = 0; i < count; i++)
{
m_AvailableProcedureTypeNames.InsertArrayElementAtIndex(i);
m_AvailableProcedureTypeNames.GetArrayElementAtIndex(i).stringValue = m_CurrentAvailableProcedureTypeNames[i];
}
if (!string.IsNullOrEmpty(m_EntranceProcedureTypeName.stringValue))
{
m_EntranceProcedureIndex = m_CurrentAvailableProcedureTypeNames.IndexOf(m_EntranceProcedureTypeName.stringValue);
if (m_EntranceProcedureIndex < 0)
{
m_EntranceProcedureTypeName.stringValue = null;
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7b6a1a49416939a48b84538399389568
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,17 @@
{
"name": "TEngine.Editor",
"references": [
"GUID:f4ecd6f7bd8993043b6cec60dd0cf2b2"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 63410924b4df8de43b318f32296693f7
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5b1b0efc98a99fa4db30cb92737d06f0
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,332 @@
using ScriptGenerator = TEngine.Editor.ScriptGenerator;
namespace TEngine.Editor
{
using System.Collections.Generic;
using System.Text;
using UnityEditor;
using UnityEngine;
public class ScriptGenerator
{
private static string gap = "/";
[MenuItem("GameObject/ScriptGenerator/UIProperty", priority = 49)]
public static void MemberProperty()
{
Generate(false);
}
[MenuItem("GameObject/ScriptGenerator/UIPropertyAndListener", priority = 49)]
public static void MemberPropertyAndListener()
{
Generate(true);
}
[MenuItem("GameObject/ScriptGenerator/UISwitchGroup", priority = 49)]
public static void UISwitchGroup()
{
var root = Selection.activeTransform;
if (root == null)
{
return;
}
var content = ScriptGenerator.SwitchGroupGenerator.Instance.Process(root);
TextEditor te = new TextEditor();
te.text = content;
te.SelectAll();
te.Copy();
}
private static void Generate(bool includeListener)
{
var root = Selection.activeTransform;
if (root != null)
{
StringBuilder strVar = new StringBuilder();
StringBuilder strBind = new StringBuilder();
StringBuilder strOnCreate = new StringBuilder();
StringBuilder strCallback = new StringBuilder();
Ergodic(root, root, ref strVar, ref strBind, ref strOnCreate, ref strCallback);
StringBuilder strFile = new StringBuilder();
if (includeListener)
{
strFile.Append("using TEngine;\n");
strFile.Append("using UnityEngine;\n");
strFile.Append("using UnityEngine.UI;\n\n");
strFile.Append("\tclass " + root.name + " : UIWindow\n");
strFile.Append("\t{\n");
}
// 脚本工具生成的代码
strFile.Append("\t#region 脚本工具生成的代码\n");
strFile.Append(strVar);
strFile.Append("\tprotected override void ScriptGenerator()\n");
strFile.Append("\t{\n");
strFile.Append(strBind);
strFile.Append(strOnCreate);
strFile.Append("\t}\n");
strFile.Append("\t#endregion");
if (includeListener)
{
strFile.Append("\n\n");
// #region 事件
strFile.Append("\t#region 事件\n");
strFile.Append(strCallback);
strFile.Append("\t#endregion\n\n");
strFile.Append("}\n");
}
TextEditor te = new TextEditor();
te.text = strFile.ToString();
te.SelectAll();
te.Copy();
}
}
private static void Ergodic(Transform root, Transform transform, ref StringBuilder strVar, ref StringBuilder strBind, ref StringBuilder strOnCreate, ref StringBuilder strCallback)
{
for (int i = 0; i < transform.childCount; ++i)
{
Transform child = transform.GetChild(i);
WriteScript(root, child, ref strVar, ref strBind, ref strOnCreate, ref strCallback);
if (child.name.StartsWith("m_item"))
{
continue;
}
Ergodic(root, child, ref strVar, ref strBind, ref strOnCreate, ref strCallback);
}
}
private static string GetRelativePath(Transform child, Transform root)
{
StringBuilder path = new StringBuilder();
path.Append(child.name);
while (child.parent != null && child.parent != root)
{
child = child.parent;
path.Insert(0, gap);
path.Insert(0, child.name);
}
return path.ToString();
}
private static string GetBtnFuncName(string varName)
{
return "OnClick" + varName.Replace("m_btn", string.Empty) + "Btn";
}
private static string GetToggleFuncName(string varName)
{
return "OnToggle" + varName.Replace("m_toggle", string.Empty) + "Change";
}
public static Dictionary<string, string> dicWidget = new Dictionary<string, string>()
{
{"m_go", "GameObject"},
{"m_item", "GameObject"},
{"m_tf", "Transform"},
{"m_rect","RectTransform"},
{"m_text","Text"},
{"m_richText","RichTextItem"},
{"m_tbtn","TextButtonItem"},
{"m_btn","Button"},
{"m_img","Image"},
{"m_rimg","RawImage"},
{"m_scroll","ScrollRect"},
{"m_input","InputField"},
{"m_grid","GridLayoutGroup"},
{"m_clay","CircleLayoutGroup"},
{"m_hlay","HorizontalLayoutGroup"},
{"m_vlay","VerticalLayoutGroup"},
{"m_slider","Slider"},
{"m_group","ToggleGroup"},
{"m_toggle","Toggle"},
{"m_curve","AnimationCurve"},
};
private static void WriteScript(Transform root, Transform child, ref StringBuilder strVar, ref StringBuilder strBind, ref StringBuilder strOnCreate, ref StringBuilder strCallback)
{
string varName = child.name;
string varType = string.Empty;
foreach (var pair in dicWidget)
{
if (varName.StartsWith(pair.Key))
{
varType = pair.Value;
break;
}
}
if (varType == string.Empty)
{
return;
}
string varPath = GetRelativePath(child, root);
if (!string.IsNullOrEmpty(varName))
{
strVar.Append("\t\tprivate " + varType + " " + varName + ";\n");
switch (varType)
{
case "Transform":
strBind.Append(string.Format("\t\t\t{0} = FindChild(\"{1}\");\n", varName, varPath));
break;
case "GameObject":
strBind.Append(string.Format("\t\t\t{0} = FindChild(\"{1}\").gameObject;\n", varName, varPath));
break;
case "AnimationCurve":
strBind.Append(string.Format("\t\t\t{0} = FindChildComponent<AnimCurveObject>(\"{1}\").m_animCurve;\n", varName, varPath));
break;
case "RichItemIcon":
strBind.Append(string.Format("\t\t\t{0} = CreateWidgetByType<{1}>(\"{2}\");\n", varName, varType, varPath));
break;
case "RedNoteBehaviour":
case "TextButtonItem":
case "SwitchTabItem":
case "UIActorWidget":
case "UIEffectWidget":
strBind.Append(string.Format("\t\t\t{0} = CreateWidget<{1}>(\"{2}\");\n", varName, varType, varPath));
break;
default:
strBind.Append(string.Format("\t\t\t{0} = FindChildComponent<{1}>(\"{2}\");\n", varName, varType, varPath));
break;
}
if (varType == "Button")
{
string varFuncName = GetBtnFuncName(varName);
strOnCreate.Append(string.Format("\t\t\t{0}.onClick.AddListener({1});\n", varName, varFuncName));
strCallback.Append(string.Format("\t\tprivate void {0}()\n", varFuncName));
strCallback.Append("\t\t{\n\t\t}\n");
}
if (varType == "Toggle")
{
string varFuncName = GetToggleFuncName(varName);
strOnCreate.Append(string.Format("\t\t\t{0}.onValueChanged.AddListener({1});\n", varName, varFuncName));
strCallback.Append(string.Format("\t\tprivate void {0}(bool isOn)\n", varFuncName));
strCallback.Append("\t\t{\n\t\t}\n");
}
}
}
public class GeneratorHelper : EditorWindow
{
[MenuItem("GameObject/ScriptGenerator/About", priority = 49)]
public static void About()
{
ScriptGenerator.GeneratorHelper welcomeWindow = (ScriptGenerator.GeneratorHelper)EditorWindow.GetWindow(typeof(ScriptGenerator.GeneratorHelper), false, "About ScriptGenerator");
}
public void Awake()
{
minSize = new Vector2(400, 600);
}
protected void OnGUI()
{
GUILayout.BeginVertical();
foreach (var item in ScriptGenerator.dicWidget)
{
GUILayout.Label(item.Key + "\t" + item.Value);
}
}
}
public class SwitchGroupGeneratorHelper : EditorWindow
{
[MenuItem("GameObject/ScriptGenerator/AboutSwitchGroup", priority = 50)]
public static void About()
{
GetWindow(typeof(SwitchGroupGeneratorHelper), false, "AboutSwitchGroup");
}
public void Awake()
{
minSize = new Vector2(400, 600);
}
protected void OnGUI()
{
GUILayout.BeginVertical();
GUILayout.Label(SwitchGroupGenerator.CONDITION + "\t" + "SwitchTabItem[]");
}
}
public class SwitchGroupGenerator
{
/*
遍历子节点,找到所有名为 m_switchGroup 开始的节点,输出该节点
*/
public const string CONDITION = "m_switchGroup";
public static readonly SwitchGroupGenerator Instance = new SwitchGroupGenerator();
public string Process(Transform root)
{
var sbd = new StringBuilder();
var list = new List<Transform>();
Collect(root, list);
foreach (var node in list)
{
sbd.AppendLine(Process(root, node)).AppendLine();
}
return sbd.ToString();
}
public void Collect(Transform node, List<Transform> nodeList)
{
if (node.name.StartsWith(CONDITION))
{
nodeList.Add(node);
return;
}
var childCnt = node.childCount;
for (var i = 0; i < childCnt; i++)
{
var child = node.GetChild(i);
Collect(child, nodeList);
}
}
public string Process(Transform root, Transform groupTf)
{
var parentPath = GetPath(root, groupTf);
var _name = groupTf.name;
var sbd = new StringBuilder(@"
var _namePath = ""#parentPath"";
var _nameTf = FindChild(_namePath);
var childCnt = _nameTf.childCount;
SwitchTabItem[] _name;
_name = new SwitchTabItem[childCnt];
for (var i = 0; i < childCnt; i++)
{
var child = _nameTf.GetChild(i);
_name[i] = CreateWidget<SwitchTabItem>(_namePath + ""/"" + child.name);
}");
sbd.Replace("_name", _name);
sbd.Replace("#parentPath", parentPath);
return sbd.ToString();
}
public string GetPath(Transform root, Transform childTf)
{
if (childTf == null)
{
return string.Empty;
}
if (childTf == root)
{
return childTf.name;
}
var parentPath = GetPath(root, childTf.parent);
if (parentPath == string.Empty)
{
return childTf.name;
}
return parentPath + "/" + childTf.name;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 94d31d88c0f4b3f4a8a672a755e356d5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: cb91232a4fb2efd47b8d8b4da4f22229
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5643953d0e0f87d4db82f6ab1e38aa77
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,11 @@
using System;
namespace TEngine.Editor
{
/// <summary>
/// 配置路径属性。
/// </summary>
public abstract class ConfigPathAttribute : Attribute
{
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ebec4c251a948e84fa46d59d31a07f6e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,172 @@
using UnityEditor;
namespace TEngine.Editor
{
/// <summary>
/// 日志脚本宏定义。
/// </summary>
public static class LogScriptingDefineSymbols
{
private const string EnableLogScriptingDefineSymbol = "ENABLE_LOG";
private const string EnableDebugAndAboveLogScriptingDefineSymbol = "ENABLE_DEBUG_AND_ABOVE_LOG";
private const string EnableInfoAndAboveLogScriptingDefineSymbol = "ENABLE_INFO_AND_ABOVE_LOG";
private const string EnableWarningAndAboveLogScriptingDefineSymbol = "ENABLE_WARNING_AND_ABOVE_LOG";
private const string EnableErrorAndAboveLogScriptingDefineSymbol = "ENABLE_ERROR_AND_ABOVE_LOG";
private const string EnableFatalAndAboveLogScriptingDefineSymbol = "ENABLE_FATAL_AND_ABOVE_LOG";
private const string EnableDebugLogScriptingDefineSymbol = "ENABLE_DEBUG_LOG";
private const string EnableInfoLogScriptingDefineSymbol = "ENABLE_LOG_INFO";
private const string EnableWarningLogScriptingDefineSymbol = "ENABLE_LOG_WARNING";
private const string EnableErrorLogScriptingDefineSymbol = "ENABLE_LOG_ERROR";
private const string EnableFatalLogScriptingDefineSymbol = "ENABLE_LOG_EXCEPTION";
private static readonly string[] AboveLogScriptingDefineSymbols = new string[]
{
EnableDebugAndAboveLogScriptingDefineSymbol,
EnableInfoAndAboveLogScriptingDefineSymbol,
EnableWarningAndAboveLogScriptingDefineSymbol,
EnableErrorAndAboveLogScriptingDefineSymbol,
EnableFatalAndAboveLogScriptingDefineSymbol
};
private static readonly string[] SpecifyLogScriptingDefineSymbols = new string[]
{
EnableDebugLogScriptingDefineSymbol,
EnableInfoLogScriptingDefineSymbol,
EnableWarningLogScriptingDefineSymbol,
EnableErrorLogScriptingDefineSymbol,
EnableFatalLogScriptingDefineSymbol
};
/// <summary>
/// 禁用所有日志脚本宏定义。
/// </summary>
[MenuItem("TEngine/设置日志|Log Scripting Define Symbols/Disable All Logs", false, 151)]
public static void DisableAllLogs()
{
ScriptingDefineSymbols.RemoveScriptingDefineSymbol(EnableLogScriptingDefineSymbol);
foreach (string specifyLogScriptingDefineSymbol in SpecifyLogScriptingDefineSymbols)
{
ScriptingDefineSymbols.RemoveScriptingDefineSymbol(specifyLogScriptingDefineSymbol);
}
foreach (string aboveLogScriptingDefineSymbol in AboveLogScriptingDefineSymbols)
{
ScriptingDefineSymbols.RemoveScriptingDefineSymbol(aboveLogScriptingDefineSymbol);
}
}
/// <summary>
/// 开启所有日志脚本宏定义。
/// </summary>
[MenuItem("TEngine/设置日志|Log Scripting Define Symbols/Enable All Logs", false, 152)]
public static void EnableAllLogs()
{
DisableAllLogs();
ScriptingDefineSymbols.AddScriptingDefineSymbol(EnableLogScriptingDefineSymbol);
}
/// <summary>
/// 开启调试及以上级别的日志脚本宏定义。
/// </summary>
[MenuItem("TEngine/设置日志|Log Scripting Define Symbols/Enable Debug And Above Logs", false, 153)]
public static void EnableDebugAndAboveLogs()
{
SetAboveLogScriptingDefineSymbol(EnableDebugAndAboveLogScriptingDefineSymbol);
}
/// <summary>
/// 开启信息及以上级别的日志脚本宏定义。
/// </summary>
[MenuItem("TEngine/设置日志|Log Scripting Define Symbols/Enable Info And Above Logs", false, 154)]
public static void EnableInfoAndAboveLogs()
{
SetAboveLogScriptingDefineSymbol(EnableInfoAndAboveLogScriptingDefineSymbol);
}
/// <summary>
/// 开启警告及以上级别的日志脚本宏定义。
/// </summary>
[MenuItem("TEngine/设置日志|Log Scripting Define Symbols/Enable Warning And Above Logs", false, 155)]
public static void EnableWarningAndAboveLogs()
{
SetAboveLogScriptingDefineSymbol(EnableWarningAndAboveLogScriptingDefineSymbol);
}
/// <summary>
/// 开启错误及以上级别的日志脚本宏定义。
/// </summary>
[MenuItem("TEngine/设置日志|Log Scripting Define Symbols/Enable Error And Above Logs", false, 156)]
public static void EnableErrorAndAboveLogs()
{
SetAboveLogScriptingDefineSymbol(EnableErrorAndAboveLogScriptingDefineSymbol);
}
/// <summary>
/// 开启严重错误及以上级别的日志脚本宏定义。
/// </summary>
[MenuItem("TEngine/设置日志|Log Scripting Define Symbols/Enable Fatal And Above Logs", false, 157)]
public static void EnableFatalAndAboveLogs()
{
SetAboveLogScriptingDefineSymbol(EnableFatalAndAboveLogScriptingDefineSymbol);
}
/// <summary>
/// 设置日志脚本宏定义。
/// </summary>
/// <param name="aboveLogScriptingDefineSymbol">要设置的日志脚本宏定义。</param>
public static void SetAboveLogScriptingDefineSymbol(string aboveLogScriptingDefineSymbol)
{
if (string.IsNullOrEmpty(aboveLogScriptingDefineSymbol))
{
return;
}
foreach (string i in AboveLogScriptingDefineSymbols)
{
if (i == aboveLogScriptingDefineSymbol)
{
DisableAllLogs();
ScriptingDefineSymbols.AddScriptingDefineSymbol(aboveLogScriptingDefineSymbol);
return;
}
}
}
/// <summary>
/// 设置日志脚本宏定义。
/// </summary>
/// <param name="specifyLogScriptingDefineSymbols">要设置的日志脚本宏定义。</param>
public static void SetSpecifyLogScriptingDefineSymbols(string[] specifyLogScriptingDefineSymbols)
{
if (specifyLogScriptingDefineSymbols == null || specifyLogScriptingDefineSymbols.Length <= 0)
{
return;
}
bool removed = false;
foreach (string specifyLogScriptingDefineSymbol in specifyLogScriptingDefineSymbols)
{
if (string.IsNullOrEmpty(specifyLogScriptingDefineSymbol))
{
continue;
}
foreach (string i in SpecifyLogScriptingDefineSymbols)
{
if (i == specifyLogScriptingDefineSymbol)
{
if (!removed)
{
removed = true;
DisableAllLogs();
}
ScriptingDefineSymbols.AddScriptingDefineSymbol(specifyLogScriptingDefineSymbol);
break;
}
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: aa2eb2851a9052e468b2c14e66f604a6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,94 @@
using System;
using System.Diagnostics;
using TEngine.Runtime;
using UnityEditor;
using UnityEngine;
namespace TEngine.Editor
{
/// <summary>
/// 打开文件夹相关的实用函数。
/// </summary>
public static class OpenFolder
{
/// <summary>
/// 打开 Data Path 文件夹。
/// </summary>
[MenuItem("TEngine/打开文件夹|Open Folder/Data Path", false, 10)]
public static void OpenFolderDataPath()
{
Execute(Application.dataPath);
}
/// <summary>
/// 打开 Persistent Data Path 文件夹。
/// </summary>
[MenuItem("TEngine/打开文件夹|Open Folder/Persistent Data Path", false, 11)]
public static void OpenFolderPersistentDataPath()
{
Execute(Application.persistentDataPath);
}
/// <summary>
/// 打开 Streaming Assets Path 文件夹。
/// </summary>
[MenuItem("TEngine/打开文件夹|Open Folder/Streaming Assets Path", false, 12)]
public static void OpenFolderStreamingAssetsPath()
{
Execute(Application.streamingAssetsPath);
}
/// <summary>
/// 打开 Temporary Cache Path 文件夹。
/// </summary>
[MenuItem("TEngine/打开文件夹|Open Folder/Temporary Cache Path", false, 13)]
public static void OpenFolderTemporaryCachePath()
{
Execute(Application.temporaryCachePath);
}
#if UNITY_2018_3_OR_NEWER
/// <summary>
/// 打开 Console Log Path 文件夹。
/// </summary>
[MenuItem("TEngine/打开文件夹|Open Folder/Console Log Path", false, 14)]
public static void OpenFolderConsoleLogPath()
{
Execute(System.IO.Path.GetDirectoryName(Application.consoleLogPath));
}
#endif
/// <summary>
/// 打开 TEngine Log Path 文件夹。
/// </summary>
[MenuItem("TEngine/打开文件夹|Open Folder/TEngine Log Path", false, 14)]
public static void OpenFolderTEngineLogPath()
{
Execute(System.IO.Path.GetDirectoryName(Application.dataPath) + "../TEnginePersistentDataPath/");
}
/// <summary>
/// 打开指定路径的文件夹。
/// </summary>
/// <param name="folder">要打开的文件夹的路径。</param>
public static void Execute(string folder)
{
folder = Utility.Text.Format("\"{0}\"", folder);
switch (Application.platform)
{
case RuntimePlatform.WindowsEditor:
Process.Start("Explorer.exe", folder.Replace('/', '\\'));
break;
case RuntimePlatform.OSXEditor:
Process.Start("open", folder);
break;
default:
throw new Exception(Utility.Text.Format("Not support open folder on '{0}' platform.", Application.platform.ToString()));
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5f92badcbe48d6542a826527ab8d006b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,150 @@
using System.Collections.Generic;
using UnityEditor;
namespace TEngine.Editor
{
/// <summary>
/// 脚本宏定义。
/// </summary>
public static class ScriptingDefineSymbols
{
private static readonly BuildTargetGroup[] BuildTargetGroups = new BuildTargetGroup[]
{
BuildTargetGroup.Standalone,
BuildTargetGroup.iOS,
BuildTargetGroup.Android,
BuildTargetGroup.WSA,
BuildTargetGroup.WebGL
};
/// <summary>
/// 检查指定平台是否存在指定的脚本宏定义。
/// </summary>
/// <param name="buildTargetGroup">要检查脚本宏定义的平台。</param>
/// <param name="scriptingDefineSymbol">要检查的脚本宏定义。</param>
/// <returns>指定平台是否存在指定的脚本宏定义。</returns>
public static bool HasScriptingDefineSymbol(BuildTargetGroup buildTargetGroup, string scriptingDefineSymbol)
{
if (string.IsNullOrEmpty(scriptingDefineSymbol))
{
return false;
}
string[] scriptingDefineSymbols = GetScriptingDefineSymbols(buildTargetGroup);
foreach (string i in scriptingDefineSymbols)
{
if (i == scriptingDefineSymbol)
{
return true;
}
}
return false;
}
/// <summary>
/// 为指定平台增加指定的脚本宏定义。
/// </summary>
/// <param name="buildTargetGroup">要增加脚本宏定义的平台。</param>
/// <param name="scriptingDefineSymbol">要增加的脚本宏定义。</param>
public static void AddScriptingDefineSymbol(BuildTargetGroup buildTargetGroup, string scriptingDefineSymbol)
{
if (string.IsNullOrEmpty(scriptingDefineSymbol))
{
return;
}
if (HasScriptingDefineSymbol(buildTargetGroup, scriptingDefineSymbol))
{
return;
}
List<string> scriptingDefineSymbols = new List<string>(GetScriptingDefineSymbols(buildTargetGroup))
{
scriptingDefineSymbol
};
SetScriptingDefineSymbols(buildTargetGroup, scriptingDefineSymbols.ToArray());
}
/// <summary>
/// 为指定平台移除指定的脚本宏定义。
/// </summary>
/// <param name="buildTargetGroup">要移除脚本宏定义的平台。</param>
/// <param name="scriptingDefineSymbol">要移除的脚本宏定义。</param>
public static void RemoveScriptingDefineSymbol(BuildTargetGroup buildTargetGroup, string scriptingDefineSymbol)
{
if (string.IsNullOrEmpty(scriptingDefineSymbol))
{
return;
}
if (!HasScriptingDefineSymbol(buildTargetGroup, scriptingDefineSymbol))
{
return;
}
List<string> scriptingDefineSymbols = new List<string>(GetScriptingDefineSymbols(buildTargetGroup));
while (scriptingDefineSymbols.Contains(scriptingDefineSymbol))
{
scriptingDefineSymbols.Remove(scriptingDefineSymbol);
}
SetScriptingDefineSymbols(buildTargetGroup, scriptingDefineSymbols.ToArray());
}
/// <summary>
/// 为所有平台增加指定的脚本宏定义。
/// </summary>
/// <param name="scriptingDefineSymbol">要增加的脚本宏定义。</param>
public static void AddScriptingDefineSymbol(string scriptingDefineSymbol)
{
if (string.IsNullOrEmpty(scriptingDefineSymbol))
{
return;
}
foreach (BuildTargetGroup buildTargetGroup in BuildTargetGroups)
{
AddScriptingDefineSymbol(buildTargetGroup, scriptingDefineSymbol);
}
}
/// <summary>
/// 为所有平台移除指定的脚本宏定义。
/// </summary>
/// <param name="scriptingDefineSymbol">要移除的脚本宏定义。</param>
public static void RemoveScriptingDefineSymbol(string scriptingDefineSymbol)
{
if (string.IsNullOrEmpty(scriptingDefineSymbol))
{
return;
}
foreach (BuildTargetGroup buildTargetGroup in BuildTargetGroups)
{
RemoveScriptingDefineSymbol(buildTargetGroup, scriptingDefineSymbol);
}
}
/// <summary>
/// 获取指定平台的脚本宏定义。
/// </summary>
/// <param name="buildTargetGroup">要获取脚本宏定义的平台。</param>
/// <returns>平台的脚本宏定义。</returns>
public static string[] GetScriptingDefineSymbols(BuildTargetGroup buildTargetGroup)
{
return PlayerSettings.GetScriptingDefineSymbolsForGroup(buildTargetGroup).Split(';');
}
/// <summary>
/// 设置指定平台的脚本宏定义。
/// </summary>
/// <param name="buildTargetGroup">要设置脚本宏定义的平台。</param>
/// <param name="scriptingDefineSymbols">要设置的脚本宏定义。</param>
public static void SetScriptingDefineSymbols(BuildTargetGroup buildTargetGroup, string[] scriptingDefineSymbols)
{
PlayerSettings.SetScriptingDefineSymbolsForGroup(buildTargetGroup, string.Join(";", scriptingDefineSymbols));
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8c68b2caa25d7904b9f7045e21cf5d33
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,114 @@
using System.Collections.Generic;
using System.Reflection;
using TEngine.Runtime;
namespace TEngine.Editor
{
/// <summary>
/// 类型相关的实用函数。
/// </summary>
internal static class Type
{
private static readonly string[] RuntimeAssemblyNames =
{
"TEngine.Runtime",
"Assembly-CSharp",
};
private static readonly string[] RuntimeOrEditorAssemblyNames =
{
"TEngine.Runtime",
"Assembly-CSharp",
"TEngine.Editor",
"Assembly-CSharp-Editor",
};
/// <summary>
/// 获取配置路径。
/// </summary>
/// <typeparam name="T">配置类型。</typeparam>
/// <returns>配置路径。</returns>
internal static string GetConfigurationPath<T>() where T : ConfigPathAttribute
{
foreach (System.Type type in Utility.Assembly.GetTypes())
{
if (!type.IsAbstract || !type.IsSealed)
{
continue;
}
foreach (FieldInfo fieldInfo in type.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly))
{
if (fieldInfo.FieldType == typeof(string) && fieldInfo.IsDefined(typeof(T), false))
{
return (string)fieldInfo.GetValue(null);
}
}
foreach (PropertyInfo propertyInfo in type.GetProperties(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly))
{
if (propertyInfo.PropertyType == typeof(string) && propertyInfo.IsDefined(typeof(T), false))
{
return (string)propertyInfo.GetValue(null, null);
}
}
}
return null;
}
/// <summary>
/// 在运行时程序集中获取指定基类的所有子类的名称。
/// </summary>
/// <param name="typeBase">基类类型。</param>
/// <returns>指定基类的所有子类的名称。</returns>
internal static string[] GetRuntimeTypeNames(System.Type typeBase)
{
return GetTypeNames(typeBase, RuntimeAssemblyNames);
}
/// <summary>
/// 在运行时或编辑器程序集中获取指定基类的所有子类的名称。
/// </summary>
/// <param name="typeBase">基类类型。</param>
/// <returns>指定基类的所有子类的名称。</returns>
internal static string[] GetRuntimeOrEditorTypeNames(System.Type typeBase)
{
return GetTypeNames(typeBase, RuntimeOrEditorAssemblyNames);
}
private static string[] GetTypeNames(System.Type typeBase, string[] assemblyNames)
{
List<string> typeNames = new List<string>();
foreach (string assemblyName in assemblyNames)
{
Assembly assembly = null;
try
{
assembly = Assembly.Load(assemblyName);
}
catch
{
continue;
}
if (assembly == null)
{
continue;
}
System.Type[] types = assembly.GetTypes();
foreach (System.Type type in types)
{
if (type.IsClass && !type.IsAbstract && typeBase.IsAssignableFrom(type))
{
typeNames.Add(type.FullName);
}
}
}
typeNames.Sort();
return typeNames.ToArray();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5a11122d3f4a70749870f268c612df21
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9f09a3719e6e4b544bfb383d1f31a327
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7477a3a5f2acedc468c305e71c9a0edd
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,556 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
using UnityEngine.Audio;
namespace TEngine.Runtime
{
/// <summary>
/// 音频类型
/// </summary>
public enum AudioType
{
/// <summary>
/// 声音
/// </summary>
Sound,
/// <summary>
/// 背景音乐
/// </summary>
Music,
/// <summary>
/// 人声
/// </summary>
Voice,
/// <summary>
/// 最大
/// </summary>
Max
}
/// <summary>
/// 音频控制管理器
/// </summary>
public class AudioMgr : UnitySingleton<AudioMgr>
{
#region Propreties
private float _volume = 1f;
private bool _enable = true;
private bool _disabled = false;
public AudioMixer audioMixer { get; set; }
float[] _agentVolume = new float[(int)AudioType.Max];
private AudioAgent[] _audioAgents = new AudioAgent[(int)AudioType.Max];
public Dictionary<string, AssetData> AudioClipPool = new Dictionary<string, AssetData>();
#endregion
#region
/// <summary>
/// 总音量
/// </summary>
public float Volume
{
get
{
if (_disabled)
{
return 0.0f;
}
return _volume;
}
set
{
if (_disabled)
{
return;
}
_volume = value;
AudioListener.volume = _volume;
}
}
/// <summary>
/// 总开关
/// </summary>
public bool Enable
{
get
{
if (_disabled)
{
return false;
}
return _enable;
}
set
{
if (_disabled)
{
return;
}
_enable = value;
AudioListener.volume = _enable ? _volume : 0f;
}
}
/// <summary>
/// 背景音量
/// </summary>
public float MusicVolume
{
get
{
if (_disabled)
{
return 0.0f;
}
return _agentVolume[(int)AudioType.Music];
}
set
{
if (_disabled)
{
return;
}
float volume = Mathf.Clamp(value, 0.0001f, 1.0f);
_agentVolume[(int)AudioType.Music] = volume;
audioMixer.SetFloat("MusicVolume", Mathf.Log10(volume) * 20f);
}
}
/// <summary>
/// 音效音量
/// </summary>
public float SoundVolume
{
get
{
if (_disabled)
{
return 0.0f;
}
return _agentVolume[(int)AudioType.Sound];
}
set
{
if (_disabled)
{
return;
}
float volume = Mathf.Clamp(value, 0.0001f, 1.0f);
_agentVolume[(int)AudioType.Sound] = volume;
audioMixer.SetFloat("SoundVolume", Mathf.Log10(volume) * 20f);
}
}
/// <summary>
/// Voice音量
/// </summary>
public float VoiceVolume
{
get
{
if (_disabled)
{
return 0.0f;
}
return _agentVolume[(int)AudioType.Voice];
}
set
{
if (_disabled)
{
return;
}
float volume = Mathf.Clamp(value, 0.0001f, 1.0f);
_agentVolume[(int)AudioType.Voice] = volume;
audioMixer.SetFloat("VoiceVolume", Mathf.Log10(volume) * 20f);
}
}
/// <summary>
/// 是否允许Music
/// </summary>
public bool MusicEnable
{
get
{
if (_disabled)
{
return false;
}
float db;
if (audioMixer.GetFloat("MusicVolume", out db))
{
return db > -80f;
}
else
{
return false;
}
}
set
{
if (_disabled)
{
return;
}
if (value)
{
audioMixer.SetFloat("MusicVolume", Mathf.Log10(_agentVolume[(int)AudioType.Music]) * 20f);
}
else
{
audioMixer.SetFloat("MusicVolume", -80f);
}
}
}
/// <summary>
/// 是否允许Sound
/// </summary>
public bool SoundEnable
{
get
{
if (_disabled)
{
return false;
}
return _audioAgents[(int)AudioType.Sound].Enable;
}
set
{
if (_disabled)
{
return;
}
_audioAgents[(int)AudioType.Sound].Enable = value;
}
}
/// <summary>
/// 是否允许Voice
/// </summary>
public bool VoiceEnable
{
get
{
if (_disabled)
{
return false;
}
return _audioAgents[(int)AudioType.Voice].Enable;
}
set
{
if (_disabled)
{
return;
}
_audioAgents[(int)AudioType.Voice].Enable = value;
}
}
#endregion
protected override void OnLoad()
{
try
{
TypeInfo typeInfo = typeof(AudioSettings).GetTypeInfo();
PropertyInfo propertyInfo = typeInfo.GetDeclaredProperty("unityAudioDisabled");
_disabled = (bool)propertyInfo.GetValue(null);
if (_disabled)
{
return;
}
}
catch (Exception e)
{
TLogger.LogError(e.ToString());
}
audioMixer = Resources.Load<AudioMixer>("Audio/TEngineAudioMixer");
for (int i = 0; i < (int)AudioType.Max; ++i)
{
int channelMaxNum = 0;
if (i == (int)AudioType.Sound)
{
channelMaxNum = 10;
}
else
{
channelMaxNum = 1;
}
_audioAgents[i] = new AudioAgent(channelMaxNum, audioMixer.FindMatchingGroups(((AudioType)i).ToString())[0]);
_agentVolume[i] = 1.0f;
}
}
#region
public TAudio Play(AudioType type, string path, bool bLoop = false, float volume = 1.0f, bool bAsync = false, bool bInPool = false)
{
if (_disabled)
{
return null;
}
TAudio audio = _audioAgents[(int)type].Play(path, bAsync, bInPool);
{
if (audio != null)
{
audio.IsLoop = bLoop;
audio.Volume = volume;
}
return audio;
}
}
public void Stop(AudioType type, bool fadeout)
{
if (_disabled)
{
return;
}
_audioAgents[(int)type].Stop(fadeout);
}
public void StopAll(bool fadeout)
{
if (_disabled)
{
return;
}
for (int i = 0; i < (int)AudioType.Max; ++i)
{
if (_audioAgents[i] != null)
{
_audioAgents[i].Stop(fadeout);
}
}
}
public void Restart()
{
if (_disabled)
{
return;
}
CleanSoundPool();
for (int i = 0; i < (int)AudioType.Max; ++i)
{
if (_audioAgents[i] != null)
{
for (int j = 0; j < _audioAgents[i]._audioObjects.Count; ++j)
{
if (_audioAgents[i]._audioObjects[j] != null)
{
_audioAgents[i]._audioObjects[j].Destroy();
_audioAgents[i]._audioObjects[j] = null;
}
}
}
_audioAgents[i] = null;
}
OnLoad();
}
#endregion
#region Pool
public void PutInAudioPool(List<string> list)
{
if (_disabled)
return;
foreach (string path in list)
{
if (!AudioClipPool.ContainsKey(path))
{
AssetData assetData = ResMgr.Instance.GetAsset(path, false);
AudioClipPool?.Add(assetData.Path, assetData);
}
}
}
public void RemoveClipFromPool(List<string> list)
{
if (_disabled)
{
return;
}
foreach (string path in list)
{
if (AudioClipPool.ContainsKey(path))
{
AudioClipPool[path].DecRef();
AudioClipPool.Remove(path);
}
}
}
public void CleanSoundPool()
{
if (_disabled)
{
return;
}
foreach (var dic in AudioClipPool)
{
dic.Value.DecRef();
}
AudioClipPool.Clear();
}
private void Update()
{
for (int i = 0; i < _audioAgents.Length; ++i)
{
if (_audioAgents[i] != null)
{
_audioAgents[i].Update(Time.deltaTime);
}
}
}
#endregion
}
#region AudioAgent
public class AudioAgent
{
public List<TAudio> _audioObjects;
AudioMixerGroup _audioMixerGroup;
int _maxChannel;
bool _bEnable = true;
public bool Enable
{
get
{
return _bEnable;
}
set
{
if (_bEnable != value)
{
_bEnable = value;
if (!_bEnable)
{
for (int i = 0; i < _audioObjects.Count; ++i)
{
if (_audioObjects[i] != null)
{
_audioObjects[i].Stop();
}
}
}
}
}
}
public AudioAgent(int maxChannel, AudioMixerGroup audioMixerGroup)
{
_maxChannel = maxChannel;
_audioObjects = new List<TAudio>();
for (int i = 0; i < _maxChannel; i++)
{
TAudio tAudio = new TAudio();
tAudio.Init(audioMixerGroup);
_audioObjects.Add(tAudio);
}
_audioMixerGroup = audioMixerGroup;
}
public void AddAudio(int Num)
{
_maxChannel += Num;
for (int i = 0; i < Num; i++)
{
_audioObjects.Add(null);
}
}
public TAudio Play(string path, bool bAsync, bool bInPool = false)
{
if (!_bEnable)
{
return null;
}
int freeChannel = -1;
float duration = -1;
int num = 0;
for (int i = 0; i < _audioObjects.Count; ++i)
{
if (_audioObjects[i] != null && _audioObjects[i]._assetData != null && _audioObjects[i].IsFinish == false)
{
if (path.Equals(_audioObjects[i]._assetData.Path))
{
num++;
}
}
}
for (int i = 0; i < _audioObjects.Count; i++)
{
if (_audioObjects[i]._assetData == null || _audioObjects[i].IsFinish == true)
{
freeChannel = i;
break;
}
else if (_audioObjects[i].Duration > duration)
{
duration = _audioObjects[i].Duration;
freeChannel = i;
}
}
if (freeChannel >= 0)
{
if (_audioObjects[freeChannel] == null)
{
_audioObjects[freeChannel] = TAudio.Create(path, bAsync, _audioMixerGroup, bInPool);
}
else
{
_audioObjects[freeChannel].Load(path, bAsync, bInPool);
}
return _audioObjects[freeChannel];
}
else
{
TLogger.LogError($"Here is no channel to play audio {path}");
return null;
}
}
public void Stop(bool fadeout)
{
for (int i = 0; i < _audioObjects.Count; ++i)
{
if (_audioObjects[i] != null)
{
_audioObjects[i].Stop(fadeout);
}
}
}
public void Update(float delta)
{
for (int i = 0; i < _audioObjects.Count; ++i)
{
if (_audioObjects[i] != null)
{
_audioObjects[i].Update(delta);
}
}
}
}
#endregion
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9f29360ec6919864bba8e9983366acad
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5848718449093574fad8b7310b176a10
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a28c58ed649f9ff46a7c8a483765b835
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,194 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!244 &-5700573295807266226
AudioMixerEffectController:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_EffectID: 4484537926adf3943b8d15631fe4c9a8
m_EffectName: Attenuation
m_MixLevel: 2a6cc932f56dd434391cf093c811be4a
m_Parameters: []
m_SendTarget: {fileID: 0}
m_EnableWetMix: 0
m_Bypass: 0
--- !u!243 &-2688087525938382097
AudioMixerGroupController:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Voice
m_AudioMixer: {fileID: 24100000}
m_GroupID: 3385c639676b0174a96c1ba9d8e7d7e8
m_Children: []
m_Volume: 351c3633d7a368b48b9993d12d4b49d1
m_Pitch: 9ae2cf17f804a73499b51997d4703eee
m_Send: 00000000000000000000000000000000
m_Effects:
- {fileID: 3233559901938498695}
m_UserColorIndex: 0
m_Mute: 0
m_Solo: 0
m_BypassEffects: 0
--- !u!244 &-2140790635248599779
AudioMixerEffectController:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_EffectID: 45c78cf1965841c4cb0d3c8abef208ec
m_EffectName: Send
m_MixLevel: 6a8d829224d69b046b7ea67a0682d9e8
m_Parameters: []
m_SendTarget: {fileID: 0}
m_EnableWetMix: 0
m_Bypass: 0
--- !u!241 &24100000
AudioMixerController:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: TEngineAudioMixer
m_OutputGroup: {fileID: 0}
m_MasterGroup: {fileID: 24300002}
m_Snapshots:
- {fileID: 24500006}
m_StartSnapshot: {fileID: 24500006}
m_SuspendThreshold: -80
m_EnableSuspend: 1
m_UpdateMode: 0
m_ExposedParameters:
- guid: 5542a83cb62d88f4bb57676181f23921
name: MusicVolume
- guid: c48081496ad8d11438c24892996c3a81
name: SoundVolume
- guid: 351c3633d7a368b48b9993d12d4b49d1
name: VoiceVolume
m_AudioMixerGroupViews:
- guids:
- 5a6f013f4ed308e49981ccc37373e521
- 1f218ab66c2b2b641b0d2ac1c8714263
- 503f9da271c2c6543b27f7c13a51e875
- 3385c639676b0174a96c1ba9d8e7d7e8
name: View
m_CurrentViewIndex: 0
m_TargetSnapshot: {fileID: 24500006}
--- !u!243 &24300002
AudioMixerGroupController:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Master
m_AudioMixer: {fileID: 24100000}
m_GroupID: 5a6f013f4ed308e49981ccc37373e521
m_Children:
- {fileID: 9185764251035995994}
- {fileID: -2688087525938382097}
- {fileID: 3168079212996900356}
m_Volume: f0efd2d64837bfe4d98d42033680eafc
m_Pitch: 86a3dfc09cd72544983864f031c3d96c
m_Send: 00000000000000000000000000000000
m_Effects:
- {fileID: 24400004}
- {fileID: -2140790635248599779}
m_UserColorIndex: 0
m_Mute: 0
m_Solo: 0
m_BypassEffects: 0
--- !u!244 &24400004
AudioMixerEffectController:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_EffectID: 43f3c3fd8be9c2649aaef2ae5860bb30
m_EffectName: Attenuation
m_MixLevel: c02fd21fa18282e498be1c97d0eef2ac
m_Parameters: []
m_SendTarget: {fileID: 0}
m_EnableWetMix: 0
m_Bypass: 0
--- !u!245 &24500006
AudioMixerSnapshotController:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Snapshot
m_AudioMixer: {fileID: 24100000}
m_SnapshotID: 5f631808614c40848b912e405ee09d3c
m_FloatValues: {}
m_TransitionOverrides: {}
--- !u!243 &3168079212996900356
AudioMixerGroupController:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Sound
m_AudioMixer: {fileID: 24100000}
m_GroupID: 503f9da271c2c6543b27f7c13a51e875
m_Children: []
m_Volume: c48081496ad8d11438c24892996c3a81
m_Pitch: 8806fa02b4e88c64b9bc81979e7cbe4e
m_Send: 00000000000000000000000000000000
m_Effects:
- {fileID: 8680063345532094417}
m_UserColorIndex: 0
m_Mute: 0
m_Solo: 0
m_BypassEffects: 0
--- !u!244 &3233559901938498695
AudioMixerEffectController:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_EffectID: 7e159eaf73c3d5f4d8b9cb86ca2fbdc9
m_EffectName: Attenuation
m_MixLevel: 35f98f9c693a7ad4f84978dca6c3cb3e
m_Parameters: []
m_SendTarget: {fileID: 0}
m_EnableWetMix: 0
m_Bypass: 0
--- !u!244 &8680063345532094417
AudioMixerEffectController:
m_ObjectHideFlags: 3
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name:
m_EffectID: 14375aad2b8c71942bb00259d2b03c8f
m_EffectName: Attenuation
m_MixLevel: 733f99698ea57374f807c226629a12c5
m_Parameters: []
m_SendTarget: {fileID: 0}
m_EnableWetMix: 0
m_Bypass: 0
--- !u!243 &9185764251035995994
AudioMixerGroupController:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Music
m_AudioMixer: {fileID: 24100000}
m_GroupID: 1f218ab66c2b2b641b0d2ac1c8714263
m_Children: []
m_Volume: 5542a83cb62d88f4bb57676181f23921
m_Pitch: d1b13bce1c2d3c047975c89e79aeff05
m_Send: 00000000000000000000000000000000
m_Effects:
- {fileID: -5700573295807266226}
m_UserColorIndex: 0
m_Mute: 0
m_Solo: 0
m_BypassEffects: 0

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3162afbd7fefd6c40bce53982808aa3c
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,326 @@
using UnityEngine;
using UnityEngine.Audio;
namespace TEngine.Runtime
{
public class TAudio
{
#region Propreties
private int _id = 0;
public AssetData _assetData = null;
public AudioSource _source = null;
Transform _transform = null;
private float _volume = 1.0f;
private float _duration = 0;
private float _fadeoutTimer = 0f;
private const float FadeoutDuration = 0.2f;
private bool _inPool = false;
#endregion
#region Public Propreties
enum State
{
None,
Loading,
Playing,
FadingOut,
End,
};
State _state = State.None;
class LoadRequest
{
public string path;
public bool bAsync;
}
LoadRequest _pendingLoad = null;
public int ID
{
get
{
return _id;
}
}
public float Volume
{
set
{
if (_source != null)
{
_volume = value;
_source.volume = _volume;
}
}
get
{
return _volume;
}
}
public bool IsFinish
{
get
{
if (_source != null)
{
return _state == State.End;
}
else
{
return true;
}
}
}
public float Duration
{
get
{
return _duration;
}
}
public float Length
{
get
{
if (_source != null && _source.clip != null)
{
return _source.clip.length;
}
return 0;
}
}
public Vector3 Position
{
get
{
return _transform.position;
}
set
{
_transform.position = value;
}
}
public bool IsLoop
{
get
{
if (_source != null)
{
return _source.loop;
}
else
{
return false;
}
}
set
{
if (_source != null)
{
_source.loop = value;
}
}
}
internal bool IsPlaying
{
get
{
if (_source != null && _source.isPlaying)
{
return true;
}
else
{
return false;
}
}
}
#endregion
public AudioSource AudioResource()
{
return _source;
}
public static TAudio Create(string path, bool bAsync, AudioMixerGroup audioMixerGroup = null, bool bInPool = false)
{
TAudio audio = new TAudio();
audio.Init(audioMixerGroup);
audio.Load(path, bAsync, bInPool);
return audio;
}
public void Init(AudioMixerGroup audioMixerGroup = null)
{
GameObject root = new GameObject("Audio");
root.transform.SetParent(AudioMgr.Instance.transform);
root.transform.localPosition = Vector3.zero;
_transform = root.transform;
_source = root.AddComponent<AudioSource>();
_source.playOnAwake = false;
if (audioMixerGroup != null)
{
_source.outputAudioMixerGroup = audioMixerGroup;
}
_id = _source.GetInstanceID();
}
public void Load(string path, bool bAsync, bool bInPool = false)
{
_inPool = bInPool;
if (_state == State.None || _state == State.End)
{
_duration = 0;
if (!string.IsNullOrEmpty(path))
{
if (AudioMgr.Instance.AudioClipPool.ContainsKey(path))
{
OnAssetLoadComplete(AudioMgr.Instance.AudioClipPool[path]);
return;
}
if (bAsync)
{
_state = State.Loading;
ResMgr.Instance.GetAssetAsync(path, false, OnAssetLoadComplete);
}
else
{
OnAssetLoadComplete(ResMgr.Instance.GetAsset(path, false));
}
}
}
else
{
_pendingLoad = new LoadRequest { path = path, bAsync = bAsync };
if (_state == State.Playing)
{
Stop(true);
}
}
}
public void Stop(bool fadeout = false)
{
if (_source != null)
{
if (fadeout)
{
_fadeoutTimer = FadeoutDuration;
_state = State.FadingOut;
}
else
{
_source.Stop();
_state = State.End;
}
}
}
void OnAssetLoadComplete(AssetData assetData)
{
if (assetData != null)
{
assetData.OnAsyncLoadComplete -= OnAssetLoadComplete;
if (_inPool && !AudioMgr.Instance.AudioClipPool.ContainsKey(assetData.Path))
{
assetData.AddRef();
AudioMgr.Instance.AudioClipPool.Add(assetData.Path, assetData);
}
}
if (_pendingLoad != null)
{
assetData.AddRef();
if (assetData != null)
{
assetData.DecRef();
}
_state = State.End;
string path = _pendingLoad.path;
bool bAsync = _pendingLoad.bAsync;
_pendingLoad = null;
Load(path, bAsync);
}
else if (assetData != null)
{
assetData.AddRef();
if (_assetData != null)
{
_assetData.DecRef();
}
_assetData = assetData;
_source.clip = _assetData.AssetObject as AudioClip;
if (_source.clip != null)
{
_source.Play();
_state = State.Playing;
}
else
{
_state = State.End;
}
}
else
{
_state = State.End;
}
}
public void Update(float delta)
{
if (_state == State.Playing)
{
if (!_source.isPlaying)
{
_state = State.End;
}
}
else if (_state == State.FadingOut)
{
if (_fadeoutTimer > 0f)
{
_fadeoutTimer -= delta;
_source.volume = _volume * _fadeoutTimer / FadeoutDuration;
}
else
{
Stop();
if (_pendingLoad != null)
{
string path = _pendingLoad.path;
bool bAsync = _pendingLoad.bAsync;
_pendingLoad = null;
Load(path, bAsync);
}
_source.volume = _volume;
}
}
_duration += delta;
}
public void Destroy()
{
if (_transform != null)
{
Object.Destroy(_transform.gameObject);
}
if (_assetData != null)
{
_assetData.DecRef();
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 640620be2dcc1d34ca617d92b2bac265
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0b6cc1f73b60ad749b2c4528bd5fc051
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,192 @@
using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
namespace TEngine.Runtime
{
/// <summary>
/// 过滤配置
/// </summary>
/// <typeparam name="TType"></typeparam>
/// <param name="val"></param>
/// <returns></returns>
public delegate bool FilterResBin<TType>(TType val);
/// <summary>
/// 计算拼接Key
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
/// <param name="val"></param>
/// <returns></returns>
public delegate TKey ConvertDictionaryKey<TKey, TValue>(TValue val);
public class ResConfigUtil
{
private static StringBuilder m_strBuilder = new StringBuilder();
private static readonly string m_split = "_";
#region
public static List<T> ReadConfigListRes<T>(string fileName = "")
{
if (string.IsNullOrEmpty(fileName))
{
fileName = typeof(T).Name;
}
string resPath = string.Format("Config/{0}.json",fileName);
TextAsset jsonStr = TResources.Load<TextAsset>(resPath);
if (jsonStr == null)
{
TLogger.LogError("Config {0} Read Json Error", fileName);
return null;
}
List<T> list = new List<T>();
var jsonData = JsonHelper.Instance.Deserialize<List<T>>(jsonStr.text);
list = jsonData;
return list;
}
public static List<T> ReadResBinDict<K,T>(Dictionary<K, T> dic,ConvertDictionaryKey<K,T> convKey ,string fileName = "")
{
if (string.IsNullOrEmpty(fileName))
{
fileName = typeof(T).Name;
}
string resPath = string.Format("Config/{0}.json", fileName);
TextAsset jsonStr = TResources.Load<TextAsset>(resPath);
if (jsonStr == null)
{
TLogger.LogError("Config {0} Read Json Error", fileName);
return null;
}
var jsonData = JsonHelper.Instance.Deserialize<List<T>>(jsonStr.text);
var etr = jsonData.GetEnumerator();
if(dic == null)
{
dic = new Dictionary<K, T>();
}
else
{
dic.Clear();
}
while (etr.MoveNext())
{
var key = convKey(etr.Current);
{
if (dic.ContainsKey(key))
{
TLogger.LogError("Config {0} Load Error, Repeat config {1}",typeof(T).ToString(),key.ToString());
}
dic.Add(key, etr.Current);
}
}
etr.Dispose();
return jsonData;
}
public static List<T> ReadResBinDict<K, T>(Dictionary<K, List<T>> dict, ConvertDictionaryKey<K, T> convKey, string fileName = "")
{
if (string.IsNullOrEmpty(fileName))
{
fileName = typeof(T).Name;
}
string resPath = string.Format("Config/{0}.json", fileName);
TextAsset jsonStr = TResources.Load<TextAsset>(resPath);
if (jsonStr == null)
{
TLogger.LogError("Config {0} Read Json Error", fileName);
return null;
}
var jsonData = JsonHelper.Instance.Deserialize<List<T>>(jsonStr.text);
var etr = jsonData.GetEnumerator();
if (dict == null)
{
dict = new Dictionary<K, List<T>>();
}
else
{
dict.Clear();
}
while (etr.MoveNext())
{
var data = etr.Current;
var key = convKey(data);
List<T> listItem;
if (!dict.TryGetValue(key, out listItem))
{
listItem = new List<T>();
dict.Add(key, listItem);
}
listItem.Add(data);
}
etr.Dispose();
return jsonData;
}
#endregion
public static UInt64 Make64Key(uint key1, uint key2)
{
return (((UInt64)key1) << 32) | key2;
}
public static string MakeStringKey(uint key1, uint key2, uint key3)
{
m_strBuilder.Length = 0;
m_strBuilder.Append(key1);
m_strBuilder.Append(m_split);
m_strBuilder.Append(key2);
m_strBuilder.Append(m_split);
m_strBuilder.Append(key3);
return m_strBuilder.ToString();
}
public static string MakeStringKey(string key1, uint key2)
{
m_strBuilder.Length = 0;
m_strBuilder.Append(key1);
m_strBuilder.Append(m_split);
m_strBuilder.Append(key2);
return m_strBuilder.ToString();
}
public static string MakeStringKey(string key1, string key2)
{
m_strBuilder.Length = 0;
m_strBuilder.Append(key1);
m_strBuilder.Append(m_split);
m_strBuilder.Append(key2);
return m_strBuilder.ToString();
}
}
}
/*
*
===》 example 《===
public class BufferMgr : Singleton<BufferMgr>
{
private Dictionary<string, BuffConfig> m_dictBaseConfig = new Dictionary<string, BuffConfig>();
public BufferMgr()
{
m_dictBaseConfig = ResConfigUtil.ReadConfigRes<BuffConfig>();
}
public Dictionary<string, BuffConfig> GetBuffConfig()
{
return m_dictBaseConfig;
}
}
*
*/

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 47732fcfbdbefb94e9922877e93e81ae
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ddadc0410f1c53a46b575aee186bc96a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,90 @@
using System.Collections.Generic;
namespace TEngine.Runtime
{
public interface IResRawListInterface<T>
{
List<T> RawList { get; }
}
public class ResDataBase<T> : IResRawListInterface<T>
{
private IResRawListInterface<T> m_sourceRawList = null;
private string m_fileName = null;
private List<T> m_rawList = null;
private bool m_loaded = false;
public List<T> RawList
{
get
{
CheckLoad();
return m_rawList;
}
}
protected void InitBase(string fileName)
{
m_fileName = fileName;
}
protected void InitBase(IResRawListInterface<T> sourceList)
{
m_sourceRawList = sourceList;
}
protected void ClearBase()
{
m_loaded = false;
m_rawList = null;
}
protected void CheckLoad()
{
if (m_loaded)
{
return;
}
#if UNITY_EDITOR
GameTickWatcher tickWatcher = new GameTickWatcher();
#endif
m_loaded = true;
if (m_sourceRawList != null)
{
m_rawList = LoadFromSourceList(m_sourceRawList);
}
else
{
m_rawList = LoadFromFile(m_fileName);
}
if (m_rawList == null)
{
m_rawList = new List<T>();
}
#if UNITY_EDITOR
TLogger.LogInfoSuccessd("Read Config {0} Used Time: {1}", typeof(T).ToString(), tickWatcher.ElapseTime());
#endif
}
#region
protected virtual List<T> LoadFromFile(string fileName)
{
return null;
}
protected virtual List<T> LoadFromSourceList(IResRawListInterface<T> sourceList)
{
return null;
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f66e08f903a6801468e66394daca48d0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,104 @@
using System.Collections.Generic;
namespace TEngine.Runtime
{
public class ResDictionary<K, T> : ResDataBase<T> where T : new()
{
private FilterResBin<T> m_filter = null;
private ConvertDictionaryKey<K, T> m_convKey = null;
private Dictionary<K, T> m_data = null;
public Dictionary<K, T> Data
{
get
{
CheckLoad();
return m_data;
}
}
public void Init(string fileName, ConvertDictionaryKey<K, T> convKey)
{
InitBase(fileName);
m_convKey = convKey;
m_filter = null;
}
public void Init(ConvertDictionaryKey<K, T> convKey)
{
Init(string.Empty, convKey);
}
public void Init(IResRawListInterface<T> rawList, ConvertDictionaryKey<K, T> convKey, FilterResBin<T> filter = null)
{
InitBase(rawList);
m_convKey = convKey;
m_filter = filter;
}
public void Clear()
{
m_data = null;
ClearBase();
}
protected override List<T> LoadFromSourceList(IResRawListInterface<T> sourceList)
{
m_data = new Dictionary<K, T>();
var rawList = sourceList.RawList;
for (int i = 0; i < rawList.Count; i++)
{
var config = rawList[i];
if (m_filter != null && !m_filter(config))
{
continue;
}
var key = m_convKey(config);
if (m_data.ContainsKey(key))
{
TLogger.LogError("Config {0} load error, repeat config: {0}", typeof(T).ToString(), key.ToString());
continue;
}
m_data.Add(key, config);
}
return rawList;
}
protected override List<T> LoadFromFile(string fileName)
{
m_data = new Dictionary<K, T>();
//读取文件不支持filter,实际也没这个需求
TLogger.LogAssert(m_filter == null);
List<T> rawList;
if (string.IsNullOrEmpty(fileName))
{
rawList = ResConfigUtil.ReadResBinDict(m_data, m_convKey);
}
else
{
rawList = ResConfigUtil.ReadResBinDict(m_data, m_convKey,fileName);
}
return rawList;
}
public bool TryGetValue(K key, out T itemData, bool showLog = false)
{
if (Data.TryGetValue(key, out itemData))
{
return true;
}
if (showLog)
{
TLogger.LogError("get config {0} failed, key: {1}!", typeof(T), key);
}
return false;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: efcf1d5d517018a4795f384a746dcb5b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,87 @@
using System.Collections.Generic;
namespace TEngine.Runtime
{
class ResDictionaryList<K, T> : ResDataBase<T> where T : new()
{
private ConvertDictionaryKey<K, T> m_convKey = null;
private Dictionary<K, List<T>> m_data = null;
public Dictionary<K, List<T>> Data
{
get
{
CheckLoad();
return m_data;
}
}
public void Init(string fileName, ConvertDictionaryKey<K, T> convKey, FilterResBin<T> filter = null)
{
InitBase(fileName);
m_convKey = convKey;
}
public void Init(ConvertDictionaryKey<K, T> convKey)
{
Init(string.Empty, convKey);
}
/// <summary>
/// 构造list数据结构依赖基础的数据源
/// </summary>
/// <param name="sourceList"></param>
/// <param name="convKey"></param>
/// <param name="filter"></param>
public void Init(IResRawListInterface<T> sourceList, ConvertDictionaryKey<K, T> convKey, FilterResBin<T> filter = null)
{
InitBase(sourceList);
m_convKey = convKey;
}
public void Clear()
{
m_data = null;
ClearBase();
}
protected override List<T> LoadFromSourceList(IResRawListInterface<T> sourceList)
{
m_data = new Dictionary<K, List<T>>();
var rawList = sourceList.RawList;
for (int i = 0; i < rawList.Count; i++)
{
var config = rawList[i];
var key = m_convKey(config);
List<T> listData;
if (!m_data.TryGetValue(key, out listData))
{
listData = new List<T>();
m_data.Add(key, listData);
}
listData.Add(config);
}
return rawList;
}
protected override List<T> LoadFromFile(string fileName)
{
m_data = new Dictionary<K, List<T>>();
List<T> list;
if (string.IsNullOrEmpty(fileName))
{
list = ResConfigUtil.ReadResBinDict(m_data, m_convKey);
}
else
{
list = ResConfigUtil.ReadResBinDict(m_data, m_convKey,fileName);
}
return list;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b1c0c610ab7c2404992367ef40ec7a9c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
namespace TEngine.Runtime
{
public class ResList<T> : ResDataBase<T> where T : new()
{
private Comparison<T> m_comparer;
public List<T> Data
{
get { return RawList; }
}
/// <summary>
/// 初始化配置
/// </summary>
/// <param name="comparer">如果需要排序,那么传入排序函数</param>
public void Init(Comparison<T> comparer = null)
{
InitBase(string.Empty);
m_comparer = comparer;
}
/// <summary>
/// 初始化配置
/// </summary>
/// <param name="fileName">指定配置文件名</param>
/// <param name="comparer">如果需要排序,那么传入排序函数</param>
public void Init(string fileName, Comparison<T> comparer = null)
{
InitBase(fileName);
m_comparer = comparer;
}
public void Clear()
{
ClearBase();
}
protected override List<T> LoadFromFile(string fileName)
{
List<T> listData;
if (string.IsNullOrEmpty(fileName))
{
listData = ResConfigUtil.ReadConfigListRes<T>();
}
else
{
listData = ResConfigUtil.ReadConfigListRes<T>(fileName);
}
if (m_comparer != null)
{
listData.Sort(m_comparer);
}
return listData;
}
protected override List<T> LoadFromSourceList(IResRawListInterface<T> sourceList)
{
if (m_comparer != null)
{
List<T> listSorted = new List<T>();
var list = sourceList.RawList;
for (int i = 0; i < list.Count; i++)
{
listSorted.Add(list[i]);
}
listSorted.Sort(m_comparer);
return listSorted;
}
return sourceList.RawList;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e387a6e0873c0724981315547f5d83c1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: be542af7cb61ded4797e1c811f855e89
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7e1909ecd7ab1cf4fb32c7aae20b74b9
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0429f063053ec3c47b11dcbcb8a6a25d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,128 @@
using System;
using System.Runtime.InteropServices;
namespace TEngine.Runtime
{
/// <summary>
/// 类型和名称的组合值。
/// </summary>
[StructLayout(LayoutKind.Auto)]
internal struct TypeNamePair : IEquatable<TypeNamePair>
{
private readonly Type m_Type;
private readonly string m_Name;
/// <summary>
/// 初始化类型和名称的组合值的新实例。
/// </summary>
/// <param name="type">类型。</param>
public TypeNamePair(Type type)
: this(type, string.Empty)
{
}
/// <summary>
/// 初始化类型和名称的组合值的新实例。
/// </summary>
/// <param name="type">类型。</param>
/// <param name="name">名称。</param>
public TypeNamePair(Type type, string name)
{
if (type == null)
{
throw new Exception("Type is invalid.");
}
m_Type = type;
m_Name = name ?? string.Empty;
}
/// <summary>
/// 获取类型。
/// </summary>
public Type Type
{
get
{
return m_Type;
}
}
/// <summary>
/// 获取名称。
/// </summary>
public string Name
{
get
{
return m_Name;
}
}
/// <summary>
/// 获取类型和名称的组合值字符串。
/// </summary>
/// <returns>类型和名称的组合值字符串。</returns>
public override string ToString()
{
if (m_Type == null)
{
throw new Exception("Type is invalid.");
}
string typeName = m_Type.FullName;
return string.IsNullOrEmpty(m_Name) ? typeName : string.Format("{0}.{1}", typeName, m_Name);
}
/// <summary>
/// 获取对象的哈希值。
/// </summary>
/// <returns>对象的哈希值。</returns>
public override int GetHashCode()
{
return m_Type.GetHashCode() ^ m_Name.GetHashCode();
}
/// <summary>
/// 比较对象是否与自身相等。
/// </summary>
/// <param name="obj">要比较的对象。</param>
/// <returns>被比较的对象是否与自身相等。</returns>
public override bool Equals(object obj)
{
return obj is TypeNamePair && Equals((TypeNamePair)obj);
}
/// <summary>
/// 比较对象是否与自身相等。
/// </summary>
/// <param name="value">要比较的对象。</param>
/// <returns>被比较的对象是否与自身相等。</returns>
public bool Equals(TypeNamePair value)
{
return m_Type == value.m_Type && m_Name == value.m_Name;
}
/// <summary>
/// 判断两个对象是否相等。
/// </summary>
/// <param name="a">值 a。</param>
/// <param name="b">值 b。</param>
/// <returns>两个对象是否相等。</returns>
public static bool operator ==(TypeNamePair a, TypeNamePair b)
{
return a.Equals(b);
}
/// <summary>
/// 判断两个对象是否不相等。
/// </summary>
/// <param name="a">值 a。</param>
/// <param name="b">值 b。</param>
/// <returns>两个对象是否不相等。</returns>
public static bool operator !=(TypeNamePair a, TypeNamePair b)
{
return !(a == b);
}
}
}

Some files were not shown because too many files have changed in this diff Show More