整理项目

This commit is contained in:
xiekaidong 2023-04-03 11:04:31 +08:00
parent 6de0517da3
commit 87af2478d9
12242 changed files with 6903866 additions and 0 deletions

19
Assets/Assets.index Normal file
View File

@ -0,0 +1,19 @@
{
"name": "Assets",
"type": "asset",
"roots": [],
"includes": [],
"excludes": [
"Temp/",
"External/"
],
"baseScore": 100,
"options": {
"disabled": false,
"files": true,
"directories": false,
"types": true,
"properties": false,
"dependencies": false
}
}

10
Assets/Assets.index.meta Normal file
View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: c94aab290ea3af541a337f4e1ced6fc5
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 11500000, guid: 595da1eac225fa84fb9c2d465cc69e8b, type: 3}

8
Assets/Editor.meta Normal file
View File

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

View File

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

View File

@ -0,0 +1,102 @@
using System.Collections;
using System.Collections.Generic;
using System;
using BF;
namespace BFEditor.Build
{
[Serializable]
public class BuildInfoCollection
{
public List<BuildInfo> infos = new List<BuildInfo>();
public BuildInfo GetFirstInfo()
{
if (infos.Count > 0)
{
return infos[0];
}
return null;
}
}
[Serializable]
public class BuildInfo
{
public string bundleName; // 包名
public string version; // 版本号
public string mode; // 渠道名_debug 或 渠道名_release
public int version_code = 1; // 各自渠道的version_code
public List<BulidGitInfo> git_info; // 打包的git信息
[NonSerialized]
public bool skipVersion = false; // 是否跳过版本校验
public bool IsGPChannel()
{
return bundleName == "com.idle.ko.io";
}
public bool IsGPOfficial()
{
return bundleName == "com.idle.ko.io";
}
// dev包使用mono编译不会导出as工程
public bool IsDevChannel()
{
return bundleName == "com.juzu.b5.dev" || bundleName == "com.juzu.b5.dev.android" ||
bundleName == "com.juzu.b5.dev.ios";
}
public bool IsReleaseChannel()
{
return !IsDevChannel();
}
// 是否是内网release
public bool IsLanRelease()
{
return bundleName == "com.juzu.b5.release.android" || bundleName == "com.juzu.b5.release.ios";
}
public bool IsPublish()
{
return !IsDevChannel() && !IsLanRelease();
}
public bool IsIOSPlatform()
{
return bundleName.Contains("ios");
}
public bool IsAndroidPlatform()
{
return !IsIOSPlatform();
}
public string GetBundleName()
{
return bundleName;
}
public string GetVersion()
{
return version;
}
public BFLanguageInfo GetLanguageInfo()
{
var languageInfo = BFPlatform.GetLanguageInfo(bundleName);
return languageInfo;
}
}
[Serializable]
public class BulidGitInfo
{
public string project_name;
public string branch;
public string hash;
}
}

View File

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

View File

@ -0,0 +1,201 @@
using System.IO;
using UnityEditor;
using UnityEngine;
using BFEditor.Resource;
using System.Collections.Generic;
namespace BFEditor.Build
{
public static class BuildMenu
{
[MenuItem("打包工具/编译并加密lua代码", priority = 1)]
static void CompileLuaManually()
{
var succ = CompileScriptsUtils.CompileAndEncryptLua(true);
if (succ)
{
EditorUtility.DisplayDialog("提示", "编译并加密Lua成功", "确定");
}
else
{
EditorUtility.DisplayDialog("提示", "编译Lua失败", "确定");
}
}
[MenuItem("打包工具/打包前准备", priority = 101)]
static void PrepareForBuild()
{
CompileScriptsUtils.RegenerateXLuaCode(true); // 重新生产XLua
MainAtlasTools.UpdateMainAtlas(); // 更新atlas
LanguageAtlasTools.UpdateLanguageAtlas(); // 更新多语言atlas
MaterialTools.ClearFbxDefaultMaterial(); // 清理fbx默认材质
ShaderDependenciesTools.ClearAllShaderDependencies(); // 清理shader依赖资源
ShaderVariantsTools.GenerateVariantCollection(); // 收集shader变体
CompileScriptsUtils.CompileAndEncryptLua(true); // 编译并且加密lua代码
AssetBundleUtils.SetAllAssetsBundleName(); // 设置abName
}
[MenuItem("打包工具/AssetBundles/设置ABName", priority = 201)]
public static void SetAssetsInfoExcludeLua()
{
AssetBundleUtils.SetAllAssetsBundleName();
EditorUtility.DisplayDialog("提示", "设置ABName成功", "确定");
}
[MenuItem("打包工具/AssetBundles/AB Build(Debug)", priority = 202)]
public static void BuildAssetBundlesDebug()
{
var outputPath = Application.streamingAssetsPath;
var success = AssetBundleUtils.BuildAssetBundles(EditorUserBuildSettings.activeBuildTarget, outputPath, false,
BuildAssetBundleOptions.ChunkBasedCompression, new BF.BFLanguageInfo(new List<string> { "en", "cn"}));
if (success)
{
EditorUtility.DisplayDialog("提示", "Build AB(Debug)成功", "确定");
}
else
{
EditorUtility.DisplayDialog("提示", "Build AB(Debug)失败", "确定");
}
}
[MenuItem("打包工具/AssetBundles/AB Build(Release)", priority = 203)]
public static void BuildAssetBundlesRelease()
{
var outputPath = Application.streamingAssetsPath;
var success = AssetBundleUtils.BuildAssetBundles(EditorUserBuildSettings.activeBuildTarget, outputPath, true,
BuildAssetBundleOptions.ChunkBasedCompression, new BF.BFLanguageInfo(new List<string> { "en", "cn"}));
if (success)
{
EditorUtility.DisplayDialog("提示", "Build AB(Release)成功", "确定");
}
else
{
EditorUtility.DisplayDialog("提示", "Build AB(Release)失败", "确定");
}
}
[MenuItem("打包工具/AssetBundles/生成AB Config", priority = 204)]
static void GenerateABConfig()
{
var outputPath = Application.streamingAssetsPath;
AssetBundleUtils.GenerateAssetBundleConfig(outputPath, new BF.BFLanguageInfo(new List<string> { "en", "cn"}));
EditorUtility.DisplayDialog("提示", "生成配置文件", "确定");
}
[MenuItem("打包工具/AssetBundles/检查循环依赖", priority = 205)]
static void CheckABCycleDepend()
{
if (!ABCycleDependUtils.CheckABCycleDepend(Application.streamingAssetsPath))
{
EditorUtility.DisplayDialog("提示", "存在循环依赖", "确定");
}
else
{
EditorUtility.DisplayDialog("提示", "不存在循环依赖", "确定");
}
}
[MenuItem("打包工具/AssetBundles/生成热更资源", priority = 206)]
static void GenUpdateRes()
{
if(!Directory.Exists(Application.streamingAssetsPath))
{
Debug.Log("streamingAssets 不存在");
return;
}
var abccPath = Path.Combine(Application.streamingAssetsPath, "ab_config.bytes");
if (!File.Exists(abccPath))
{
Debug.LogError("ab_config.bytes不存在");
return;
}
var bytes = File.ReadAllBytes(abccPath);
var json = BF.AssetBundleConfigCollection.Decompress(bytes);
var abcc = BF.AssetBundleConfigCollection.Create(json);
var dirInfo = new DirectoryInfo(Application.streamingAssetsPath);
var files = dirInfo.GetFiles("*.*", SearchOption.AllDirectories);
var assetFiles = new List<string>();
var dirPath = Path.Combine(Application.streamingAssetsPath, "../../update");
if (!Directory.Exists(dirPath))
{
Directory.CreateDirectory(dirPath);
}
else
{
Directory.Delete(dirPath, true);
Directory.CreateDirectory(dirPath);
}
// 先设置版本号并保存下ab_config
abcc.version = "1.0.0";
var newJson = abcc.ToJson();
var newBytes = BF.AssetBundleConfigCollection.Compress(newJson);
File.WriteAllBytes(Path.Combine(dirPath, "ab_config.bytes"), newBytes);
// 初始化所有ab文件以及对应的md5
var mainConfig = abcc.mainConfigs;
Dictionary<string, string> md5Dict = new Dictionary<string, string>();
for (var i = 0; i < mainConfig.Count; ++i)
{
md5Dict.Add(mainConfig[i].assetBundlePath, mainConfig[i].md5);
}
int pathLength = Application.streamingAssetsPath.Length;
for (var i = 0; i < files.Length; ++i)
{
var fileInfo = files[i];
if (fileInfo.Extension.ToLower() == ".meta")
{
continue;
}
if (fileInfo.Name.EndsWith(".DS_Store"))
{
continue;
}
if (fileInfo.Name.CompareTo("ab_config.bytes") == 0)
{
continue;
}
else
{
var md5 = BF.GameLaunchUtils.GetFileMD5(fileInfo.FullName);
var fileName = fileInfo.FullName.Substring(pathLength + 1).Replace("\\", "/");
if (md5Dict.ContainsKey(fileName))
{
if (md5Dict[fileName].CompareTo(md5) == 0)
{
md5Dict.Remove(fileName);
}
else
{
break;
}
File.Copy(fileInfo.FullName, Path.Combine(dirPath, md5));
}
else
{
Debug.Log("md5校验失败:" + fileName);
break;
}
}
}
if (md5Dict.Count <= 0)
{
Debug.Log("热更资源生成完毕,版本号:" + abcc.version);
}
else
{
Directory.Delete(dirPath, true);
Debug.Log("热更资源生成失败,版本号:" + abcc.version);
}
}
[MenuItem("打包工具/打包窗口", priority = 301)]
static void ShowBuildWindow()
{
BuildProjectWindow.ShowWindow();
}
}
}

View File

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

View File

@ -0,0 +1,328 @@
using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
using BFEditor.Resource;
using BF;
namespace BFEditor.Build
{
public static class BuildProjectTools
{
static string AssetBundleITPath = Application.dataPath + "/../Build/output/{0}";
static string AssetBundleCachePath = Application.dataPath + "/../HistoryAssetBundles";
static BuildProjectTools()
{
BuildAndroidUtils.EnsureJavaHome();
}
/// <summary>
/// 提供给neo出包接口
/// </summary>
public static void BuildBFPlayer()
{
var dict = BFEditorUtils.GetCommandLineArgs();
if (dict.TryGetValue("data", out string dataStr))
{
Debug.Log("BuildBFPlayer " + dataStr);
var json = "{\"infos\":" + dataStr + "}";
var buildInfoCollection = JsonUtility.FromJson<BuildInfoCollection>(json);
if (!BuildBFPlayer(buildInfoCollection.GetFirstInfo()))
{
throw new Exception();
}
}
else
{
throw new Exception();
}
}
public static bool BuildBFPlayer(BuildInfo buildInfo)
{
// assetbundles
if (!BuildResources(buildInfo, Application.streamingAssetsPath, true))
{
return false;
}
// 删除不进包的多语言
// if (!DeleteOverLanguageAb(buildInfo))
// {
// return false;
// }
// 打包
if (!BuildPlayer(buildInfo))
{
Debug.LogError("[bferror]buildPlayer失败");
return false;
}
AssetDatabase.Refresh();
return true;
}
static bool BuildPlayer(BuildInfo buildInfo)
{
Debug.Log("[bfinfo]开始打包, bundleName : " + buildInfo.bundleName + " version : " + buildInfo.version + " mode : " + buildInfo.mode);
var watch = new System.Diagnostics.Stopwatch();
watch.Start();
var result = false;
if (buildInfo.IsAndroidPlatform() && BuildAndroidUtils.BuildAndroidPlayer(buildInfo))
{
result = true;
}
#if UNITY_EDITOR_OSX
if (buildInfo.IsIOSPlatform() && BuildIOSUtils.BuildIOSPlayer(buildInfo))
{
result = true;
}
#endif
if (result)
{
watch.Stop();
Debug.Log("[bfinfo]打包完成,耗时: " + BFEditorUtils.GetTimeStr(watch.ElapsedMilliseconds));
}
return result;
}
/// <summary>
/// 提供给neo打热更接口
/// </summary>
public static void BuildResources()
{
var dict = BFEditorUtils.GetCommandLineArgs();
if (dict.TryGetValue("data", out string dataStr))
{
Debug.Log("BuildResources " + dataStr);
var json = "{\"infos\":" + dataStr + "}";
var buildInfoCollection = JsonUtility.FromJson<BuildInfoCollection>(json);
var bulidInfo = buildInfoCollection.GetFirstInfo();
var itPath = string.Format(AssetBundleITPath, bulidInfo.mode);
if (!BuildResources(bulidInfo, itPath, false))
{
throw new Exception();
}
}
else
{
throw new Exception();
}
}
public static bool BuildResources(BuildInfo buildInfo, string outputPath, bool needCopyToITPath)
{
if (Directory.Exists(outputPath))
{
Directory.Delete(outputPath, true);
}
// 首先尝试从缓存获取
// if (TryGetAbFromCache(buildInfo, outputPath))
// {
// // 拷贝资源到it上传路径
// // if (needCopyToITPath)
// // {
// // CopyResToITPath(buildInfo, outputPath);
// // }
// return true;
// }
var watch = new System.Diagnostics.Stopwatch();
watch.Start();
// var buildTarget = buildInfo.IsIOSPlatform() ? BuildTarget.iOS : BuildTarget.Android;
var isRelease = buildInfo.IsReleaseChannel();
Debug.Log("[bfinfo]开始打包资源, bundleName : " + buildInfo.bundleName + " version : " + buildInfo.version + " mode : " + buildInfo.mode);
// // 检查平台
// if (EditorUserBuildSettings.activeBuildTarget != buildTarget)
// {
// Debug.LogError("[bferror]当前没有在对应平台");
// return false;
// }
// 更新atlas
MainAtlasTools.UpdateMainAtlas();
// LanguageAtlasTools.UpdateLanguageAtlas();
// 生成tmp字体
TMPTools.GenFormalFontAsset(false);
// 清理fbx默认材质
MaterialTools.ClearFbxDefaultMaterial();
// 清理shader依赖资源
ShaderDependenciesTools.ClearAllShaderDependencies();
// 收集shader变体
ShaderVariantsTools.GenerateVariantCollection();
// ios因为环境问题先不编译打版本前提前编译好
if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.Android)
{
// 编译并且加密lua代码
if (!CompileScriptsUtils.CompileAndEncryptLua(!isRelease))
{
Debug.LogError("[bferror]lua代码编译失败");
return false;
}
}
// 设置abName
AssetBundleUtils.SetAllAssetsBundleName();
// 打ab资源
if (!AssetBundleUtils.BuildAssetBundles(EditorUserBuildSettings.activeBuildTarget, outputPath, isRelease, BuildAssetBundleOptions.ChunkBasedCompression,
buildInfo.GetLanguageInfo(), buildInfo.version))
{
Debug.LogError("[bferror]打ab失败");
return false;
}
// 检查循环依赖
if (!ABCycleDependUtils.CheckABCycleDepend(outputPath))
{
Debug.LogError("[bferror]打ab失败ab存在循环依赖");
return false;
}
// 本地缓存ab
if (buildInfo.IsReleaseChannel())
{
Debug.Log("[bfinfo]正在缓存assetbundles...");
BFEditorUtils.CopyDirWithIgnore(outputPath, Path.Combine(AssetBundleCachePath, buildInfo.version), new List<string> { ".meta" });
}
// 拷贝资源到it上传路径
// if (needCopyToITPath)
// {
// CopyResToITPath(buildInfo, outputPath);
// }
watch.Stop();
Debug.Log("[bfinfo]资源打包完成,耗时: " + BFEditorUtils.GetTimeStr(watch.ElapsedMilliseconds));
return true;
}
/// <summary>
/// 尝试从缓存中获取ab会进行md5校验
/// </summary>
static bool TryGetAbFromCache(BuildInfo buildInfo, string outputPath)
{
var cachePath = Path.Combine(AssetBundleCachePath, buildInfo.version);
if (!Directory.Exists(cachePath))
{
return false;
}
if (CheckCacheAbMd5(cachePath, buildInfo))
{
Debug.Log(string.Format("[bfinfo]正在拷贝本地缓存资源 {0} ---> {1}", cachePath, outputPath));
BFEditorUtils.CopyDirWithIgnore(cachePath, outputPath, new List<string> { ".meta" });
}
return true;
}
public static void CopyResToITPath(BuildInfo buildInfo, string outputPath)
{
Debug.Log("[bfinfo]正在拷贝assetbundles到运维上传路径...");
var itPath = string.Format(AssetBundleITPath, buildInfo.mode);
if (Directory.Exists(itPath))
{
Directory.Delete(itPath, true);
}
BFEditorUtils.CopyDirWithIgnore(outputPath, itPath, new List<string> { ".meta" });
}
/// <summary>
/// 校验缓存资源
/// </summary>
static bool CheckCacheAbMd5(string abPath, BuildInfo buildInfo)
{
Debug.Log("[bfinfo]正在校验缓存资源...");
var abccPath = Path.Combine(abPath, "ab_config.bytes");
if (!File.Exists(abccPath))
{
Debug.LogError("[bferror]校验缓存资源失败 ab_config.bytes不存在");
return false;
}
var bytes = File.ReadAllBytes(abccPath);
var json = AssetBundleConfigCollection.Decompress(bytes);
var abcc = AssetBundleConfigCollection.Create(json);
foreach (var abc in abcc.mainConfigs)
{
var path = Path.Combine(abPath, abc.assetBundlePath);
if (!File.Exists(path))
{
Debug.LogError(string.Format("[bferror]校验缓存资源失败 {0} 文件丢失", abc.assetBundlePath));
return false;
}
var md5 = GameLaunchUtils.GetFileMD5(path);
if (md5 != abc.md5)
{
Debug.LogError(string.Format("[bferror]校验缓存资源失败 {0} 文件md5不一致", abc.assetBundlePath));
return false;
}
}
// 只有版本号不一样, 则修改abConfig的版本号
if (abcc.version != buildInfo.version)
{
Debug.Log(string.Format("[bfinfo]资源版本号不一样,修改版本号,缓存资源版本{0}, 目标版本{1}", abcc.version, buildInfo.version));
abcc.version = buildInfo.version;
var newJson = abcc.ToJson();
var newBytes = AssetBundleConfigCollection.Compress(newJson);
File.WriteAllBytes(abccPath, newBytes);
}
Debug.Log("[bfinfo]缓存资源校验完成");
return true;
}
/// <summary>
/// 剔除冗余的多语言资源
/// </summary>
static bool DeleteOverLanguageAb(BuildInfo buildInfo)
{
var bundleName = buildInfo.bundleName;
var languageInfo = BFPlatform.GetLanguageInfo(bundleName);
if (languageInfo == null)
{
Debug.LogError("[bferror]没有对应的多语言info " + bundleName);
return false;
}
foreach (var languagePath in AssetBundleUtils.languagePathList)
{
var languageAbDirPath = Application.streamingAssetsPath + "/" + languagePath;
var subLanguageDirs = Directory.GetDirectories(languageAbDirPath);
foreach (var dir in subLanguageDirs)
{
var dirInfo = new DirectoryInfo(dir);
var name = dirInfo.Name;
if (languageInfo.Contains(name))
{
continue;
}
Debug.Log("[bfinfo]剔除不进包的多语言资源 " + dirInfo.FullName);
dirInfo.Delete(true);
}
}
return true;
}
}
}

View File

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

View File

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

View File

@ -0,0 +1,89 @@
using UnityEditor;
using UnityEngine;
namespace BFEditor.Build
{
public enum BFPlatformOptions
{
AndroidDev = 1,
IOSDev,
AndroidRelease,
AndroidGP,
}
public class BuildProjectWindow : EditorWindow
{
BFPlatformOptions platform = BFPlatformOptions.AndroidDev;
const string ANDROID_DEV_PACKAGE_NAME = "com.juzu.b5.dev.android";
const string ANDROID_RELEASE_PACKAGE_NAME = "com.juzu.b5.release.android";
const string ANDROID_GP_PACKAGE_NAME = "com.idle.ko.io";
const string IOS_PACKAGE_NAME = "com.juzu.b5.dev.ios";
public BuildProjectWindow()
{
titleContent = new GUIContent("打包");
}
private void OnEnable() { }
void OnGUI()
{
GUILayout.BeginVertical("box");
EditorGUILayout.LabelField("选择渠道");
platform = (BFPlatformOptions)EditorGUILayout.EnumPopup("", platform);
EditorGUILayout.Space();
EditorGUILayout.LabelField("版本: 0.1.0");
EditorGUILayout.Space();
string packageName;
string mode;
bool skipVersion = false;
if (platform == BFPlatformOptions.AndroidDev)
{
packageName = ANDROID_DEV_PACKAGE_NAME;
skipVersion = true;
mode = "dev_debug";
}
else if(platform == BFPlatformOptions.AndroidRelease)
{
packageName = ANDROID_RELEASE_PACKAGE_NAME;
mode = "release_release";
}
else if(platform == BFPlatformOptions.AndroidGP)
{
packageName = ANDROID_GP_PACKAGE_NAME;
mode = "publish_release";
}
else
{
packageName = IOS_PACKAGE_NAME;
mode = "dev_debug";
}
EditorGUILayout.LabelField("包名: " + packageName);
EditorGUILayout.LabelField("mode: " + mode);
EditorGUILayout.Space();
EditorGUILayout.Space();
if (GUILayout.Button("一键打包"))
{
var buildInfo = new BuildInfo();
buildInfo.version = "0.1.0";
buildInfo.mode = mode;
buildInfo.bundleName = packageName;
buildInfo.skipVersion = skipVersion;
BuildProjectTools.BuildBFPlayer(buildInfo);
}
GUILayout.EndVertical();
}
public static void ShowWindow()
{
var window = GetWindow<BuildProjectWindow>();
window.Show();
}
}
}

View File

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

View File

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

View File

@ -0,0 +1,81 @@
using System.Linq;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;
namespace BFEditor.Build
{
public class ABCycleDependUtils
{
static AssetBundleManifest manifest;
public static bool CheckABCycleDepend(string outputPath)
{
Debug.Log("[bfinfo]检查循环依赖...");
manifest = GetManifest(outputPath);
if (manifest == null)
{
return true;
}
var result = true;
var allABs = manifest.GetAllAssetBundles();
var totalCount = allABs.Length;
var unit = (float)totalCount / 100;
var index = 1;
var i = 0;
foreach (var ab in allABs)
{
if (i >= index * unit)
{
EditorUtility.DisplayProgressBar("提示", "正在检查ab循环依赖...", (float)index / 100);
index++;
}
result = CheckCycleDepend(ab) && result;
i++;
}
EditorUtility.ClearProgressBar();
return result;
}
static AssetBundleManifest GetManifest(string outputPath)
{
var streamPath = Path.Combine(outputPath, "asset_bundle_manifest.ab");
if (!File.Exists(streamPath))
{
Debug.LogError("文件不存在 " + streamPath);
return null;
}
var assetBundle = AssetBundle.LoadFromFile(streamPath);
var manifest = assetBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
assetBundle.Unload(false);
return manifest;
}
static bool CheckCycleDepend(string assetBundle)
{
var result = true;
var depends = manifest.GetAllDependencies(assetBundle);
foreach (var depend in depends)
{
var depend2 = manifest.GetAllDependencies(depend);
if (depend2.Contains(assetBundle))
{
Debug.LogError("存在循环依赖 " + assetBundle + " " + depend);
result = false;
}
}
return result;
}
}
}

View File

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

View File

@ -0,0 +1,949 @@
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Collections;
using BF;
namespace BFEditor.Build
{
struct ABNameInfo
{
public string name;
public string path;
}
public class AssetBundleUtils
{
const string ASSETS_STR = "Assets/";
const string FIRST_LUA = "lua/app/first/first.lua.bytes";
const string PROTOC_LUA = "lua/app/first/protoc.lua.bytes";
const string FIRST_TEXT_LUA = "lua/app/first/first_text.lua.bytes";
static string assetDirectory = Application.dataPath + "/";
public static List<string> languagePathList = new List<string> { "arts/language/", "lua/app/config/strings/" };
//需要忽略的文件后缀
static Dictionary<string, bool> ignoreResSuffix = new Dictionary<string, bool>() {
{ ".meta", true },
{ ".ds_store", true },
{ ".cs", true },
{ ".fnt", true },
{ ".unity", true },
{ ".ttf", true },
{ ".otf", true },
};
public static void ClearStreamingAssets()
{
Debug.Log("[bfinfo]清理streamingAssets...");
EditorUtility.DisplayProgressBar("提示", "正在清理streamingAssets...", 1);
var path = Path.Combine(Application.dataPath, "StreamingAssets");
if (Directory.Exists(path))
{
var di = new DirectoryInfo(path);
di.Delete(true);
AssetDatabase.Refresh();
}
EditorUtility.ClearProgressBar();
}
/// <summary>
/// 打对应平台下的AB
/// </summary>
public static bool BuildAssetBundles(BuildTarget target, string outputPath, bool release, BuildAssetBundleOptions options,
BFLanguageInfo languageInfo, string version = "0.1.0")
{
Debug.Log("[bfinfo]正在build assetbundle...");
if (languageInfo == null)
{
Debug.LogError("[bferror]BuildAssetBundles: languageInfo = null");
return false;
}
var streamingPath = Application.streamingAssetsPath;
if (Directory.Exists(streamingPath))
{
Directory.Delete(streamingPath, true);
}
if (!Directory.Exists(outputPath))
{
Directory.CreateDirectory(outputPath);
}
AssetBundleManifest manifest;
if (release)
{
manifest = BuildPipeline.BuildAssetBundles(outputPath, options |
BuildAssetBundleOptions.StrictMode |
BuildAssetBundleOptions.DisableLoadAssetByFileName |
BuildAssetBundleOptions.DisableLoadAssetByFileNameWithExtension |
BuildAssetBundleOptions.DeterministicAssetBundle,
target);
}
else
{
manifest = BuildPipeline.BuildAssetBundles(outputPath, options |
BuildAssetBundleOptions.StrictMode |
BuildAssetBundleOptions.DisableLoadAssetByFileName |
BuildAssetBundleOptions.DisableLoadAssetByFileNameWithExtension |
BuildAssetBundleOptions.DeterministicAssetBundle,
target);
}
if (manifest == null)
{
return false;
}
if (!RebuildMp4AssetsBundle(outputPath, target))
{
return false;
}
DeleteManifest(outputPath);
RenameManifest(outputPath);
GenerateAssetBundleConfig(outputPath, languageInfo, version); //生成 assetbundleconfig 配置文件
AssetDatabase.Refresh();
return true;
}
static void DeleteManifest(string abPath)
{
var dir = new DirectoryInfo(abPath);
var files = dir.GetFiles("*.manifest", SearchOption.AllDirectories);
for (var i = 0; i < files.Length; ++i)
{
var fileInfo = files[i];
FileUtil.DeleteFileOrDirectory(fileInfo.FullName);
}
AssetDatabase.Refresh();
}
public static void RenameManifest(string abPath)
{
var manifestName = "";
int subIndex;
if (abPath[abPath.Length - 1] == '/')
{
subIndex = abPath.LastIndexOf('/', abPath.Length - 2);
if (subIndex >= 0)
{
manifestName = abPath.Substring(subIndex + 1, abPath.Length - subIndex - 2);
}
}
else
{
subIndex = abPath.LastIndexOf('/');
if (subIndex >= 0)
{
manifestName = abPath.Substring(subIndex + 1);
}
}
var manifestPath = Path.Combine(abPath, manifestName);
var manifestNewPath = Path.Combine(abPath, "asset_bundle_manifest.ab");
Debug.Log(manifestPath);
Debug.Log(manifestNewPath);
File.Move(manifestPath, manifestNewPath);
AssetDatabase.Refresh();
}
public static void GenerateAssetBundleConfig(string abPath, BFLanguageInfo languageInfo, string version = "0.1.0")
{
var abcc = new AssetBundleConfigCollection();
abcc.version = version;
var manifestPath = Path.Combine(abPath, "asset_bundle_manifest.ab");
var fileInfo = new FileInfo(manifestPath);
var abc = new AssetBundleConfig();
abc.assetBundlePath = "asset_bundle_manifest.ab";
abc.md5 = GetFileMD5(manifestPath);
abc.rawSize = fileInfo.Length;
abcc.AddAssetBundleConfig(abc);
var dirInfo = new DirectoryInfo(abPath);
var floders = dirInfo.GetDirectories();
for (int i = 0; i < floders.Length; i++)
{
var f = floders[i];
WriteInfo(abPath, f.Name, abcc, languageInfo);
}
var bytes = AssetBundleConfigCollection.Compress(abcc.ToJson());
File.WriteAllBytes(Path.Combine(abPath, "ab_config.bytes"), bytes);
}
static void WriteInfo(string rootPath, string path, AssetBundleConfigCollection abcc, BFLanguageInfo languageInfo)
{
var searchPath = Path.Combine(rootPath, path);
var dirInfo = new DirectoryInfo(searchPath);
var files = dirInfo.GetFiles();
var subDirs = dirInfo.GetDirectories();
for (int i = 0; i < files.Length; i++)
{
var f = files[i];
var extension = f.Extension;
if (extension == ".meta" || extension == ".DS_Store")
{
continue;
}
var abCfg = new AssetBundleConfig();
abCfg.assetBundlePath = Path.Combine(path, f.Name).Replace("\\", "/");
var bundle = AssetBundle.LoadFromFile(f.FullName);
var assets = bundle.GetAllAssetNames();
abCfg.assetsPath = assets;
bundle.Unload(true);
abCfg.md5 = GetFileMD5(f.FullName);
abCfg.rawSize = f.Length;
if (abCfg == null)
{
Debug.LogError("f : " + f.FullName);
}
if (TryGetLanguageName(abCfg.assetBundlePath, languageInfo, out string languageName))
{
abcc.AddLangAssetBundleConfig(languageName, abCfg);
}
else
{
abcc.AddAssetBundleConfig(abCfg);
}
}
for (int i = 0; i < subDirs.Length; i++)
{
var subDir = Path.Combine(path, subDirs[i].Name);
WriteInfo(rootPath, subDir, abcc, languageInfo);
}
}
/// <summary>
/// 是否是多语言包资源
/// </summary>
static bool TryGetLanguageName(string abPath, BFLanguageInfo languageInfo, out string languageName)
{
if(true)
{
languageName = "";
return false;
}
foreach (var startStr in languagePathList)
{
if (abPath.Contains(startStr))
{
var startIndex = abPath.IndexOf(startStr);
var temp = abPath.Substring(startIndex + startStr.Length);
var endIndex = temp.IndexOf("/");
var name = temp.Remove(endIndex);
if (languageInfo.Contains(name))
{
languageName = "";
return false;
}
if (!Resource.ResourceProcessConfig.languageNames.Contains(name))
{
languageName = "";
return false;
}
languageName = name;
return true;
}
}
languageName = "";
return false;
}
public static void RegenerateABConfigMd5(string abPath, string version = "")
{
Debug.Log("[bfinfo]正在重新生成abConfig md5...");
var abConfigPath = Path.Combine(abPath, "ab_config.bytes");
var bytes = File.ReadAllBytes(abConfigPath);
var json = AssetBundleConfigCollection.Decompress(bytes);
var abcc = AssetBundleConfigCollection.Create(json);
foreach (var abc in abcc.allConfigs)
{
abc.md5 = GetFileMD5(Path.Combine(abPath, abc.assetBundlePath));
}
if (!string.IsNullOrEmpty(version))
{
abcc.version = version;
}
var newJson = abcc.ToJson();
var newBytes = AssetBundleConfigCollection.Compress(newJson);
File.WriteAllBytes(abConfigPath, newBytes);
}
static string GetFileMD5(string filePath)
{
return GameLaunchUtils.GetFileMD5(filePath);
}
/// <summary>
/// 重新打包mp4AB 不使用压缩
/// </summary>
public static bool RebuildMp4AssetsBundle(string outputPath, BuildTarget target)
{
var suc = true;
var videoAbPath = Path.Combine(outputPath, "arts/video");
if (!Directory.Exists(videoAbPath))
{
Directory.CreateDirectory(videoAbPath);
}
var fileInfos = new DirectoryInfo(videoAbPath).GetFiles();
foreach (var fInfo in fileInfos)
{
if (fInfo.Extension != ".ab")
{
continue;
}
var fileName = fInfo.Name;
fInfo.Delete();
var tempPath = outputPath + "_temp";
if (Directory.Exists(tempPath))
{
Directory.Delete(tempPath, true);
}
Directory.CreateDirectory(tempPath);
var buildMap = new AssetBundleBuild[1];
var cgAbName = "arts/video/" + fileName;
buildMap[0].assetBundleName = cgAbName;
var mp4Assets = new string[1];
mp4Assets[0] = "Assets/arts/video/" + Path.GetFileNameWithoutExtension(fileName);
buildMap[0].assetNames = mp4Assets;
var manifest = BuildPipeline.BuildAssetBundles(tempPath, buildMap, BuildAssetBundleOptions.UncompressedAssetBundle, target);
if (manifest)
{
File.Move(Path.Combine(tempPath, cgAbName), Path.Combine(outputPath, cgAbName));
}
else
{
Debug.LogError("[bferror]重新打包mp4不压缩ab失败 " + fileName);
suc = false;
}
Directory.Delete(tempPath, true);
}
AssetDatabase.Refresh();
return suc;
}
/// <summary>
/// 设置所有资源的ABName
/// </summary>
public static void SetAllAssetsBundleName()
{
Debug.Log("[bfinfo]设置资源abName...");
float total = 14;
float index = 1;
ClearFolderABName(Path.Combine(Application.dataPath, "arts", "effects"));
ClearFolderABName(Path.Combine(Application.dataPath, "first"));
SetLuaAssetBundleName(index++ / total);
SetSceneABName(index++ / total);
SetAnimationsABName(Path.Combine(Application.dataPath, "arts", "animations"), index++ / total);
SetAtlasABName(Path.Combine(Application.dataPath, "arts", "atlas"), index++ / total);
// SetEffectsABName(Path.Combine(Application.dataPath, "arts", "effects"), index++ / total);
SetBMFontsABName(Path.Combine(Application.dataPath, "arts", "fonts", "bmfonts"), index++ / total);
SetTmpFontsABName(Path.Combine(Application.dataPath, "arts", "fonts", "tmpfonts"), index++ / total);
SetMaterialABName(Path.Combine(Application.dataPath, "arts", "materials"), index++ / total);
SetModelsABName(Path.Combine(Application.dataPath, "arts", "models"), index++ / total);
SetShadersABName(Path.Combine(Application.dataPath, "arts", "shaders"), index++ / total);
SetSoundsABName(Path.Combine(Application.dataPath, "arts", "sounds"), index++ / total);
SetTexturesABName(Path.Combine(Application.dataPath, "arts", "textures"), index++ / total);
SetPrefabsABName(Path.Combine(Application.dataPath, "prefabs"), index++ / total);
SetProtoABName(Path.Combine(Application.dataPath, "proto"), index++ / total);
SetSpineABName(Path.Combine(Application.dataPath, "arts", "spines"), index++ / total);
SetFirstABName(Path.Combine(Application.dataPath, "first"), index++ / total);
// SetTimelineABName(Path.Combine(Application.dataPath, "arts", "timeline"), 16f / total);
// SetVideoABName(Path.Combine(Application.dataPath, "arts", "video"), 17f / total);
// SetLanguageResABName(Resource.ResourceProcessConfig.LANGUAGE_PATH, 19f / total);
// SetBakedatasABName(Path.Combine(Application.dataPath, "arts", "bakedatas"), 21f / total);
// SetLightProbesABName(Path.Combine(Application.dataPath, "arts", "lightprobes"), 22f / total);
// SetReflectionsABName(Path.Combine(Application.dataPath, "arts", "reflections"), 23f / total);
EditorUtility.ClearProgressBar();
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
static void SetLuaAssetBundleName(float progress)
{
EditorUtility.DisplayProgressBar("提示", "正在设置lua ABName", progress);
var dirInfo = new DirectoryInfo(Path.Combine(Application.dataPath, "lua"));
SetABNameByFile(dirInfo);
}
static void SetAnimationsABName(string dirPath, float progress)
{
EditorUtility.DisplayProgressBar("提示", "正在设置animation ABName", progress);
string[] subDirList = { "ui", "frame" };
for (var i = 0; i < subDirList.Length; ++i)
{
var dirInfo = new DirectoryInfo(Path.Combine(dirPath, subDirList[i]));
if (dirInfo.Exists)
{
SetABNameBySubDir(dirInfo);
}
}
}
// atlas和textures里的图片是一一对应的所以ab name设置为一样的
static void SetAtlasABName(string dirPath, float progress)
{
EditorUtility.DisplayProgressBar("提示", "正在设置atlas ABName", progress);
var abNameInfos = new List<ABNameInfo>();
string[] subDirList = { "ui", "icon" };
for (var i = 0; i < subDirList.Length; ++i)
{
var dirInfo = new DirectoryInfo(Path.Combine(dirPath, subDirList[i]));
var files = dirInfo.GetFiles("*.*", SearchOption.AllDirectories);
for (var j = 0; j < files.Length; ++j)
{
var fileInfo = files[j];
if (CheckIgnoreOrHidden(fileInfo))
{
continue;
}
ABNameInfo ab;
ab.name = fileInfo.FullName.Substring(assetDirectory.Length).Replace(".asset", "").Replace(".spriteatlas", "").Replace("atlas", "textures");
var relativePath = fileInfo.FullName.Substring(assetDirectory.Length);
ab.path = relativePath;
abNameInfos.Add(ab);
}
}
foreach (var relativePath in abNameInfos)
{
SetAssetBundleName(ASSETS_STR + relativePath.path, relativePath.name);
}
}
static void SetEffectsABName(string dirPath, float progress)
{
EditorUtility.DisplayProgressBar("提示", "正在设置特效 ABName", progress);
SetEffectsCommonABName(Path.Combine(dirPath, "texture"));
SetEffectsCommonABName(Path.Combine(dirPath, "models"));
// arts/effects/material 的子文件夹
string[] materialSubDirList = { "battle", "buff", "ui", "show" };
var materialSubDirPath = Path.Combine(dirPath, "material");
var dirInfo = new DirectoryInfo(materialSubDirPath);
var subDirs = dirInfo.GetDirectories();
for (var i = 0; i < subDirs.Length; ++i)
{
if(((IList)materialSubDirList).Contains(subDirs[i].Name))
{
var subDirInfo = new DirectoryInfo(Path.Combine(materialSubDirPath, subDirs[i].Name));
SetABNameBySubDir(subDirInfo);
}
else
{
SetABNameByDir(subDirs[i]);
}
}
}
static void SetEffectsCommonABName(string dirPath)
{
var dir = new DirectoryInfo(dirPath);
var files = dir.GetFiles("*.*", SearchOption.AllDirectories);
var assetFiles = new List<string>();
for (var i = 0; i < files.Length; ++i)
{
var fileInfo = files[i];
if (CheckIgnoreOrHidden(fileInfo))
{
continue;
}
var relativePath = fileInfo.FullName.Substring(assetDirectory.Length);
assetFiles.Add(relativePath);
}
foreach (var relativePath in assetFiles)
{
//prefab设置为和同名特效一个ab包
if (relativePath.ToLower().Contains("prefab"))
{
var abName = relativePath.Replace("prefab", "fbx");
SetAssetBundleName(ASSETS_STR + relativePath, abName);
}
else
{
SetAssetBundleName(ASSETS_STR + relativePath, relativePath);
}
}
}
static void SetBMFontsABName(string dirPath, float progress)
{
EditorUtility.DisplayProgressBar("提示", "正在设置BMFonts ABName", progress);
var dirInfo = new DirectoryInfo(dirPath);
SetABNameBySubDir(dirInfo);
}
static void SetMaterialABName(string dirPath, float progress)
{
EditorUtility.DisplayProgressBar("提示", "正在设置材质 ABName", progress);
var dirInfo = new DirectoryInfo(dirPath);
SetABNameByDir(dirInfo);
}
static void SetShadersABName(string dirPath, float progress)
{
EditorUtility.DisplayProgressBar("提示", "正在设置shader ABName", progress);
var dirInfo = new DirectoryInfo(dirPath);
SetABNameByDir(dirInfo);
}
static void SetModelsABName(string dirPath, float progress)
{
EditorUtility.DisplayProgressBar("提示", "正在设置模型 ABName", progress);
// 这几个文件夹按子文件夹来打ab,其他的文件夹就直接按每个文件夹打一个ab
string[] subDirList = { "characters", "maps", "maincity", "battle", "weapon" };
var dirInfo = new DirectoryInfo(dirPath);
var subDirs = dirInfo.GetDirectories();
for (var i = 0; i < subDirs.Length; ++i)
{
if(((IList)subDirList).Contains(subDirs[i].Name))
{
var subDirInfo = new DirectoryInfo(Path.Combine(dirPath, subDirs[i].Name));
SetABNameBySubDir(subDirInfo);
// // 角色材质因为改动比较频繁,和模型打包在一起的话热更的时候会导致热更量比较大,所以单独设置一下
// if(subDirs[i].Name == "characters")
// {
// var materialPath = BFEditorUtils.GetAssetPathsWithSuffix(subDirInfo.FullName, ".mat");
// foreach (var path in materialPath)
// {
// SetABNameByDirName(path);
// }
// }
}
else
{
SetABNameByDir(subDirs[i]);
}
}
}
static void SetSoundsABName(string dirPath, float progress)
{
EditorUtility.DisplayProgressBar("提示", "正在设置sounds ABName", progress);
var dirInfo = new DirectoryInfo(dirPath);
SetABNameByFile(dirInfo);
}
static void SetVideoABName(string dirPath, float progress)
{
EditorUtility.DisplayProgressBar("提示", "正在设置video ABName", progress);
var dirInfo = new DirectoryInfo(dirPath);
SetABNameByFile(dirInfo);
}
static void SetSpineABName(string dirPath, float progress)
{
EditorUtility.DisplayProgressBar("提示", "正在设置spine ABName", progress);
string[] dirList = { "ui", "characters", "battle" };
for (var i = 0; i < dirList.Length; ++i)
{
var dirInfo = new DirectoryInfo(Path.Combine(dirPath, dirList[i]));
SetABNameBySubDir(dirInfo);
}
}
static void SetTexturesABName(string dirPath, float progress)
{
EditorUtility.DisplayProgressBar("提示", "正在设置texture ABName", progress);
SetSpriteABName(dirPath);
SetBackgroundABName(dirPath);
}
static void SetSpriteABName(string dirPath)
{
string[] dirList = { "ui", "icon" };
for (var i = 0; i < dirList.Length; ++i)
{
var dirInfo = new DirectoryInfo(Path.Combine(dirPath, dirList[i]));
SetABNameBySubDir(dirInfo);
}
}
static void SetBackgroundABName(string dirPath)
{
var dirInfo = new DirectoryInfo(Path.Combine(dirPath, "background"));
SetABNameByFile(dirInfo);
}
static void SetPrefabsABName(string dirPath, float progress)
{
EditorUtility.DisplayProgressBar("提示", "正在设置perfab ABName", progress);
var dirInfo = new DirectoryInfo(dirPath);
SetABNameByFile(dirInfo);
}
static void SetProtoABName(string dirPath, float progress)
{
EditorUtility.DisplayProgressBar("提示", "正在设置proto ABName", progress);
var dirInfo = new DirectoryInfo(dirPath);
var pathList = new List<string>();
var files = dirInfo.GetFiles("*.*", SearchOption.AllDirectories);
for (var i = 0; i < files.Length; ++i)
{
var fileInfo = files[i];
if (CheckIgnoreOrHidden(fileInfo))
{
continue;
}
var relativePath = fileInfo.FullName.Substring(assetDirectory.Length);
pathList.Add(relativePath);
}
foreach (var path in pathList)
{
SetAssetBundleName(ASSETS_STR + path, "proto/proto");
}
}
static void SetSceneABName(float progress)
{
EditorUtility.DisplayProgressBar("提示", "正在设置scene ABName", progress);
var scenes = GetBuildScenes();
foreach (var scene in scenes)
{
SetAssetBundleName(scene, scene.Replace("Assets/", ""));
}
}
public static void SetTimelineABName(string dirPath, float progress)
{
EditorUtility.DisplayProgressBar("提示", "正在设置timeline ABName", progress);
var dirInfo = new DirectoryInfo(dirPath);
SetABNameByFile(dirInfo);
}
public static void SetFirstABName(string dirPath, float progress)
{
EditorUtility.DisplayProgressBar("提示", "正在设置首包ABName", progress);
var dirInfo = new DirectoryInfo(dirPath);
var pathList = new List<string>();
var files = dirInfo.GetFiles("*.*", SearchOption.AllDirectories);
for (var i = 0; i < files.Length; ++i)
{
var fileInfo = files[i];
if (CheckIgnoreOrHidden(fileInfo))
{
continue;
}
var relativePath = fileInfo.FullName.Substring(assetDirectory.Length);
pathList.Add(relativePath);
}
pathList.Add(FIRST_LUA);
pathList.Add(PROTOC_LUA);
pathList.Add(FIRST_TEXT_LUA);
foreach (var path in pathList)
{
SetAssetBundleName(ASSETS_STR + path, "first/first");
}
}
public static void SetLanguageResABName(string dirPath, float progress)
{
EditorUtility.DisplayProgressBar("提示", "正在设置多语言资源 ABName", progress);
var dirInfo = new DirectoryInfo(dirPath);
var files = dirInfo.GetFiles("*.*", SearchOption.AllDirectories);
foreach (var f in files)
{
if (CheckIgnoreOrHidden(f))
{
continue;
}
var dirName = Path.GetDirectoryName(f.FullName);
var abName = dirName.Substring(assetDirectory.Length);
var relativePath = f.FullName.Substring(assetDirectory.Length);
SetAssetBundleName(ASSETS_STR + relativePath, abName);
}
}
public static void SetTmpFontsABName(string dirPath, float progress)
{
EditorUtility.DisplayProgressBar("提示", "正在设置tmp font ABName", progress);
var dirInfo = new DirectoryInfo(dirPath);
SetABNameBySubDir(dirInfo);
}
public static void SetBakedatasABName(string dirPath, float progress)
{
EditorUtility.DisplayProgressBar("提示", "正在设置bakedatas ABName", progress);
var dirInfo = new DirectoryInfo(dirPath);
SetABNameBySubDir(dirInfo);
}
public static void SetLightProbesABName(string dirPath, float progress)
{
EditorUtility.DisplayProgressBar("提示", "正在设置lightprobe ABName", progress);
var dirInfo = new DirectoryInfo(dirPath);
SetABNameBySubDir(dirInfo);
}
public static void SetReflectionsABName(string dirPath, float progress)
{
EditorUtility.DisplayProgressBar("提示", "正在设置reflection ABName", progress);
var dirInfo = new DirectoryInfo(dirPath);
SetABNameBySubDir(dirInfo);
}
/// <summary>
/// 通过文件名设置abName, dirInfo下的每个资源的abName都设置为fileName
/// </summary>
static void SetABNameByFile(DirectoryInfo dirInfo)
{
var files = dirInfo.GetFiles("*.*", SearchOption.AllDirectories);
var assetFiles = new List<string>();
for (var i = 0; i < files.Length; ++i)
{
var fileInfo = files[i];
if (CheckIgnoreOrHidden(fileInfo))
{
continue;
}
var relativePath = fileInfo.FullName.Substring(assetDirectory.Length);
assetFiles.Add(relativePath);
}
foreach (var relativePath in assetFiles)
{
SetAssetBundleName(ASSETS_STR + relativePath, relativePath);
}
}
/// <summary>
/// 根据dirInfo下的二级文件夹名设置资源abName
/// </summary>
static void SetABNameBySubDir(DirectoryInfo dirInfo)
{
var dirs = dirInfo.GetDirectories();
for (var i = 0; i < dirs.Length; i++)
{
SetABNameByDir(dirs[i]);
}
}
/// <summary>
/// 根据文件夹路径设置资源的abName
/// </summary>
static void SetABNameByDirName(string relativeFilePath)
{
if (!File.Exists(relativeFilePath))
{
return;
}
var dirPath = Path.GetDirectoryName(relativeFilePath);
var name = dirPath.Replace("\\", "/").Replace(ASSETS_STR, "");
SetAssetBundleName(relativeFilePath, name);
}
/// <summary>
/// 根据文件夹路径设置资源的abName, dirInfo下的资源abName都设置为dirPath相对路径
/// </summary>
static void SetABNameByDir(DirectoryInfo dirInfo)
{
var abInfos = new List<ABNameInfo>();
var files = dirInfo.GetFiles("*.*", SearchOption.AllDirectories);
for (var i = 0; i < files.Length; ++i)
{
var fileInfo = files[i];
if (CheckIgnoreOrHidden(fileInfo))
{
continue;
}
ABNameInfo into;
into.name = dirInfo.FullName.Substring(assetDirectory.Length);
var relativePath = fileInfo.FullName.Substring(assetDirectory.Length);
into.path = relativePath;
abInfos.Add(into);
}
foreach (var abInfo in abInfos)
{
SetAssetBundleName(ASSETS_STR + abInfo.path, abInfo.name);
}
}
static void ClearFolderABName(string dirPath)
{
var dirInfo = new DirectoryInfo(dirPath);
var files = dirInfo.GetFiles("*.*", SearchOption.AllDirectories);
var assetFiles = new List<string>();
for (var i = 0; i < files.Length; ++i)
{
var fileInfo = files[i];
if (CheckIgnoreOrHidden(fileInfo))
{
continue;
}
var relativePath = fileInfo.FullName.Substring(assetDirectory.Length);
assetFiles.Add(relativePath);
}
foreach (var relativePath in assetFiles)
{
ClearAssetBundleName(ASSETS_STR + relativePath);
}
}
static void ClearAssetBundleName(string assetPath)
{
if (string.IsNullOrEmpty(assetPath))
{
return;
}
if (!assetPath.Contains("Editor/"))
{
var importer = AssetImporter.GetAtPath(assetPath);
if (importer == null)
{
AssetDatabase.ImportAsset(assetPath);
importer = AssetImporter.GetAtPath(assetPath);
}
if (!string.IsNullOrEmpty(importer.assetBundleName))
{
importer.assetBundleName = string.Empty;
EditorUtility.SetDirty(importer);
AssetDatabase.WriteImportSettingsIfDirty(assetPath);
}
}
}
static void SetAssetBundleName(string assetPath, string bundleName)
{
if (string.IsNullOrEmpty(assetPath) || string.IsNullOrWhiteSpace(bundleName))
{
return;
}
if (Regex.IsMatch(assetPath, @"[\u4e00-\u9fbb]"))
{
Debug.LogError("AssetBundleUtils SetAssetBundleName: " + assetPath + " 存在中文");
return;
}
if (!assetPath.Contains("Editor/"))
{
bundleName = bundleName.ToLower() + ".ab";
//所有AB名设定为相对于assets目录
var special = "assets/";
if (bundleName.Contains(special))
{
bundleName = bundleName.Substring(special.Length);
}
var changed = false;
var importer = AssetImporter.GetAtPath(assetPath);
if (importer == null)
{
AssetDatabase.ImportAsset(assetPath);
importer = AssetImporter.GetAtPath(assetPath);
}
if (string.IsNullOrEmpty(importer.assetBundleName) || !string.Equals(importer.assetBundleName, bundleName))
{
importer.assetBundleName = bundleName;
EditorUtility.SetDirty(importer);
changed = true;
}
if (changed)
{
AssetDatabase.WriteImportSettingsIfDirty(assetPath);
}
}
}
static bool CheckIgnoreOrHidden(FileInfo fi)
{
if (((fi.Attributes & FileAttributes.Hidden) > 0) || ((fi.Attributes & FileAttributes.System) > 0))
{
return true;
}
if (ignoreResSuffix.ContainsKey(fi.Extension.ToLower()))
{
return true;
}
return false;
}
public static string[] GetBuildScenes()
{
var result = new List<string>();
foreach (var scene in EditorBuildSettings.scenes)
{
if (scene == null)
{
continue;
}
if (scene.enabled)
{
result.Add(scene.path);
}
}
return result.ToArray();
}
}
}

View File

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

View File

@ -0,0 +1,542 @@
using UnityEngine;
using UnityEditor;
using System.IO;
using UnityEditor.Build.Reporting;
using System.Text.RegularExpressions;
using System;
using System.Threading;
namespace BFEditor.Build
{
public static class BuildAndroidUtils
{
const string JAVA_HOME_ENVIRONMENT = "JAVA_HOME";
const string JAVA_HOME_ENVIRONMENT_PATH = "/Applications/Unity/PlaybackEngines/AndroidPlayer/Tools/OpenJDK/MacOS";
const string ANDROID_DEFINE_SYMBOLS = "THREAD_SAFE;USE_AB";
const string AS_PROJECT_PATH = "BFVersions/android";
const string GRADLE_PATH = "/Users/aoddabao/.gradle/wrapper/dists/gradle-5.6.4-all/ankdp27end7byghfw1q2sw75f/gradle-5.6.4/bin/gradle";
const string BUGLY_APP_ID = "1eb4e5e560";
const string BUGLY_APP_KEY = "99e249db-4eeb-440e-83e7-c2bd80e8b5e4";
static Thread buglyUploadThread;
static string ReleaseSOPath = Application.dataPath + "/../BFVersions/android/ub-release/src/main/jniLibs";
static string BuglyPath = Application.dataPath + "/../Bugly";
static string BuglySOPath = Application.dataPath + "/../Bugly/so";
static string GradleExcuteProjectPath = Application.dataPath + "/../BFVersions/android/ub-release";
static string DevAsProjectPath = Application.dataPath + "/../BFVersions/android/dz_dev";
static string LanReleaseAsProjectPath = Application.dataPath + "/../BFVersions/android/dz_release";
static string GoogleAsProjectPath = Application.dataPath + "/../BFVersions/android/dz_google_apk";
static string GoogleCommonProjectPath = Application.dataPath + "/../BFVersions/android/google_common";
static string GPAsProjectPath = Application.dataPath + "/../BFVersions/android/ub-gp"; // gp删档测试渠道
static string GPOfficialAsProjectPath = Application.dataPath + "/../BFVersions/android/ub-google"; // gp正式渠道
static string SignShellPath = Application.dataPath + "/../BFFiles/androidkey";
static string GpAlginShellPath = Application.dataPath + "/../BFFiles/androidkey";
static BuildAndroidUtils()
{
EnsureJavaHome();
}
public static void EnsureJavaHome()
{
// var javaHomeEnvironment = Environment.GetEnvironmentVariable(JAVA_HOME_ENVIRONMENT);
// if (string.IsNullOrEmpty(javaHomeEnvironment))
// {
// Environment.SetEnvironmentVariable(JAVA_HOME_ENVIRONMENT, JAVA_HOME_ENVIRONMENT_PATH);
// }
}
/// <summary>
/// 打包android
/// </summary>
public static bool BuildAndroidPlayer(BuildInfo buildInfo)
{
var buildTarget = BuildTarget.Android;
// 检查平台
if (EditorUserBuildSettings.activeBuildTarget != buildTarget)
{
Debug.LogError("[bferror]当前没有在对应平台");
return false;
}
// 重新生成XLua
CompileScriptsUtils.RegenerateXLuaCode(true);
// 打包设置
BuildSettings(buildInfo);
// 开始打包
var bpOptions = GetBuildOptions(buildInfo);
var report = BuildPipeline.BuildPlayer(bpOptions);
// 打包成功
if (report.summary.result == BuildResult.Succeeded)
{
return BuildAndroidAPK(buildInfo);
}
else
{
Debug.LogError("[bferror]unity打包失败");
return false;
}
}
/// <summary>
/// 打包设置
/// </summary>
static void BuildSettings(BuildInfo buildInfo)
{
// 设置bundleVersion
PlayerSettings.bundleVersion = buildInfo.version;
// 设置VersionCode
PlayerSettings.Android.bundleVersionCode = buildInfo.version_code;
// 设置竖屏
PlayerSettings.defaultInterfaceOrientation = UIOrientation.Portrait;
PlayerSettings.allowedAutorotateToPortrait = false;
PlayerSettings.allowedAutorotateToPortraitUpsideDown = false;
PlayerSettings.allowedAutorotateToLandscapeLeft = false;
PlayerSettings.allowedAutorotateToLandscapeRight = false;
// 安卓构建目标CPU架构
PlayerSettings.Android.targetArchitectures = AndroidArchitecture.ARMv7 | AndroidArchitecture.ARM64;
// 强加Internet权限
PlayerSettings.Android.forceInternetPermission = true;
// 关闭启动动画
PlayerSettings.SplashScreen.show = false;
// 设置包名
PlayerSettings.SetApplicationIdentifier(BuildTargetGroup.Android, buildInfo.bundleName);
Debug.Log("[bfinfo]设置包名:" + buildInfo.bundleName);
// 跳过版本控制
var symbols = ANDROID_DEFINE_SYMBOLS;
if (buildInfo.skipVersion)
{
symbols = symbols + ";SKIP_VERSION;";
}
PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.Android, symbols);
Debug.Log("[bfinfo]设置defineSymbols: " + symbols);
// 是否是dev
var development = buildInfo.IsDevChannel() ? true : false;
EditorUserBuildSettings.development = development;
// 商品名称
// 应用名
if (buildInfo.IsPublish())
{
PlayerSettings.productName = "K.O";
}
else
{
PlayerSettings.productName = development ? "b5-dev" : "b5-release";
}
// BuildType设置dev/release
EditorUserBuildSettings.androidBuildType = development ? AndroidBuildType.Debug : AndroidBuildType.Release;
// 是否导出as工程
// EditorUserBuildSettings.exportAsGoogleAndroidProject = development ? false : true;
EditorUserBuildSettings.exportAsGoogleAndroidProject = true;
// dev使用Mono release使用IL2CPP
var scriptImp = development ? ScriptingImplementation.Mono2x : ScriptingImplementation.IL2CPP;
PlayerSettings.SetScriptingBackend(BuildTargetGroup.Android, scriptImp);
}
/// <summary>
/// 获取打包参数
/// </summary>
static BuildPlayerOptions GetBuildOptions(BuildInfo buildInfo)
{
var bpOptions = new BuildPlayerOptions();
bpOptions.scenes = AssetBundleUtils.GetBuildScenes();
bpOptions.target = BuildTarget.Android;
// if (buildInfo.IsDevChannel())
// {
// bpOptions.locationPathName = GetDevApkPathName(buildInfo);
// Debug.Log("[bfinfo]apk path : " + bpOptions.locationPathName);
// }
// else
// {
// bpOptions.locationPathName = GetASProjectPathName();
// Debug.Log("[bfinfo]asProject path : " + bpOptions.locationPathName);
// }
bpOptions.locationPathName = GetASProjectPathName(buildInfo);
BuildOptions options;
if (buildInfo.IsReleaseChannel())
{
options = BuildOptions.None;
}
else
{
options = BuildOptions.Development;
}
bpOptions.options = options;
return bpOptions;
}
static string GetDevApkPathName(BuildInfo buildInfo)
{
var apkName = buildInfo.skipVersion ? "dz_dev_skip_version.apk" : "dz_dev_debug.apk";
var path = Path.Combine(AS_PROJECT_PATH, apkName);
return path;
}
static string GetASProjectPathName(BuildInfo buildInfo)
{
var dir = Path.Combine(AS_PROJECT_PATH, buildInfo.mode);
if (Directory.Exists(dir))
{
Directory.Delete(dir, true);
}
return dir;
}
static bool BuildAPK(BuildInfo buildInfo)
{
if (buildInfo.IsGPChannel())
{
MergeGPToReleaseProject(buildInfo);
FixGradleVersion(buildInfo.version_code, buildInfo.version);
}
if (buildInfo.IsLanRelease())
{
AddBuglyParamsToReleaseProject();
}
Debug.Log("[bfinfo]正在buildApk...");
var success = true;
var args = "";
if (buildInfo.IsReleaseChannel())
{
args += " assembleRelease";
}
else
{
args += " assembleDebug";
}
BFEditorUtils.RunCommond(GRADLE_PATH, args, GradleExcuteProjectPath,
(msg) =>
{
},
(errorMsg) =>
{
if (errorMsg.Contains("BUILD FAILED") || errorMsg.Contains("FAILURE") || errorMsg.Contains("ERROR"))
{
success = false;
Debug.LogError("[bferror] " + errorMsg);
}
});
if (buildInfo.IsGPChannel())
{
// 尝试制作并上传bugly符号表 开新Thread不影响打包流程
if (success)
{
CopySOAndUploadBuglySymbol(buildInfo.bundleName, buildInfo.version);
}
}
return success;
}
static bool BuildAAB(BuildInfo buildInfo)
{
var result = true;
if(buildInfo.IsGPChannel())
{
// MergeGPToReleaseProject(buildInfo);
// FixGradleVersion(buildInfo.version_code, buildInfo.version);
}
return result;
}
static bool BuildAndroidAPK(BuildInfo buildInfo)
{
var result = true;
if(buildInfo.IsDevChannel())
{
MergeProject(buildInfo, DevAsProjectPath);
}
else if(buildInfo.IsLanRelease())
{
MergeProject(buildInfo, LanReleaseAsProjectPath);
}
else if(buildInfo.IsGPChannel())
{
MergeProject(buildInfo, GoogleAsProjectPath);
}
return result;
}
/// <summary>
/// 合并dev工程
/// </summary>
static void MergeProject(BuildInfo buildInfo, String asProjectPath)
{
Debug.Log("[bfinfo]正在整合dev project...");
var dir = Path.Combine(Application.dataPath, "../", AS_PROJECT_PATH, buildInfo.mode);
var javaPath = Path.Combine(dir, "unityLibrary/src/main/java");
var manifestPath = Path.Combine(dir, "unityLibrary/src/main/AndroidManifest.xml");
// 获取到unity打出的build-id
var reader = new StreamReader(new FileStream(manifestPath, FileMode.Open));
string buildIdLine;
while ((buildIdLine = reader.ReadLine()) != null)
{
if (buildIdLine.Contains("unity.build-id"))
{
Debug.Log("[bfinfo]修正build-id: " + buildIdLine);
break;
}
}
reader.Close();
reader.Dispose();
if (Directory.Exists(javaPath))
{
var di = new DirectoryInfo(javaPath);
di.Delete(true);
}
BFEditorUtils.CopyDir(GoogleCommonProjectPath, dir);
// 获取到google_common复制过去的build.gradle和AndroidManifest
var buildGradlePath = Path.Combine(dir, "launcher/build.gradle");
var text = File.ReadAllText(buildGradlePath);
var regex = new Regex("REPLACE_APPLICATION_ID");
text = regex.Replace(text, buildInfo.bundleName);
File.WriteAllText(buildGradlePath, text);
var androidManifestPath = Path.Combine(dir, "launcher/src/main/AndroidManifest.xml");
text = File.ReadAllText(androidManifestPath);
regex = new Regex("REPLACE_APPLICATION_ID");
text = regex.Replace(text, buildInfo.bundleName);
File.WriteAllText(androidManifestPath, text);
BFEditorUtils.CopyDir(asProjectPath, dir);
text = File.ReadAllText(manifestPath);
regex = new Regex("REPLACE_BUILD_ID");
text = regex.Replace(text, buildIdLine);
File.WriteAllText(manifestPath, text);
}
/// <summary>
/// 合并gp工程
/// </summary>
static void MergeGPToReleaseProject(BuildInfo buildInfo)
{
Debug.Log("[bfinfo]正在整合gp渠道sdk...");
var javaPath = GradleExcuteProjectPath + "/src/main/java";
var manifestPath = GradleExcuteProjectPath + "/src/main/AndroidManifest.xml";
// 获取到unity打出的build-id
var reader = new StreamReader(new FileStream(manifestPath, FileMode.Open));
string buildIdLine;
while ((buildIdLine = reader.ReadLine()) != null)
{
if (buildIdLine.Contains("unity.build-id"))
{
Debug.Log("[bfinfo]修正build-id: " + buildIdLine);
break;
}
}
reader.Close();
reader.Dispose();
if (Directory.Exists(javaPath))
{
var di = new DirectoryInfo(javaPath);
di.Delete(true);
}
if (buildInfo.IsGPOfficial())
{
BFEditorUtils.CopyDir(GPOfficialAsProjectPath, GradleExcuteProjectPath);
}
else
{
BFEditorUtils.CopyDir(GPAsProjectPath, GradleExcuteProjectPath);
}
var text = File.ReadAllText(manifestPath);
var regex = new Regex("REPLACE_BUILD_ID");
text = regex.Replace(text, buildIdLine);
File.WriteAllText(manifestPath, text);
}
/// <summary>
/// fix versionCode versionName
/// </summary>
static void FixGradleVersion(int versionCode, string versionName)
{
Debug.Log("[bfinfo]修正build.gradle: VersionCode " + versionCode + " VersionName " + versionName);
var gradleFilePath = Path.Combine(GradleExcuteProjectPath, "build.gradle");
var text = File.ReadAllText(gradleFilePath);
var regex = new Regex("versionCode 1");
text = regex.Replace(text, string.Format("versionCode {0}", versionCode));
regex = new Regex("versionName '0.1'");
text = regex.Replace(text, string.Format("versionName '{0}'", versionName));
File.WriteAllText(gradleFilePath, text);
}
/// <summary>
/// 内网Release包接入Bugly
/// </summary>
static void AddBuglyParamsToReleaseProject()
{
// 修改build.grade
var gradleFilePath = Path.Combine(GradleExcuteProjectPath, "build.gradle");
var text = File.ReadAllText(gradleFilePath);
var regex = new Regex("implementation");
var match = regex.Match(text);
var index = match.Index;
text = text.Insert(index, "\n" +
"implementation 'com.tencent.bugly:crashreport:latest.release'\n" +
"implementation 'com.tencent.bugly:nativecrashreport:latest.release'\n");
File.WriteAllText(gradleFilePath, text);
// 修改AndroidManifest.xml
var manifestPath = GradleExcuteProjectPath + "/src/main/AndroidManifest.xml";
text = File.ReadAllText(manifestPath);
regex = new Regex("<uses-permission");
match = regex.Match(text);
index = match.Index;
text = text.Insert(index, "\n" +
"<uses-permission android:name='android.permission.READ_PHONE_STATE' />\n" +
"<uses-permission android:name='android.permission.ACCESS_WIFI_STATE' />\n" +
"<uses-permission android:name='android.permission.READ_LOGS' />\n" +
"<uses-permission android:name='android.permission.POST_NOTIFICATIONS'/>\n");
File.WriteAllText(manifestPath, text);
// 修改UnityPlayerActivity.java
var javaPath = GradleExcuteProjectPath + "/src/main/java/com/juzu/ub/release/android/UnityPlayerActivity.java";
text = File.ReadAllText(javaPath);
regex = new Regex("import");
match = regex.Match(text);
index = match.Index;
text = text.Insert(index, "\nimport com.tencent.bugly.crashreport.CrashReport;");
regex = new Regex("requestFocus\\(\\);");
match = regex.Match(text);
index = match.Index;
var length = match.Length;
var formatInsertString = string.Format("\nCrashReport.initCrashReport(getApplicationContext(), \"{0}\", false);", BUGLY_APP_ID);
text = text.Insert(index + length, formatInsertString);
File.WriteAllText(javaPath, text);
}
/// <summary>
/// release包加密后签名
/// </summary>
static bool ReleaseApkSign()
{
Debug.Log("[bfinfo]正在签名apk...");
var success = true;
BFEditorUtils.RunCommond("sh", "release_sign.sh", SignShellPath,
(msg) =>
{
},
(errorMsg) =>
{
Debug.LogError("[bferror] " + errorMsg);
success = false;
});
return success;
}
/// <summary>
/// gp包对齐
/// </summary>
static bool GPApkAlign()
{
Debug.Log("[bfinfo]正在对齐apk...");
var success = true;
BFEditorUtils.RunCommond("sh", "gp_align.sh", GpAlginShellPath,
(msg) =>
{
},
(errorMsg) =>
{
Debug.LogError("[bferror] " + errorMsg);
success = false;
});
return success;
}
/// <summary>
/// google official包对齐
/// </summary>
static bool GoogleOfficialApkAlign()
{
Debug.Log("[bfinfo]正在对齐apk...");
var success = true;
BFEditorUtils.RunCommond("sh", "google_align.sh", GpAlginShellPath,
(msg) =>
{
},
(errorMsg) =>
{
Debug.LogError("[bferror] " + errorMsg);
success = false;
});
return success;
}
/// <summary>
/// CopyRelease文件夹内的so到bugly文件夹制作并上传Bugly符号表
/// 因是非出包必要步骤且依赖网络环境和腾讯服务器有时会失败所以开一个Thread来处理
/// </summary>
static void CopySOAndUploadBuglySymbol(string bundleName, string version)
{
// 将release的so复制到bugly文件下
BFEditorUtils.CopyDir(ReleaseSOPath, BuglySOPath);
// 开启Thread处理打包so和上传
TryCloseUploadThread();
var args = "bugly.sh" + " " + BUGLY_APP_ID + " " + BUGLY_APP_KEY + " " + bundleName + " " + version;
buglyUploadThread = new Thread(new ThreadStart(() => { UploadBuglySymbol(args); }));
buglyUploadThread.IsBackground = true;
buglyUploadThread.Start();
}
static void UploadBuglySymbol(string args)
{
BFEditorUtils.RunCommond("sh", args, BuglyPath,
(msg) =>
{
Debug.Log("UploadBuglySymbol: " + msg);
},
(errorMsg) =>
{
Debug.LogError("[bferror] UploadBuglySymbol: " + errorMsg);
});
TryCloseUploadThread();
}
static void TryCloseUploadThread()
{
if (buglyUploadThread != null)
{
var tmp = buglyUploadThread;
buglyUploadThread = null;
tmp.Abort();
}
}
}
}

View File

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

View File

@ -0,0 +1,348 @@
#if UNITY_EDITOR_OSX
using UnityEngine;
using UnityEditor;
using UnityEditor.Build.Reporting;
using UnityEditor.iOS.Xcode;
using System.IO;
#endif
namespace BFEditor.Build
{
public static class BuildIOSUtils
{
#if UNITY_EDITOR_OSX
const string IOS_DEFINE_SYMBOLS = "THREAD_SAFE;USE_AB";
const string DEV_XCODE_LOCAL_PATH = "BFVersions/ios/dev";
const string RELEASE_XCODE_LOCAL_PATH = "BFVersions/ios/release";
static string devXCodePath = Application.dataPath + "/../" + DEV_XCODE_LOCAL_PATH;
static string releaseXCodePath = Application.dataPath + "/../" + RELEASE_XCODE_LOCAL_PATH;
static string devOptionsPListPath = Application.dataPath + "/../" + "BFVersions/ios/exports/dev/ExportOptions.plist";
static string releaseOptionsPListPath = Application.dataPath + "/../" + "BFVersions/ios/exports/release/ExportOptions.plist";
static string publishOptionsPListPath = Application.dataPath + "/../" + "BFVersions/ios/exports/dis/ExportOptions.plist";
public static bool BuildIOSPlayer(BuildInfo buildInfo)
{
var buildTarget = BuildTarget.iOS;
// 检查平台
if (EditorUserBuildSettings.activeBuildTarget != buildTarget)
{
Debug.LogError("[bferror]当前没有在对应平台");
return false;
}
// 重新生成XLua
CompileScriptsUtils.RegenerateXLuaCode(true);
// 打包设置
BuildSettings(buildInfo);
// 开始打包
var bpOptions = GetBuildOptions(buildInfo);
var report = BuildPipeline.BuildPlayer(bpOptions);
if (report.summary.result == BuildResult.Succeeded)
{
return BuildIpaFromXCode(buildInfo);
}
else
{
Debug.LogError("[bferror]unity打包xcode失败");
return false;
}
}
/// <summary>
/// 打包设置
/// </summary>
static void BuildSettings(BuildInfo buildInfo)
{
// 设置bundleVersion
PlayerSettings.bundleVersion = buildInfo.version;
// 设置buildNumber
PlayerSettings.iOS.buildNumber = buildInfo.version_code.ToString();
// 设置竖屏
PlayerSettings.defaultInterfaceOrientation = UIOrientation.Portrait;
PlayerSettings.allowedAutorotateToPortrait = false;
PlayerSettings.allowedAutorotateToPortraitUpsideDown = false;
PlayerSettings.allowedAutorotateToLandscapeLeft = false;
PlayerSettings.allowedAutorotateToLandscapeRight = false;
// 允许Xcode根据appleDeveloperTeamID自动签署应用程序
PlayerSettings.iOS.appleEnableAutomaticSigning = !buildInfo.IsPublish();
// 使用手动签名时iOS资源调配配置文件的类型自动
PlayerSettings.iOS.iOSManualProvisioningProfileType = buildInfo.IsPublish() ? ProvisioningProfileType.Distribution : ProvisioningProfileType.Automatic;
// 关闭启动动画
PlayerSettings.SplashScreen.show = false;
// 设置包名
PlayerSettings.SetApplicationIdentifier(BuildTargetGroup.iOS, buildInfo.bundleName);
Debug.Log("[bfinfo]设置包名:" + buildInfo.bundleName);
// 是否跳过版本控制
var symbols = IOS_DEFINE_SYMBOLS;
if (buildInfo.skipVersion)
{
symbols = symbols + ";SKIP_VERSION;";
}
PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.iOS, symbols);
Debug.Log("[bfinfo]设置defineSymbols: " + symbols);
// 是否是dev
var development = buildInfo.IsDevChannel();
EditorUserBuildSettings.development = development;
// 商品名称
if (buildInfo.IsPublish())
{
PlayerSettings.productName = "Heroic Expedition";
}
else
{
PlayerSettings.productName = development ? "b5-dev" : "b5-release";
}
// BuildType设置dev/release
EditorUserBuildSettings.iOSBuildConfigType = development ? iOSBuildType.Debug : iOSBuildType.Release;
// 使用IL2CPP
var scriptImp = ScriptingImplementation.IL2CPP;
PlayerSettings.SetScriptingBackend(BuildTargetGroup.iOS, scriptImp);
// 目标平台架构目前支持ARM64
PlayerSettings.SetArchitecture(BuildTargetGroup.iOS, 1);
}
/// <summary>
/// 获取打包参数
/// </summary>
static BuildPlayerOptions GetBuildOptions(BuildInfo buildInfo)
{
var bpOptions = new BuildPlayerOptions();
bpOptions.scenes = AssetBundleUtils.GetBuildScenes();
bpOptions.target = BuildTarget.iOS;
var path = buildInfo.IsReleaseChannel() ? RELEASE_XCODE_LOCAL_PATH : DEV_XCODE_LOCAL_PATH;
bpOptions.locationPathName = path;
Debug.Log("[bfinfo]xcode path : " + path);
var absolutePath = buildInfo.IsReleaseChannel() ? releaseXCodePath : devXCodePath;
if (Directory.Exists(absolutePath))
{
Directory.Delete(absolutePath, true);
}
if (!buildInfo.IsPublish())
{
var options = BuildOptions.Development | BuildOptions.ConnectWithProfiler | BuildOptions.AllowDebugging;
bpOptions.options = options;
}
return bpOptions;
}
/// <summary>
/// 打包ipa
/// </summary>
static bool BuildIpaFromXCode(BuildInfo buildInfo)
{
// 修改XCode设置
FixXCodeProject(buildInfo);
// 权限
UnlockKeyChain();
// archive
if (!Archive(buildInfo))
{
return false;
}
// 导出ipa
if (!ExportIpa(buildInfo))
{
return false;
}
return true;
}
/// <summary>
/// xCode工程设置
/// </summary>
static void FixXCodeProject(BuildInfo buildInfo)
{
var isDev = buildInfo.IsDevChannel();
var xCodeProjectPath = isDev ? devXCodePath : releaseXCodePath;
var path = xCodeProjectPath + "/Unity-iPhone.xcodeproj/project.pbxproj";
var pbxProject = new PBXProject();
pbxProject.ReadFromFile(path);
var targetGuid = pbxProject.TargetGuidByName("Unity-iPhone");
pbxProject.AddBuildProperty(targetGuid, "OTHER_LDFLAGS", "-ObjC");
pbxProject.SetBuildProperty(targetGuid, "ENABLE_BITCODE", "NO");
pbxProject.SetBuildProperty(targetGuid, "ENABLE_BITCODE", "NO");
pbxProject.SetBuildProperty(targetGuid, "DEVELOPMENT_TEAM", "49QQW8856Q");
if (buildInfo.IsPublish())
{
pbxProject.SetBuildProperty(targetGuid, "PROVISIONING_PROFILE_SPECIFIER", "ub_appstore_dis");
}
// 添加系统库
AddSystemLibReferenceToProject(pbxProject, targetGuid, "libsqlite3.tbd");
AddSystemLibReferenceToProject(pbxProject, targetGuid, "libz.1.tbd");
AddSystemLibReferenceToProject(pbxProject, targetGuid, "libiconv.2.tbd");
AddSystemLibReferenceToProject(pbxProject, targetGuid, "libresolv.9.tbd");
var pListPath = Path.Combine(xCodeProjectPath, "Info.plist");
var pList = new PlistDocument();
pList.ReadFromFile(pListPath);
// 版本号
var vKey = "CFBundleShortVersionString";
var vValue = new PlistElementString(buildInfo.version);
var pListRoot = pList.root;
var rootDict = pListRoot.values;
if (!rootDict.ContainsKey(vKey))
{
rootDict.Add(vKey, vValue);
}
else
{
rootDict[vKey] = vValue;
}
// VersionCode
var vCodeKey = "CFBundleVersion";
var vCodeValue = new PlistElementString(buildInfo.version_code.ToString());
if (!rootDict.ContainsKey(vCodeKey))
{
rootDict.Add(vCodeKey, vCodeValue);
}
else
{
rootDict[vCodeKey] = vCodeValue;
}
// 数美SDK会使用位置必须加入这个说明
var localtionKey = "NSLocationWhenInUseUsageDescription";
var localtionValue = new PlistElementString("We use your location to give you a better localization.");
if (!rootDict.ContainsKey(localtionKey))
{
rootDict.Add(localtionKey, localtionValue);
}
else
{
rootDict[localtionKey] = localtionValue;
}
// 提审提示缺少出口合规证明,这里直接设置为false即可
var encryptionKey = "ITSAppUsesNonExemptEncryption";
var encryptionValue = new PlistElementBoolean(false);
if (!rootDict.ContainsKey(encryptionKey))
{
rootDict.Add(encryptionKey, encryptionValue);
}
else
{
rootDict[encryptionKey] = encryptionValue;
}
pList.WriteToFile(pListPath);
pbxProject.WriteToFile(path);
}
//添加系统lib方法
static void AddSystemLibReferenceToProject(PBXProject pbxProject, string targetGuid, string lib)
{
var fileGuid = pbxProject.AddFile("usr/lib/" + lib, "Frameworks/" + lib, PBXSourceTree.Sdk);
pbxProject.AddFileToBuild(targetGuid, fileGuid);
}
/// <summary>
/// Archive
/// </summary>
static bool Archive(BuildInfo buildInfo)
{
Debug.Log("[bfinfo]正在archive...");
var result = true;
var xCodeProjectPath = buildInfo.IsDevChannel() ? devXCodePath : releaseXCodePath;
var archivePath = xCodeProjectPath + "/build/archive/Unity-iPhone.xcarchive";
var args = string.Format("archive -scheme Unity-iPhone -configuration Release -archivePath {0}", archivePath);
BFEditorUtils.RunCommond("xcodebuild", args, xCodeProjectPath,
(info) =>
{
Debug.Log(info);
},
(error) =>
{
if (error.Contains("ARCHIVE FAILED")) // 失败标志
{
result = false;
}
Debug.LogError("[bferror] " + error);
}
);
return result;
}
/// <summary>
/// 导出ipa
/// </summary>
static bool ExportIpa(BuildInfo buildInfo)
{
Debug.Log("[bfinfo]正在导出ipa...");
var result = false;
var xCodeProjectPath = buildInfo.IsDevChannel() ? devXCodePath : releaseXCodePath;
string exportPListPath;
if (buildInfo.IsPublish())
{
exportPListPath = publishOptionsPListPath;
}
else
{
exportPListPath = buildInfo.IsDevChannel() ? devOptionsPListPath : releaseOptionsPListPath;
}
var ipaPath = xCodeProjectPath + "/build/ipa";
var archivePath = xCodeProjectPath + "/build/archive/Unity-iPhone.xcarchive";
var args = string.Format("-exportArchive -archivePath {0} -exportPath {1} -exportOptionsPlist {2} -allowProvisioningUpdates", archivePath, ipaPath, exportPListPath);
BFEditorUtils.RunCommond("xcodebuild", args, null,
(info) =>
{
if(info.Contains("EXPORT SUCCEEDED"))
{
result = true;
}
},
(error) =>
{
}
);
return result;
}
/// <summary>
/// 远程打包签名ipa时需要钥匙串权限
/// </summary>
static void UnlockKeyChain()
{
BFEditorUtils.RunCommond("security", "-v unlock-keychain -p '123456' /Users/aoddabao/Library/Keychains/login.keychain-db", null,
(msg) =>
{
Debug.Log(msg);
},
(msg) =>
{
Debug.LogError(msg);
}
);
}
#endif
}
}

View File

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

View File

@ -0,0 +1,189 @@
using System;
using UnityEngine;
using System.IO;
using UnityEditor;
using System.Collections.Generic;
using CSObjectWrapEditor;
using XLua;
using System.Diagnostics;
namespace BFEditor.Build
{
public static class CompileScriptsUtils
{
public static void CompileAndEncryptLua()
{
if (!CompileAndEncryptLua(true))
{
throw new Exception();
}
}
public static bool CompileAndEncryptLua(bool debugFlag)
{
var result = false;
if (CompileDevLuaToRunLua(debugFlag))
{
ProjectEncryptUtils.EncryptLua();
result = true;
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
return result;
}
public static bool CompileDevLuaToRunLua(bool debugFlag = true)
{
UnityEngine.Debug.Log("[bfinfo]编译lua代码...");
var srcDir = Path.Combine(Application.dataPath, "Developer/lua");
var destDir = Path.Combine(Application.dataPath, "lua");
return CompileLua(srcDir, destDir, debugFlag);
}
static bool CompileLua(string srcDir, string destDir, bool debugFlag)
{
var startTime = DateTime.Now.Ticks;
var srcFileMap = new Dictionary<string, bool>();
#if UNITY_EDITOR_OSX
var scriptPath = Path.Combine(Application.dataPath, "../tools/luac/mac/compile.sh");
var luacPath = Path.Combine(Application.dataPath, "../tools/luac/mac/luac");
#else
srcDir = srcDir.Replace('\\', '/');
destDir = destDir.Replace('\\', '/');
var scriptPath = Path.Combine(Application.dataPath, "../tools/luac/windows/compile.bat").Replace('\\', '/');
var luacPath = Path.Combine(Application.dataPath, "../tools/luac/windows/luac.exe").Replace('\\', '/');
#endif
if (luacPath.Length == 0 || !File.Exists(luacPath))
{
EditorUtility.ClearProgressBar();
EditorUtility.DisplayDialog("提示", "编译Lua失败 找不到luac", "确定");
return false;
}
EditorUtility.DisplayProgressBar("提示", "正在编译lua文件...", 0);
var srcDirInfo = new DirectoryInfo(srcDir);
var srcFiles = srcDirInfo.GetFiles("*.lua", SearchOption.AllDirectories);
var fileList = new List<FileInfo>(srcFiles);
for (int i = fileList.Count - 1; i >= 0; i--)
{
if (fileList[i].DirectoryName.Contains("/Test/"))
{
fileList.RemoveAt(i);
}
}
var totalCount = fileList.Count;
var unit = (float)totalCount / 100;
for (var i = 0; i < fileList.Count; ++i)
{
var fileInfo = fileList[i];
var assetName = fileInfo.FullName.Substring(srcDir.Length) + ".bytes";
srcFileMap[assetName] = true;
var subPath = "";
#if UNITY_EDITOR_WIN
var subIndex = assetName.Replace('\\', '/').LastIndexOf('/');
#else
var subIndex = assetName.LastIndexOf('/');
#endif
if (subIndex >= 0)
{
subPath = assetName.Substring(0, subIndex + 1);
}
var copyPath = destDir + subPath;
if (!Directory.Exists(copyPath))
{
Directory.CreateDirectory(copyPath);
}
}
#if UNITY_EDITOR_OSX
var startInfo = new ProcessStartInfo("sh");
startInfo.Arguments = scriptPath + " " + srcDir + " " + srcDir.Length + " " + destDir + " " + luacPath;
#else
var startInfo = new ProcessStartInfo(scriptPath);
startInfo.Arguments = " " + srcDir + " " + srcDir.Length + " " + destDir + " " + luacPath;
#endif
startInfo.WorkingDirectory = srcDir;
startInfo.UseShellExecute = false;
startInfo.RedirectStandardInput = true;
startInfo.RedirectStandardOutput = true;
startInfo.CreateNoWindow = true;
var process = new Process();
process.StartInfo = startInfo;
process.Start();
process.BeginOutputReadLine();
process.OutputDataReceived += (sender, e) =>
{
if (!string.IsNullOrEmpty(e.Data))
{
UnityEngine.Debug.Log(e.Data);
}
};
process.WaitForExit();
process.Close();
EditorUtility.ClearProgressBar();
var deleteList = new List<string>();
var destDirInfo = new DirectoryInfo(destDir);
var destFiles = destDirInfo.GetFiles("*.bytes", SearchOption.AllDirectories);
for (var i = 0; i < destFiles.Length; ++i)
{
var fileInfo = destFiles[i];
var assetName = fileInfo.FullName.Substring(destDir.Length);
if (!srcFileMap.ContainsKey(assetName))
{
deleteList.Add(fileInfo.FullName);
}
}
if (deleteList.Count > 0)
{
EditorUtility.DisplayProgressBar("提示", "正在删除失效lua文件", 0);
var count = deleteList.Count;
foreach (var path in deleteList)
{
UnityEngine.Debug.LogWarning("Delete dest lua compile file " + path);
File.Delete(path);
}
EditorUtility.ClearProgressBar();
}
var endTime = DateTime.Now.Ticks;
UnityEngine.Debug.Log("Lua文件已完成编译用时" + (endTime - startTime) / 10000 + "毫秒");
return true;
}
public static void RegenerateXLuaCode(bool showDialog = false)
{
if (showDialog)
{
EditorUtility.DisplayProgressBar("提示", "正在重新生成xlua代码...", 1);
}
UnityEngine.Debug.Log("[bfinfo]重新生成XLuaCode...");
DelegateBridge.Gen_Flag = true;
Generator.ClearAll();
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
Generator.GenAll();
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
if (showDialog)
{
EditorUtility.ClearProgressBar();
}
}
}
}

View File

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

View File

@ -0,0 +1,156 @@
using UnityEngine;
using UnityEditor;
using System.IO;
using System;
namespace BFEditor.Build
{
public static class ProjectEncryptUtils
{
const string NHP_PATH = "BFVersions/NHPProtect/NHPProtect.jar";
const string LUA_ENCRYPT_KEY = "reD9xI4k5MR0qqi8";
const string LUA_ENCRYPT_HEAD = "bf_encrypt_lua";
/// <summary>
/// assetBundle加密
/// </summary>
public static bool EncryptAssetBundle(string abPath, bool showDialog = false)
{
Debug.Log("[bfinfo]正在加密assetbundle...");
var success = true;
var path = Application.dataPath.Replace("Assets", NHP_PATH);
var args = string.Format("-jar {0} -SingleAstEnc {1}", path, abPath);
EditorUtility.DisplayProgressBar("提示", "正在加密assetbundle...", 0.5f);
BFEditorUtils.RunCommond("java", args, null,
(msg) =>
{
Debug.Log(msg);
},
(errorMsg) =>
{
Debug.LogError(errorMsg);
success = false;
}
);
EditorUtility.ClearProgressBar();
if (success)
{
Directory.Delete(abPath, true);
Debug.Log("删除未加密AB:" + abPath);
var dest = abPath;
var src = dest + "_encrypted";
Directory.Move(src, dest);
Debug.Log("加密AB路径改名:" + src + "---->" + dest);
var tips = "加密AssetBundle成功";
if (showDialog)
{
EditorUtility.DisplayDialog("提示", tips, "确定");
}
else
{
Debug.Log(tips);
}
}
else
{
if (showDialog)
{
EditorUtility.DisplayDialog("提示", "加密AssetBundle失败", "确定");
}
else
{
Debug.LogError("加密AssetBundle失败");
}
}
return success;
}
/// <summary>
/// apk加密
/// </summary>
public static bool EncryptApk(string apkPath, bool showDialog = false)
{
var success = true;
var path = Application.dataPath.Replace("Assets", NHP_PATH);
var args = string.Format("-jar {0} -yunconfig -apksign -zipalign -input ", path) + apkPath;
EditorUtility.DisplayProgressBar("提示", "正在加密apk", 0.5f);
BFEditorUtils.RunCommond("java", args, null,
(msg) =>
{
Debug.Log(msg);
},
(errorMsg) =>
{
Debug.LogError(errorMsg);
success = false;
}
);
EditorUtility.ClearProgressBar();
if (success)
{
var fileName = Path.GetFileNameWithoutExtension(apkPath);
var encryptedName = apkPath.Replace(fileName, fileName + "_encrypted");
var tips = "加密APK成功,APK路径:" + encryptedName;
if (showDialog)
{
EditorUtility.DisplayDialog("提示", tips, "确定");
}
else
{
Debug.Log(tips);
}
}
else
{
if (showDialog)
{
EditorUtility.DisplayDialog("提示", "加密APK失败", "确定");
}
else
{
Debug.LogError("加密APK失败");
}
}
return success;
}
public static void EncryptLua()
{
Debug.Log("[bfinfo]正在加密lua...");
var luaPath = Path.Combine(Application.dataPath, "lua");
var dirInfo = new DirectoryInfo(luaPath);
var srcFiles = dirInfo.GetFiles("*.lua.bytes", SearchOption.AllDirectories);
foreach (var file in srcFiles)
{
var bytes = File.ReadAllBytes(file.FullName);
var index = 0;
foreach (var item in bytes)
{
bytes[index] = (byte)(item ^ (LUA_ENCRYPT_KEY[index % LUA_ENCRYPT_KEY.Length]));
index++;
}
var newbytes = new byte[bytes.Length + LUA_ENCRYPT_HEAD.Length];
index = 0;
foreach (var item in LUA_ENCRYPT_HEAD)
{
var headByte = (byte)item;
newbytes[index] = headByte;
index++;
}
foreach (var item in bytes)
{
newbytes[index] = item;
index++;
}
File.WriteAllBytes(file.FullName, newbytes);
}
}
}
}

View File

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

View File

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

View File

@ -0,0 +1,63 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;
namespace BFEditor
{
[InitializeOnLoadAttribute]
public static class BFEditorApplication
{
private const string DEFAULT_FONT_PATH = "assets/arts/fonts/tmpfonts/default/tmpfont/font_sdf.asset";
private const string DEFAULT_FONT_NUMBER_PATH = "assets/arts/fonts/tmpfonts/default/tmpfont/font_number_sdf.asset";
private const string DEFAULT_FONT_BATTLE_PATH = "assets/arts/fonts/tmpfonts/battle/font_battle_sdf.asset";
static BFEditorApplication()
{
EditorApplication.playModeStateChanged += PlayModeStateChanged;
}
private static void PlayModeStateChanged(PlayModeStateChange state)
{
// 编辑器下离开play模式
if (state == PlayModeStateChange.ExitingPlayMode)
{
// 游戏运行的时候处理了一下default字体资源这里就需要重置一下否则无法正常显示文本
if (File.Exists(DEFAULT_FONT_PATH))
{
var fontAsset = AssetDatabase.LoadAssetAtPath<TMPro.TMP_FontAsset>(DEFAULT_FONT_PATH);
if (fontAsset)
{
// fontAsset.characterTable.Clear();
fontAsset.ClearFontAssetData();
EditorUtility.SetDirty(fontAsset);
}
}
if (File.Exists(DEFAULT_FONT_NUMBER_PATH))
{
var fontAsset = AssetDatabase.LoadAssetAtPath<TMPro.TMP_FontAsset>(DEFAULT_FONT_NUMBER_PATH);
if (fontAsset)
{
// fontAsset.characterTable.Clear();
fontAsset.ClearFontAssetData();
EditorUtility.SetDirty(fontAsset);
}
}
if (File.Exists(DEFAULT_FONT_BATTLE_PATH))
{
var fontAsset = AssetDatabase.LoadAssetAtPath<TMPro.TMP_FontAsset>(DEFAULT_FONT_BATTLE_PATH);
if (fontAsset)
{
// fontAsset.characterTable.Clear();
fontAsset.ClearFontAssetData();
EditorUtility.SetDirty(fontAsset);
}
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
}
}
}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,302 @@
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEngine;
namespace BFEditor
{
public class EmojiEditor : EditorWindow
{
[MenuItem("BF编辑器/Emoji表情配置")]
public static void EmojiConfiguration()
{
EmojiEditor window = ScriptableObject.CreateInstance<EmojiEditor>();
window.ShowUtility();
}
void OnInspectorUpdate()
{
Repaint();
}
private int emojiSize = 0;
private int columnCount = 0;
//读取PNG路径
private const string READ_EMOJI_PATH_PREFS = "read_emoji_path";
private string readEmojiPath = "";
//生成texture路径
private const string GET_EMOJI_PATH_PREFS = "tar_emoji_path";
private string tarEmojiPath = "";
//配置表路径
private const string CONFIG_PATH_PREFS = "emoji_config_path";
private string tarCfgPath = "";
private string newTextureName = "emoji_total";
private string cfgName = "emoji_info";
private Dictionary<int,Dictionary<int, List<float>>> emojiUVs = new Dictionary<int, Dictionary<int, List<float>>>();
public void OnGUI()
{
emojiSize = EditorGUILayout.IntField("单个表情像素", emojiSize);
EditorGUILayout.Space();
columnCount = EditorGUILayout.IntField("图片表情列数", columnCount);
EditorGUILayout.Space();
//emoji路径
if (GUILayout.Button("选择读取Emoji目标路径"))
{
var readEmojiPath = EditorUtility.OpenFolderPanel("生成png目标文件夹", "", "");
if (!string.IsNullOrEmpty(readEmojiPath))
{
PlayerPrefs.SetString(READ_EMOJI_PATH_PREFS, readEmojiPath);
}
}
readEmojiPath = PlayerPrefs.GetString(READ_EMOJI_PATH_PREFS, "");
EditorGUILayout.LabelField("读取Emoji文件路径", readEmojiPath);
EditorGUILayout.Space();
//生成目标路径
if (GUILayout.Button("选择生成目标路径"))
{
var tarGetPath = EditorUtility.OpenFolderPanel("生成png目标文件夹", "", "");
if (!string.IsNullOrEmpty(tarGetPath))
{
PlayerPrefs.SetString(GET_EMOJI_PATH_PREFS, tarGetPath);
}
}
tarEmojiPath = PlayerPrefs.GetString(GET_EMOJI_PATH_PREFS, "");
EditorGUILayout.LabelField("生成PNG文件路径", tarEmojiPath);
//生成PNG名
newTextureName = EditorGUILayout.TextField("生成PNG名", newTextureName);
EditorGUILayout.Space();
//配置表路径
if (GUILayout.Button("选择配置表目标路径"))
{
var tarCfgPath = EditorUtility.OpenFolderPanel("生成配置表目标文件夹", "", "");
if (!string.IsNullOrEmpty(tarCfgPath))
{
PlayerPrefs.SetString(CONFIG_PATH_PREFS, tarCfgPath);
}
}
tarCfgPath = PlayerPrefs.GetString(CONFIG_PATH_PREFS, "");
EditorGUILayout.LabelField("生成配置表文件路径", tarCfgPath);
cfgName = EditorGUILayout.TextField("生成配置表名", cfgName);
EditorGUILayout.Space();
//合成PNG
if (GUILayout.Button("合成PNG并生成配置"))
{
if (string.IsNullOrEmpty(tarEmojiPath))
{
EditorUtility.DisplayDialog("提示", "没有指定PNG生成文件夹路径", "确定");
return;
}
if (!Directory.Exists(tarEmojiPath))
{
EditorUtility.DisplayDialog("提示", "生成PNG文件夹路径不存在", "确定");
return;
}
if (string.IsNullOrEmpty(readEmojiPath))
{
EditorUtility.DisplayDialog("提示", "没有指定读取emoji文件夹路径", "确定");
return;
}
if (!Directory.Exists(readEmojiPath))
{
EditorUtility.DisplayDialog("提示", "读取emoji文件夹路径不存在", "确定");
return;
}
if (string.IsNullOrEmpty(tarCfgPath))
{
EditorUtility.DisplayDialog("提示", "没有指定配置表文件夹路径", "确定");
return;
}
if (!Directory.Exists(tarCfgPath))
{
EditorUtility.DisplayDialog("提示", "配置表文件夹路径不存在", "确定");
return;
}
EncodePng();
SaveUVsConfig();
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
EditorUtility.DisplayDialog("提示", "生成PNG和配置表完成", "确定");
}
}
private void EncodePng()
{
if (emojiSize == 0 || columnCount == 0)
{
EditorUtility.DisplayDialog("提示", "表情参数不正确", "确定");
return;
}
emojiUVs.Clear();
//先计算生成texture大小
var width = emojiSize * columnCount;
var exp = 1;
while (exp < width)
{
exp <<= 1;
}
width = exp;
var height = 0;
//读取totalCount
var pngs = Directory.GetFiles(readEmojiPath, "*.png");
Debug.Log("表情总数 " + pngs.Length);
var totalCount = pngs.Length;
var otherNum = totalCount % columnCount;
if (otherNum == 0)
{
height = (int)Mathf.Ceil(totalCount / columnCount) * emojiSize;
}
else
{
height = ((int)(totalCount / columnCount) + 1)* emojiSize;
}
exp = 1;
while (exp < height)
{
exp <<= 1;
}
height = exp;
var newTexture = new Texture2D(width, height, TextureFormat.ARGB32, false);
var tmpPngPath = "";
var row = 0;
var column = 0;
var countDown = 0;
foreach (var pngPath in pngs)
{
tmpPngPath = pngPath.Substring(pngPath.IndexOf("Assets"));
tmpPngPath = tmpPngPath.Replace('\\', '/');
var texture = (Texture2D)AssetDatabase.LoadAssetAtPath(tmpPngPath, typeof(Texture2D));
if (texture.width != emojiSize || texture.height != emojiSize)
{
Debug.LogErrorFormat("{0} 表情大小不符合规范 width : {1} height : {2}", texture.name, texture.width, texture.height);
return;
}
Color32[] colors = texture.GetPixels32();
row = countDown % columnCount;
column = countDown / columnCount;
//画上去
var xPos = row * emojiSize;
var yPos = column * emojiSize;
newTexture.SetPixels32(xPos, yPos, emojiSize, emojiSize, colors);
//config
if (!Regex.IsMatch(texture.name,@"emoji\d{1,}_\d{1,}"))
{
Debug.LogErrorFormat("{0} 表情名称不符合规范", texture.name);
return;
}
var index1 = texture.name.Substring(5, 1);
var index2 = int.Parse(texture.name.Split('_')[1]);
var emojiTypeIndex = int.Parse(index1);
Dictionary<int, List<float>> emojiInfos;
if (!emojiUVs.TryGetValue(emojiTypeIndex, out emojiInfos))
{
emojiInfos = new Dictionary<int, List<float>>();
emojiUVs.Add(emojiTypeIndex, emojiInfos);
}
var uvs = new List<float>(8);
Debug.LogWarning(xPos);
Debug.LogWarning(yPos);
Debug.LogWarning(width);
Debug.LogWarning(height);
uvs.Add((float)xPos / width); //uv0
uvs.Add((float)(yPos + emojiSize) / height);
uvs.Add((float)(xPos + emojiSize) / width); //uv1
uvs.Add((float)(yPos + emojiSize) / height);
uvs.Add((float)(xPos + emojiSize) / width); //uv2
uvs.Add((float)yPos / height);
uvs.Add((float)xPos / width); //uv3
uvs.Add((float)yPos / height);
emojiInfos.Add(index2, uvs);
countDown++;
}
newTexture.Apply();
string path = tarEmojiPath + "/" + newTextureName + ".png";
File.WriteAllBytes(path, newTexture.EncodeToPNG());
}
private void SaveUVsConfig()
{
StringBuilder sb = new StringBuilder();
string space = "\t";
string doubleSpace = "\t\t";
string tripleSpace = "\t\t\t";
string fourthSpace = "\t\t\t\t";
sb.Append("local emojiInfo = {\n");
foreach (var pairs in emojiUVs)
{
sb.Append(space + "[" + (pairs.Key) + "] = {\n");
var countDown = 1;
foreach (var _pairs in pairs.Value)
{
sb.Append(doubleSpace + "[" + countDown + "] = {\n");
sb.Append(tripleSpace + "[1] = " + _pairs.Value[0] + ",\n");
sb.Append(tripleSpace + "[2] = " + _pairs.Value[1] + ",\n");
sb.Append(tripleSpace + "[3] = " + _pairs.Value[2] + ",\n");
sb.Append(tripleSpace + "[4] = " + _pairs.Value[3] + ",\n");
sb.Append(tripleSpace + "[5] = " + _pairs.Value[4] + ",\n");
sb.Append(tripleSpace + "[6] = " + _pairs.Value[5] + ",\n");
sb.Append(tripleSpace + "[7] = " + _pairs.Value[6] + ",\n");
sb.Append(tripleSpace + "[8] = " + _pairs.Value[7] + ",\n");
sb.Append(doubleSpace + "},\n");
countDown++;
}
sb.Append(space + "},\n");
}
sb.Append("}\n");
sb.Append("return emojiInfo");
using (StreamWriter fs = new StreamWriter(tarCfgPath + "/" + cfgName + ".lua"))
{
Debug.Log(sb.ToString());
fs.Write(sb.ToString());
}
// using (FileStream fs = File.Open(tarCfgPath + "/" + cfgName + ".lua", FileMode.Create))
// {
// BinaryWriter bw = new BinaryWriter(fs);
// bw.Write(sb.ToString());
// }
Close();
}
}
}

View File

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

View File

@ -0,0 +1,32 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;
namespace BFEditor
{
public static class GameplayEditorMenu
{
[MenuItem("BF编辑器/新手引导编辑器")]
static void OpenTutorialEditor()
{
var designExcelPath = ExportExcelTools.GetDesignExcelPath();
if (string.IsNullOrEmpty(designExcelPath) || !Directory.Exists(designExcelPath))
{
OpenTutorialExcelPathWindow();
return;
}
TutorialConfigWindow window = (TutorialConfigWindow)EditorWindow.GetWindow<TutorialConfigWindow>("新手引导配置窗口");
window.Show();
}
public static void OpenTutorialExcelPathWindow()
{
var window = (TutorialExcelPathWindow)EditorWindow.GetWindowWithRect(typeof(TutorialExcelPathWindow),
new Rect(Screen.width / 2, Screen.height / 2, 500, 120), true);
window.Show();
}
}
}

View File

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

View File

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

View File

@ -0,0 +1,528 @@
#if UNITY_EDITOR && UNITY_STANDALONE
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using NPOI.SS.UserModel;
using System.Linq;
using System.IO;
using System;
namespace BFEditor
{
public class TutorialConfigBridge
{
static TutorialConfigBridge instance;
public static TutorialConfigBridge Instance
{
get
{
if (instance == null)
{
instance = new TutorialConfigBridge();
}
return instance;
}
}
public const int EXTRA_ROW_NUM = 4;//约定的非内容行数
public const int SOURCE_ROW_INDEX = 1;//约定可作为格式规范的行
private static string tutorialExcelPath;
public IWorkbook tutorialConfig;
//excel表内容 *******************************************************************F*****************************
public int idIndex;
public int nextIndex;
public int guideTypeStrIndex;
public int guideDescribeStrIndex;
public int typeIndex;
public int stageIndex;
public int importantIndex;
public int stepsIndex;
public int targetIndex;
public int conditionIndex;
public int nextConditionIndex;
public int actionIndex;
public int paramsIndex;
//表现相关
public int handPosIndex;
public int boxPosIndex;
public int arrowIndex;
public int boxHeadIndex;
public int boxHeadActIndex;
public int boxHeadFlipIndex;
public int boxHeadPositionIndex;
public int animationPosIndex;
public int boxHeadScaleIndex;
public int voiceIndex;
public int highlightIndex;
public int radiusIndex;
public int maskColorIndex;
/// <summary>
/// 打开窗口前调用 用于加载Excel表内容
/// </summary>
public bool LoadTutorialExcel()
{
var tutorialExcelPath = ExportExcelTools.GetDesignExcelPath() + "/tutorial.xlsx";
tutorialConfig = BFExcelHelper.GetWorkbook(tutorialExcelPath);
idIndex = tutorialConfig.GetBFConfigKeyColumnIndex();
nextIndex = tutorialConfig.GetBFConfigfieldColumnIndex("next");
guideTypeStrIndex = tutorialConfig.GetBFConfigfieldColumnIndex("guideTypeStr");
guideDescribeStrIndex = tutorialConfig.GetBFConfigfieldColumnIndex("guideDescribeStr");
typeIndex = tutorialConfig.GetBFConfigfieldColumnIndex("type");
stageIndex = tutorialConfig.GetBFConfigfieldColumnIndex("stage");
importantIndex = tutorialConfig.GetBFConfigfieldColumnIndex("important");
stepsIndex = tutorialConfig.GetBFConfigfieldColumnIndex("steps");
targetIndex = tutorialConfig.GetBFConfigfieldColumnIndex("target");
conditionIndex = tutorialConfig.GetBFConfigfieldColumnIndex("condition");
nextConditionIndex = tutorialConfig.GetBFConfigfieldColumnIndex("nextCondition");
actionIndex = tutorialConfig.GetBFConfigfieldColumnIndex("action");
paramsIndex = tutorialConfig.GetBFConfigfieldColumnIndex("params");
handPosIndex = tutorialConfig.GetBFConfigfieldColumnIndex("handPos");
boxPosIndex = tutorialConfig.GetBFConfigfieldColumnIndex("boxPos");
arrowIndex = tutorialConfig.GetBFConfigfieldColumnIndex("arrow");
boxHeadIndex = tutorialConfig.GetBFConfigfieldColumnIndex("boxHead");
boxHeadActIndex = tutorialConfig.GetBFConfigfieldColumnIndex("boxHeadAct");
boxHeadFlipIndex = tutorialConfig.GetBFConfigfieldColumnIndex("boxHeadFlip");
boxHeadPositionIndex = tutorialConfig.GetBFConfigfieldColumnIndex("boxHeadPosition");
animationPosIndex = tutorialConfig.GetBFConfigfieldColumnIndex("animationPos");
boxHeadScaleIndex = tutorialConfig.GetBFConfigfieldColumnIndex("boxHeadScale");
voiceIndex = tutorialConfig.GetBFConfigfieldColumnIndex("voice");
highlightIndex = tutorialConfig.GetBFConfigfieldColumnIndex("highlight");
radiusIndex = tutorialConfig.GetBFConfigfieldColumnIndex("radius");
maskColorIndex = tutorialConfig.GetBFConfigfieldColumnIndex("maskColor");
return true;
}
public ICell _GetCell(int index, int columnIndex)
{
var sheet = tutorialConfig.GetBFSheet();
var row = sheet.GetRow(index);
var cell = row.GetBFMissingCell(columnIndex);
return cell;
}
public string _GetCellValue(int index, int columnIndex)
{
string cellValue = string.Empty;
var cell = _GetCell(index, columnIndex);
switch (cell.CellType)
{
case CellType.String://字符串类型
cellValue = cell.StringCellValue;
break;
case CellType.Numeric: //数值类型
cellValue = cell.NumericCellValue.ToString();
break;
case CellType.Formula: //公式
cell.SetCellType(CellType.Numeric);
cellValue = cell.NumericCellValue.ToString();
break;
case CellType.Blank:
break;
case CellType.Boolean:
break;
case CellType.Error:
break;
default:
break;
}
return cellValue;
}
public void _SetCellValue(int index, int columnIndex, string value)
{
var cell = _GetCell(index, columnIndex);
cell.SetCellValue(value);
}
//默认引导类型说明
public string GetDefaultGuideTypeStr(TutorialType type)
{
string value = string.Empty;
switch (type)
{
case TutorialType.NONE:
value = string.Empty;
break;
case TutorialType.CLICK:
value = "【手指】";
break;
case TutorialType.CLICK_AND_TEXT:
value = "【文本框】【手指】";
break;
case TutorialType.TEXT:
value = "【文本框】";
break;
case TutorialType.HIGHLIGHT_AND_TEXT:
value = "【文本框】【高亮】";
break;
case TutorialType.CLICK_FULL_SCREEN:
value = "【手指】";
break;
case TutorialType.TALK:
value = "【剧情】";
break;
case TutorialType.ACTION:
value = "【事件】";
break;
case TutorialType.CLICK_IN_BUILDING_MARK:
value = "【手指】";
break;
case TutorialType.FIND_BUILDING_IN_SCENE:
value = "【手指】";
break;
case TutorialType.HIGHLIGHT_BOSS:
value = "【文本框】【高亮】";
break;
case TutorialType.HIGHLIGHT_COMEOUT:
value = "【文本框】【高亮】";
break;
case TutorialType.HIGHLIGHT_BATTLE_UNIT:
value = "【文本框】【高亮】";
break;
default:
value = string.Empty;
break;
}
return value;
}
//数据Get Set ******************************************************************************
//ID
public string GetID(int index)
{
return _GetCellValue(index, idIndex);
}
public void SetID(int index, string value)
{
_SetCellValue(index, idIndex, value);
}
//Next
public string GetNextID(int index)
{
return _GetCellValue(index, nextIndex);
}
public void SetNextID(int index, string value)
{
_SetCellValue(index, nextIndex, value);
}
//GuideTypeStr
public string GetGuideTypeStr(int index)
{
return _GetCellValue(index, guideTypeStrIndex);
}
public void SetGuideTypeStr(int index, string value)
{
_SetCellValue(index, guideTypeStrIndex, value);
}
//GuideDescribeStr
public string GetGuideDescribeStr(int index)
{
return _GetCellValue(index, guideDescribeStrIndex);
}
public void SetGuideDescribeStr(int index, string value)
{
_SetCellValue(index, guideDescribeStrIndex, value);
}
//Type
public string GetType(int index)
{
return _GetCellValue(index, typeIndex);
}
public void SetType(int index, string value)
{
_SetCellValue(index, typeIndex, value);
}
//Stage
public string GetStage(int index)
{
return _GetCellValue(index, stageIndex);
}
public void SetStage(int index, string value)
{
_SetCellValue(index, stageIndex, value);
}
//Important
public string GetImportant(int index)
{
return _GetCellValue(index, importantIndex);
}
public void SetImportant(int index, string value)
{
_SetCellValue(index, importantIndex, value);
}
//Steps
public string GetSteps(int index)
{
return _GetCellValue(index, stepsIndex);
}
public void SetSteps(int index, string value)
{
_SetCellValue(index, stepsIndex, value);
}
//Target
public string GetTarget(int index)
{
return _GetCellValue(index, targetIndex);
}
public void SetTarget(int index, string value)
{
_SetCellValue(index, targetIndex, value);
}
//Condition
public string GetCondition(int index)
{
return _GetCellValue(index, conditionIndex);
}
public void SetCondition(int index, string value)
{
_SetCellValue(index, conditionIndex, value);
}
//NextCondition
public string GetNextCondition(int index)
{
return _GetCellValue(index, nextConditionIndex);
}
public void SetNextCondition(int index, string value)
{
_SetCellValue(index, nextConditionIndex, value);
}
//Action
public string GetAction(int index)
{
return _GetCellValue(index, actionIndex);
}
public void SetAction(int index, string value)
{
_SetCellValue(index, actionIndex, value);
}
//ActionParams
public string GetActionParams(int index)
{
return _GetCellValue(index, paramsIndex);
}
public void SetActionParams(int index, string value)
{
_SetCellValue(index, paramsIndex, value);
}
//HandPos
public string GetHandPos(int index)
{
return _GetCellValue(index, handPosIndex);
}
public void SetHandPos(int index, string value)
{
_SetCellValue(index, handPosIndex, value);
}
//BoxPos
public string GetBoxPos(int index)
{
return _GetCellValue(index, boxPosIndex);
}
public void SetBoxPos(int index, string value)
{
_SetCellValue(index, boxPosIndex, value);
}
//Arrow
public string GetArrow(int index)
{
return _GetCellValue(index, arrowIndex);
}
public void SetArrow(int index, string value)
{
_SetCellValue(index, arrowIndex, value);
}
//BoxHead
public string GetBoxHead(int index)
{
return _GetCellValue(index, boxHeadIndex);
}
public void SetBoxHead(int index, string value)
{
_SetCellValue(index, boxHeadIndex, value);
}
//BoxHeadAct
public string GetBoxHeadAct(int index)
{
return _GetCellValue(index, boxHeadActIndex);
}
public void SetBoxHeadAct(int index, string value)
{
_SetCellValue(index, boxHeadActIndex, value);
}
//BoxHeadFlip
public string GetBoxHeadFlip(int index)
{
return _GetCellValue(index, boxHeadFlipIndex);
}
public void SetBoxHeadFlip(int index, string value)
{
_SetCellValue(index, boxHeadFlipIndex, value);
}
//BoxHeadPosition
public string GetBoxHeadPosition(int index)
{
return _GetCellValue(index, boxHeadPositionIndex);
}
public void SetBoxHeadPosition(int index, string value)
{
_SetCellValue(index, boxHeadPositionIndex, value);
}
//AnimationPos
public string GetAnimationPosition(int index)
{
return _GetCellValue(index, animationPosIndex);
}
public void SetAnimationPosition(int index, string value)
{
_SetCellValue(index, animationPosIndex, value);
}
//BoxHeadScale
public string GetBoxHeadScale(int index)
{
return _GetCellValue(index, boxHeadScaleIndex);
}
public void SetBoxHeadScale(int index, string value)
{
_SetCellValue(index, boxHeadScaleIndex, value);
}
//Voice
public string GetVoice(int index)
{
return _GetCellValue(index, voiceIndex);
}
public void SetVoice(int index, string value)
{
_SetCellValue(index, voiceIndex, value);
}
//Voice
public string GetHighlight(int index)
{
return _GetCellValue(index, highlightIndex);
}
public void SetHighlight(int index, string value)
{
_SetCellValue(index, highlightIndex, value);
}
//Radius
public string GetRadius(int index)
{
return _GetCellValue(index, radiusIndex);
}
public void SetRadius(int index, string value)
{
_SetCellValue(index, radiusIndex, value);
}
//MaskColor
public string GetMaskColor(int index)
{
return _GetCellValue(index, maskColorIndex);
}
public void SetMaskColor(int index, string value)
{
_SetCellValue(index, maskColorIndex, value);
}
//通用方法 ******************************************************************************
/// <summary>
/// 获得实际行数
/// </summary>
public int GetRowNum()
{
if (tutorialConfig == null)
return 0;
var sheet = tutorialConfig.GetBFSheet();
return sheet.LastRowNum + 1;
}
/// <summary>
/// 插入一条新引导
/// </summary>
/// <param name="index"></param>
public void InsertRow(int index)
{
if (tutorialConfig == null)
return;
var sheet = tutorialConfig.GetBFSheet();
//如果不是在最末处添加 则需要移动
if (index < sheet.LastRowNum + 1)
{
sheet.ShiftRows(index, sheet.LastRowNum, 1);
}
var row = sheet.CreateRow(index);
var sourceRow = sheet.GetRow(SOURCE_ROW_INDEX);//作为可参考的原row
for (int i = 0; i < sourceRow.LastCellNum; i++)
{
var cell = row.CreateCell(i);
cell.SetCellType(sourceRow.GetCell(i).CellType);
}
//保存
SaveExcel();
//重新加载
LoadTutorialExcel();
}
/// <summary>
/// 删除一条引导
/// </summary>
/// <param name="index"></param>
public void DeleteRow(int index)
{
if (tutorialConfig == null)
return;
var sheet = tutorialConfig.GetBFSheet();
sheet.ShiftRows(index + 1, sheet.LastRowNum + 1, -1);
//保存
SaveExcel();
//重新加载
LoadTutorialExcel();
}
/// <summary>
/// 将现有数据保存至Excel
/// </summary>
public void SaveExcel()
{
if (tutorialConfig == null)
return;
var tutorialExcelPath = ExportExcelTools.GetDesignExcelPath() + "/tutorial.xlsx";
tutorialConfig.SaveBFExcel(tutorialExcelPath);
}
}
}
#endif

View File

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

View File

@ -0,0 +1,750 @@
using System.Text;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
namespace BFEditor
{
public class TutorialConfigWindow : EditorWindow
{
#if UNITY_EDITOR && UNITY_STANDALONE
int selectIndex = -1;//当前选中的引导Index
const float WidthOfLeftPanel = 850;
const float WidthOfMiddlePanel = 350;
const float WidthOfRightPanel = 300;
Vector2 leftScrollPos;
Vector2 middleScrollPos;
Vector2 rightScrollPos;
const int DEFAULT_MASK_COLOR = 156;//默认遮罩颜色
void Awake()
{
TutorialConfigBridge.Instance.LoadTutorialExcel();
}
void OnGUI()
{
DrawOverviewArea();
DrawDetailArea();
DrawHelpArea();
}
void DrawOverviewArea()
{
GUILayout.BeginArea(new Rect(0, 0, WidthOfLeftPanel, position.height));
leftScrollPos = EditorGUILayout.BeginScrollView(leftScrollPos);
for (int i = TutorialConfigBridge.EXTRA_ROW_NUM; i < TutorialConfigBridge.Instance.GetRowNum(); i++)
{
GUILayout.BeginHorizontal();
EditorGUILayout.LabelField(string.Format("ID:{0}", TutorialConfigBridge.Instance.GetID(i)), GUILayout.Width(80));
EditorGUILayout.LabelField(string.Format("类型:{0}", TutorialConfigBridge.Instance.GetGuideTypeStr(i)), GUILayout.Width(200));
EditorGUILayout.LabelField(string.Format("说明:{0}", TutorialConfigBridge.Instance.GetGuideDescribeStr(i)), GUILayout.Width(300));
if (GUILayout.Button("查看", GUILayout.Width(50)))
{
selectIndex = i;
}
if (GUILayout.Button("上方插入", GUILayout.Width(60)))
{
TutorialConfigBridge.Instance.InsertRow(i);
Repaint();
}
if (GUILayout.Button("下方插入", GUILayout.Width(60)))
{
TutorialConfigBridge.Instance.InsertRow(i + 1);
Repaint();
}
if (GUILayout.Button("删除", GUILayout.Width(50)))
{
TutorialConfigBridge.Instance.DeleteRow(i);
Repaint();
}
GUILayout.EndHorizontal();
}
EditorGUILayout.EndScrollView();
GUILayout.EndArea();
}
void DrawDetailArea()
{
GUILayout.BeginArea(new Rect(WidthOfLeftPanel + 20, 0, WidthOfMiddlePanel, position.height));
middleScrollPos = EditorGUILayout.BeginScrollView(middleScrollPos);
if (TutorialConfigBridge.EXTRA_ROW_NUM <= selectIndex && selectIndex < TutorialConfigBridge.Instance.GetRowNum())
{
var id = TutorialConfigBridge.Instance.GetID(selectIndex);
var idType = TutorialIdType.NONE; // 引导大类型
if (!string.IsNullOrEmpty(id))
{
idType = (TutorialIdType)(int.Parse(id) / 10000);
}
//版本
if (idType == TutorialIdType.VERSION)
{
DrawID();
}
//其他(强引导1,功能开启2,功能指引4,跳转8)
else
{
DrawID();
DrawNext();
DrawType();
DrawGuideTypeStr();
DrawDescribeStr();
EditorGUILayout.Space();
DrawImportantAndSteps();
EditorGUILayout.Space();
DrawTarget();
EditorGUILayout.Space();
DrawStage();
DrawCondition();
DrawNextCondition();
DrawAction();
DrawParams();
EditorGUILayout.Space();
DrawHandPos();
DrawBoxPos();
DrawArrow();
DrawBoxHead();
DrawBoxHeadAct();
DrawBoxHeadFlip();
DrawBoxHeadPosition();
DrawAnimationPos();
DrawBoxHeadScale();
DrawHighlight();
DrawRadius();
DrawMaskColor();
EditorGUILayout.Space();
DrawVoice();
}
}
EditorGUILayout.EndScrollView();
GUILayout.EndArea();
}
void DrawHelpArea()
{
GUILayout.BeginArea(new Rect(WidthOfLeftPanel + WidthOfMiddlePanel + 40, 0, WidthOfRightPanel, position.height));
rightScrollPos = EditorGUILayout.BeginScrollView(rightScrollPos);
DrawGuideTypeHelp();
DrawImportantTypeHelp();
DrawConditionTypeHelp();
DrawActionTypeHelp();
EditorGUILayout.EndScrollView();
GUILayout.EndArea();
}
//详细绘制 *******************************************************************************************
void DrawID()
{
EditorGUILayout.BeginHorizontal();
EditorGUI.BeginChangeCheck();
var value = EditorGUILayout.TextField("ID(int):", TutorialConfigBridge.Instance.GetID(selectIndex));
if (EditorGUI.EndChangeCheck())
{
int parseInt = 0;
if (int.TryParse(value, out parseInt))
{
TutorialConfigBridge.Instance.SetID(selectIndex, value);
UpdateExcel();
}
else
{
Debug.LogError("ID需要为5位整数");
}
}
EditorGUILayout.EndHorizontal();
}
void DrawNext()
{
EditorGUILayout.BeginHorizontal();
EditorGUI.BeginChangeCheck();
var value = EditorGUILayout.TextField("NextID(int):", TutorialConfigBridge.Instance.GetNextID(selectIndex));
if (EditorGUI.EndChangeCheck())
{
int parseInt = 0;
if (int.TryParse(value, out parseInt))
{
TutorialConfigBridge.Instance.SetNextID(selectIndex, value);
UpdateExcel();
}
else
{
Debug.LogError("NextID需要为5位整数");
}
}
EditorGUILayout.EndHorizontal();
}
void DrawType()
{
EditorGUILayout.BeginHorizontal();
EditorGUI.BeginChangeCheck();
var curType = TutorialConfigBridge.Instance.GetType(selectIndex);
var curTypeEnum = TutorialType.NONE;
if (!string.IsNullOrEmpty(curType))
{
curTypeEnum = (TutorialType)int.Parse(curType);
}
var newType = (TutorialType)EditorGUILayout.EnumPopup("引导类型:", curTypeEnum);
if (EditorGUI.EndChangeCheck())
{
var value = ((int)newType).ToString();
TutorialConfigBridge.Instance.SetType(selectIndex, value);
//自动配置TypeStr
TutorialConfigBridge.Instance.SetGuideTypeStr(selectIndex, TutorialConfigBridge.Instance.GetDefaultGuideTypeStr(newType));
UpdateExcel();
}
EditorGUILayout.EndHorizontal();
}
void DrawGuideTypeStr()
{
EditorGUILayout.BeginHorizontal();
EditorGUI.BeginChangeCheck();
var value = EditorGUILayout.TextField("类型说明(string):", TutorialConfigBridge.Instance.GetGuideTypeStr(selectIndex));
if (EditorGUI.EndChangeCheck())
{
TutorialConfigBridge.Instance.SetGuideTypeStr(selectIndex, value);
UpdateExcel();
}
EditorGUILayout.EndHorizontal();
}
void DrawDescribeStr()
{
EditorGUILayout.BeginHorizontal();
EditorGUI.BeginChangeCheck();
var value = EditorGUILayout.TextField("引导说明(string):", TutorialConfigBridge.Instance.GetGuideDescribeStr(selectIndex));
if (EditorGUI.EndChangeCheck())
{
TutorialConfigBridge.Instance.SetGuideDescribeStr(selectIndex, value);
UpdateExcel();
}
EditorGUILayout.EndHorizontal();
}
void DrawStage()
{
EditorGUILayout.BeginHorizontal();
EditorGUI.BeginChangeCheck();
var value = EditorGUILayout.TextField("触发关卡(int):", TutorialConfigBridge.Instance.GetStage(selectIndex));
if (EditorGUI.EndChangeCheck())
{
int parseInt = 0;
if (int.TryParse(value, out parseInt))
{
TutorialConfigBridge.Instance.SetStage(selectIndex, value);
UpdateExcel();
}
else
{
Debug.LogError("Stage需要为整数");
}
}
EditorGUILayout.EndHorizontal();
}
void DrawImportantAndSteps()
{
EditorGUILayout.BeginHorizontal();
EditorGUI.BeginChangeCheck();
var value = EditorGUILayout.TextField("关键节点类型(string):", TutorialConfigBridge.Instance.GetImportant(selectIndex));
if (EditorGUI.EndChangeCheck())
{
TutorialConfigBridge.Instance.SetImportant(selectIndex, value);
UpdateExcel();
}
EditorGUILayout.EndHorizontal();
//Steps
if (!string.IsNullOrEmpty(value))
{
EditorGUILayout.BeginHorizontal();
EditorGUI.BeginChangeCheck();
var value2 = EditorGUILayout.TextField("中断时重启步骤([int]):", TutorialConfigBridge.Instance.GetSteps(selectIndex));
if (EditorGUI.EndChangeCheck())
{
TutorialConfigBridge.Instance.SetSteps(selectIndex, value2);
UpdateExcel();
}
EditorGUILayout.EndHorizontal();
}
else
{
var value2 = TutorialConfigBridge.Instance.GetSteps(selectIndex);
if (!string.IsNullOrEmpty(value2))
{
//清空steps
TutorialConfigBridge.Instance.SetSteps(selectIndex, string.Empty);
UpdateExcel();
}
}
}
void DrawTarget()
{
EditorGUILayout.BeginHorizontal();
EditorGUI.BeginChangeCheck();
var value = EditorGUILayout.TextField("UI目标(string):", TutorialConfigBridge.Instance.GetTarget(selectIndex));
if (EditorGUI.EndChangeCheck())
{
TutorialConfigBridge.Instance.SetTarget(selectIndex, value);
UpdateExcel();
}
EditorGUILayout.EndHorizontal();
}
void DrawCondition()
{
//type
EditorGUILayout.BeginHorizontal();
EditorGUI.BeginChangeCheck();
var curCondition = TutorialConfigBridge.Instance.GetCondition(selectIndex);
var curConditionTypeEnum = TutorialConditionType.NONE;
var curConditionValue = 0;
if (!string.IsNullOrEmpty(curCondition))
{
Vector2Int formatCurCondition = SplitStringToVector2Int(curCondition);
curConditionTypeEnum = (TutorialConditionType)formatCurCondition.x;
curConditionValue = formatCurCondition.y;
}
var newConditionType = (TutorialConditionType)EditorGUILayout.EnumPopup("触发条件类型:", curConditionTypeEnum);
if (EditorGUI.EndChangeCheck())
{
//如果设置为None 则清空数据
if (newConditionType == TutorialConditionType.NONE)
{
TutorialConfigBridge.Instance.SetCondition(selectIndex, string.Empty);
UpdateExcel();
}
//正常更新type
else
{
var newValue = Vector2IntToString(new Vector2Int((int)newConditionType, curConditionValue));
TutorialConfigBridge.Instance.SetCondition(selectIndex, newValue);
UpdateExcel();
}
}
EditorGUILayout.EndHorizontal();
//value
if (curConditionTypeEnum != TutorialConditionType.NONE)
{
EditorGUILayout.BeginHorizontal();
EditorGUI.BeginChangeCheck();
var value = EditorGUILayout.TextField("触发条件参数(int):", curConditionValue.ToString());
if (EditorGUI.EndChangeCheck())
{
int parseInt = 0;
if (int.TryParse(value, out parseInt))
{
var newValue2 = Vector2IntToString(new Vector2Int((int)newConditionType, parseInt));
TutorialConfigBridge.Instance.SetCondition(selectIndex, newValue2);
UpdateExcel();
}
else
{
Debug.LogError("触发条件参数需要为整数");
}
}
EditorGUILayout.EndHorizontal();
}
}
void DrawNextCondition()
{
//type
EditorGUILayout.BeginHorizontal();
EditorGUI.BeginChangeCheck();
var curCondition = TutorialConfigBridge.Instance.GetNextCondition(selectIndex);
var curConditionTypeEnum = TutorialConditionType.NONE;
var curConditionValue = 0;
if (!string.IsNullOrEmpty(curCondition))
{
Vector2Int formatCurCondition = SplitStringToVector2Int(curCondition);
curConditionTypeEnum = (TutorialConditionType)formatCurCondition.x;
curConditionValue = formatCurCondition.y;
}
var newConditionType = (TutorialConditionType)EditorGUILayout.EnumPopup("结束条件类型:", curConditionTypeEnum);
if (EditorGUI.EndChangeCheck())
{
//如果设置为None 则清空数据
if (newConditionType == TutorialConditionType.NONE)
{
TutorialConfigBridge.Instance.SetNextCondition(selectIndex, string.Empty);
UpdateExcel();
}
//正常更新type
else
{
var newValue = Vector2IntToString(new Vector2Int((int)newConditionType, curConditionValue));
TutorialConfigBridge.Instance.SetNextCondition(selectIndex, newValue);
UpdateExcel();
}
}
EditorGUILayout.EndHorizontal();
//value
if (curConditionTypeEnum != TutorialConditionType.NONE)
{
EditorGUILayout.BeginHorizontal();
EditorGUI.BeginChangeCheck();
var value = EditorGUILayout.TextField("结束条件参数(int):", curConditionValue.ToString());
if (EditorGUI.EndChangeCheck())
{
int parseInt = 0;
if (int.TryParse(value, out parseInt))
{
var newValue2 = Vector2IntToString(new Vector2Int((int)newConditionType, parseInt));
TutorialConfigBridge.Instance.SetNextCondition(selectIndex, newValue2);
UpdateExcel();
}
else
{
Debug.LogError("结束条件参数需要为整数");
}
}
EditorGUILayout.EndHorizontal();
}
}
void DrawAction()
{
EditorGUILayout.BeginHorizontal();
EditorGUI.BeginChangeCheck();
var curType = TutorialConfigBridge.Instance.GetAction(selectIndex);
var curTypeEnum = TutorialActionType.NONE;
if (!string.IsNullOrEmpty(curType))
{
curTypeEnum = (TutorialActionType)int.Parse(curType);
}
var newType = (TutorialActionType)EditorGUILayout.EnumPopup("特殊行为:", curTypeEnum);
if (EditorGUI.EndChangeCheck())
{
var value = ((int)newType).ToString();
TutorialConfigBridge.Instance.SetAction(selectIndex, value);
UpdateExcel();
}
EditorGUILayout.EndHorizontal();
}
void DrawParams()
{
EditorGUILayout.BeginHorizontal();
EditorGUI.BeginChangeCheck();
var value = EditorGUILayout.TextField("特殊参数([int]):", TutorialConfigBridge.Instance.GetActionParams(selectIndex));
if (EditorGUI.EndChangeCheck())
{
TutorialConfigBridge.Instance.SetActionParams(selectIndex, value);
UpdateExcel();
}
EditorGUILayout.EndHorizontal();
}
//表现相关
void DrawHandPos()
{
EditorGUILayout.BeginHorizontal();
EditorGUI.BeginChangeCheck();
var curValue = TutorialConfigBridge.Instance.GetHandPos(selectIndex);
Vector2Int formatCurValue = SplitStringToVector2Int(curValue);
var newValue = EditorGUILayout.Vector2IntField("手指偏移:", formatCurValue);
if (EditorGUI.EndChangeCheck())
{
var value = Vector2IntToString(newValue);
TutorialConfigBridge.Instance.SetHandPos(selectIndex, value);
UpdateExcel();
}
EditorGUILayout.EndHorizontal();
}
void DrawBoxPos()
{
EditorGUILayout.BeginHorizontal();
EditorGUI.BeginChangeCheck();
var curValue = TutorialConfigBridge.Instance.GetBoxPos(selectIndex);
Vector2Int formatCurValue = SplitStringToVector2Int(curValue);
var newValue = EditorGUILayout.Vector2IntField("文本框偏移:", formatCurValue);
if (EditorGUI.EndChangeCheck())
{
var value = Vector2IntToString(newValue);
TutorialConfigBridge.Instance.SetBoxPos(selectIndex, value);
UpdateExcel();
}
EditorGUILayout.EndHorizontal();
}
void DrawArrow()
{
EditorGUILayout.BeginHorizontal();
EditorGUI.BeginChangeCheck();
var curValue = TutorialConfigBridge.Instance.GetArrow(selectIndex);
var toggleValue = EditorGUILayout.Toggle("文本框箭头:", curValue == "1");
if (EditorGUI.EndChangeCheck())
{
var value = toggleValue ? "1" : string.Empty;
TutorialConfigBridge.Instance.SetArrow(selectIndex, value);
UpdateExcel();
}
EditorGUILayout.EndHorizontal();
}
void DrawBoxHead()
{
EditorGUILayout.BeginHorizontal();
EditorGUI.BeginChangeCheck();
var value = EditorGUILayout.TextField("引导员形象(string):", TutorialConfigBridge.Instance.GetBoxHead(selectIndex));
if (EditorGUI.EndChangeCheck())
{
TutorialConfigBridge.Instance.SetBoxHead(selectIndex, value);
UpdateExcel();
}
EditorGUILayout.EndHorizontal();
}
void DrawBoxHeadAct()
{
EditorGUILayout.BeginHorizontal();
EditorGUI.BeginChangeCheck();
var curValue = TutorialConfigBridge.Instance.GetBoxHeadAct(selectIndex);
var toggleValue = EditorGUILayout.Toggle("引导员show动作(仅对话类):", curValue == "1");
if (EditorGUI.EndChangeCheck())
{
var value = toggleValue ? "1" : string.Empty;
TutorialConfigBridge.Instance.SetBoxHeadAct(selectIndex, value);
UpdateExcel();
}
EditorGUILayout.EndHorizontal();
}
void DrawBoxHeadFlip()
{
EditorGUILayout.BeginHorizontal();
EditorGUI.BeginChangeCheck();
var curValue = TutorialConfigBridge.Instance.GetBoxHeadFlip(selectIndex);
var toggleValue = EditorGUILayout.Toggle("引导员翻转:", curValue == "1");
if (EditorGUI.EndChangeCheck())
{
var value = toggleValue ? "1" : string.Empty;
TutorialConfigBridge.Instance.SetBoxHeadFlip(selectIndex, value);
UpdateExcel();
}
EditorGUILayout.EndHorizontal();
}
void DrawBoxHeadPosition()
{
EditorGUILayout.BeginHorizontal();
EditorGUI.BeginChangeCheck();
var curValue = TutorialConfigBridge.Instance.GetBoxHeadPosition(selectIndex);
var toggleValue = EditorGUILayout.Toggle("引导员是否在左边(剧情专用):", curValue == "1");
if (EditorGUI.EndChangeCheck())
{
var value = toggleValue ? "1" : string.Empty;
TutorialConfigBridge.Instance.SetBoxHeadPosition(selectIndex, value);
UpdateExcel();
}
EditorGUILayout.EndHorizontal();
}
void DrawAnimationPos()
{
EditorGUILayout.BeginHorizontal();
EditorGUI.BeginChangeCheck();
var curValue = TutorialConfigBridge.Instance.GetAnimationPosition(selectIndex);
Vector2Int formatCurValue = SplitStringToVector2Int(curValue);
var newValue = EditorGUILayout.Vector2IntField("引导员偏移:", formatCurValue);
if (EditorGUI.EndChangeCheck())
{
var value = Vector2IntToString(newValue);
TutorialConfigBridge.Instance.SetAnimationPosition(selectIndex, value);
UpdateExcel();
}
EditorGUILayout.EndHorizontal();
}
void DrawBoxHeadScale()
{
EditorGUILayout.BeginHorizontal();
EditorGUI.BeginChangeCheck();
var value = EditorGUILayout.TextField("引导员缩放(1000标准值)(int):", TutorialConfigBridge.Instance.GetBoxHeadScale(selectIndex));
if (EditorGUI.EndChangeCheck())
{
int parseInt = 0;
if (int.TryParse(value, out parseInt))
{
TutorialConfigBridge.Instance.SetBoxHeadScale(selectIndex, value);
UpdateExcel();
}
else
{
Debug.LogError("BoxHeadScale需要为整数");
}
}
EditorGUILayout.EndHorizontal();
}
void DrawHighlight()
{
EditorGUILayout.BeginHorizontal();
EditorGUI.BeginChangeCheck();
var curValue = TutorialConfigBridge.Instance.GetHighlight(selectIndex);
var toggleValue = EditorGUILayout.Toggle("高亮形状(默认圆,勾上为方形):", curValue == "1");
if (EditorGUI.EndChangeCheck())
{
var value = toggleValue ? "1" : string.Empty;
TutorialConfigBridge.Instance.SetHighlight(selectIndex, value);
UpdateExcel();
}
EditorGUILayout.EndHorizontal();
}
void DrawRadius()
{
EditorGUILayout.BeginHorizontal();
EditorGUI.BeginChangeCheck();
var value = EditorGUILayout.TextField("高亮半径(默认100)(int):", TutorialConfigBridge.Instance.GetRadius(selectIndex));
if (EditorGUI.EndChangeCheck())
{
int parseInt = 0;
if (int.TryParse(value, out parseInt))
{
TutorialConfigBridge.Instance.SetRadius(selectIndex, value);
UpdateExcel();
}
else
{
Debug.LogError("Radius需要为整数");
}
}
EditorGUILayout.EndHorizontal();
}
void DrawMaskColor()
{
EditorGUILayout.BeginHorizontal();
EditorGUI.BeginChangeCheck();
var curValue = TutorialConfigBridge.Instance.GetMaskColor(selectIndex);
var curSliderValue = DEFAULT_MASK_COLOR;
if (!string.IsNullOrEmpty(curValue))
{
curSliderValue = int.Parse(curValue);
}
var sliderValue = EditorGUILayout.IntSlider("遮罩颜色值(剧情类无效):", curSliderValue, 0, 255);
if (EditorGUI.EndChangeCheck())
{
var value = sliderValue.ToString();
TutorialConfigBridge.Instance.SetMaskColor(selectIndex, value);
UpdateExcel();
}
EditorGUILayout.EndHorizontal();
}
void DrawVoice()
{
EditorGUILayout.BeginHorizontal();
EditorGUI.BeginChangeCheck();
var value = EditorGUILayout.TextField("语音(string):", TutorialConfigBridge.Instance.GetVoice(selectIndex));
if (EditorGUI.EndChangeCheck())
{
TutorialConfigBridge.Instance.SetVoice(selectIndex, value);
UpdateExcel();
}
EditorGUILayout.EndHorizontal();
}
//帮助绘制 *******************************************************************************************
void DrawGuideTypeHelp()
{
//TODO
}
void DrawImportantTypeHelp()
{
//TODO
}
void DrawConditionTypeHelp()
{
//TODO
}
void DrawActionTypeHelp()
{
//TODO
}
//通用接口 *******************************************************************************************
/// <summary>
/// 将[a,b,c...,x]格式的string类型转为int[]类型
/// </summary>
/// <param name="value"></param>
int[] SplitStringToIntArray(string value)
{
int[] array = null;
if (value.StartsWith("[") && value.EndsWith("]"))
{
var valueStrs = value.Split(',', '[', ']');
array = new int[valueStrs.Length];
for (int i = 0; i < array.Length - 2; i++)
{
array[i] = int.Parse(valueStrs[i + 1]);
}
}
return array;
}
string IntArrayToString(int[] array)
{
string stringValue = string.Empty;
if (array != null && array.Length > 0)
{
stringValue = "[";
for (int i = 0; i < array.Length; i++)
{
stringValue = stringValue + array[i];
if (i < array.Length - 1)
{
stringValue = stringValue + ",";
}
}
stringValue = stringValue + "]";
}
return stringValue;
}
/// <summary>
/// 将[a,b]格式的string类型转为Vector2Int类型
/// </summary>
/// <param name="value"></param>
Vector2Int SplitStringToVector2Int(string value)
{
Vector2Int array = new Vector2Int();
if (value.StartsWith("[") && value.EndsWith("]"))
{
var valueStrs = value.Split(',', '[', ']');
if (valueStrs.Length == 4)
{
for (int i = 0; i < 2; i++)
{
array[i] = int.Parse(valueStrs[i + 1]);
}
}
}
return array;
}
string Vector2IntToString(Vector2Int value)
{
string stringValue = string.Empty;
stringValue = string.Format("[{0},{1}]", value.x, value.y);
return stringValue;
}
/// <summary>
/// 更新Excel
/// </summary>
void UpdateExcel()
{
TutorialConfigBridge.Instance.SaveExcel();
}
#endif
}
}

View File

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

View File

@ -0,0 +1,72 @@
namespace BFEditor
{
public enum TutorialIdType
{
NONE = 0,
FORCE = 1,
FUNCTION_OPEN = 2,
// SPECIAL = 3,//不会在编辑器中生成
FUNCTION_GUIDE = 4,
JUMP = 8,
VERSION = 9,
}
public enum TutorialType
{
NONE = 0,
CLICK = 2,
CLICK_AND_TEXT = 4,
TEXT = 5,
HIGHLIGHT_AND_TEXT = 6,
CLICK_FULL_SCREEN = 7,
TALK = 8,
ACTION = 10,
CLICK_IN_BUILDING_MARK = 22, // 城建商店里点击
FIND_BUILDING_IN_SCENE = 23, // 在城建中找到已建造的建筑
HIGHLIGHT_BOSS = 31, // 高亮 boss建筑
HIGHLIGHT_COMEOUT = 32, // 高亮 上阵
HIGHLIGHT_BATTLE_UNIT = 42, // 高亮 特定战斗单位
CLICK_AND_TEXT_FOR_HANG_UP = 51, // 领取挂机奖励专用
}
public enum TutorialActionType
{
NONE = 0,
SET_GESTURE_ENABLE = 1, // 设置手势有效
SET_GESTURE_DISABLE = 2, // 设置手势无效
SET_BATTLE_TUTORIAL = 3, // 设定为引导战斗,无法退出
SET_BATTLE_NORMAL = 4, // 设定为正常战斗
SET_PURCHASE_BUILDING_ENABLE = 5, // 设置城建购买按钮是否有效
SELECT_SUMMON_TYPE = 6, // 选择抽卡类别
SELECT_TEAM_MEMBER = 7, // 上阵选人
SELECT_HERO_LIST = 8, // 找到英雄列表中的英雄cell
SELECT_BATTLE_UNIT = 9, // 选择特定的战斗单位
SELECT_TARGET_BUY_BUILDING = 10, // 选择城建商店特定建筑
WAIT_UI_OR_PLOT_OVER = 11, // 特定UI打开或收到插画播放结束的消息
WAIT_SUMMON_DISPLAY = 12, // 等待抽卡展示结束
WAIT_COMEOUT_UI_LOAD_OVER = 13, // 出战界面是否全部加载完毕
WAIT_BATTLE_START_AND_PAUSE = 14, // 当战斗相机初始动画完毕后 暂停
RESUME_AND_START_BATTLE = 15, // 恢复继续战斗(相机初始动画配对)
WAIT_BATTLE = 16, // 等待战斗进行,此阶段允许普通ui操作
WAIT_LEVEL_UP_UI = 17, // 等待升级界面弹出完毕
WAIT_TIME = 18, // 等待一定时间
WAIT_UI_CLOSE = 19, // 等待一个UI关闭
SHOW_RISK_SIDE_BAR = 20, // 播放侧边栏显示效果
WAIT_BATTLE_ROUND_AND_PAUSE = 21, // 当战斗到特定回合时 暂停
RESUME_AND_START_ROUND = 22, // 恢复继续战斗(回合暂停配对)
}
public enum TutorialConditionType
{
NONE = 0,
UI_OPEN = 2,
UI_CLOSE = 3,
CLICK = 4,
SCENE = 6,
TEAM_MEMBER_NUM = 11,//出战选人数量
SUMMON_CELL_CENTER = 12,
HAS_BUILDING = 31,
HERO_LEVEL = 41,
HERO_EQUIP = 42,
}
}

View File

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

View File

@ -0,0 +1,75 @@
using System;
using System.IO;
using UnityEditor;
using UnityEngine;
using XLua;
using Debug = UnityEngine.Debug;
namespace BFEditor
{
public class TutorialExcelPathWindow : EditorWindow
{
private string designExcelPath = "";
private bool designSuccFlag = false;
private bool skillSpecialSuccFlag = false;
private bool isDev = false;
public TutorialExcelPathWindow()
{
this.titleContent = new GUIContent("引导文件");
}
void Awake()
{
var luaPath = Path.Combine(Application.dataPath, "Developer/lua/main.lua").Replace("\\", "/");
isDev = File.Exists(luaPath);
}
private void OnEnable()
{
designExcelPath = ExportExcelTools.GetDesignExcelPath();
}
private void OnGUI()
{
#region Excel路径
GUILayout.Space(18);
GUILayout.BeginHorizontal();
GUILayout.Label("选择excel路径");
GUILayout.TextField(designExcelPath, GUILayout.Width(300));
if (GUILayout.Button("选择", GUILayout.Width(80)))
{
string openPath = EditorUtility.OpenFolderPanel("select excel path", designExcelPath, "");
if (openPath.CompareTo("") != 0){
designExcelPath = openPath;
}
}
GUILayout.EndHorizontal();
GUILayout.Space(10);
#endregion
#region lua
GUILayout.BeginHorizontal();
GUILayout.Space(150);
if (GUILayout.Button("打开引导编辑器", GUILayout.Width(200), GUILayout.Height(40)))
{
OnClickOpen();
}
GUILayout.EndHorizontal();
GUILayout.Space(30);
#endregion
}
private void OnClickOpen()
{
if (string.IsNullOrEmpty(designExcelPath) || !Directory.Exists(designExcelPath))
{
EditorUtility.DisplayDialog("错误", "excel路径不存在请先设置正确路径", "ok");
return;
}
ExportExcelTools.SetDesignExcelPath(designExcelPath);
TutorialConfigWindow window = (TutorialConfigWindow)EditorWindow.GetWindow<TutorialConfigWindow>("新手引导配置窗口");
window.Show();
}
}
}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,61 @@
using System;
using UnityEngine;
using UnityEditor;
namespace BFEditor
{
public class AddAccountWindow : EditorWindow
{
static Action<string> onClickAdd;
string accountJson;
static AddAccountWindow window;
AddAccountWindow()
{
this.titleContent = new GUIContent("添加账号");
}
void OnGUI()
{
GUILayout.Space(10);
GUILayout.BeginHorizontal();
GUILayout.Space(20);
var style = new GUIStyle(GUI.skin.textField);
style.wordWrap = true;
accountJson = GUILayout.TextField(accountJson, style, GUILayout.Width(400), GUILayout.Height(150));
GUILayout.EndHorizontal();
GUILayout.Space(20);
if (GUILayout.Button("导入"))
{
if (onClickAdd != null)
{
onClickAdd(accountJson);
}
}
}
public static void OpenWindow(Action<string> action)
{
var rect = new Rect(Screen.width / 2, Screen.height / 2, 450, 245);
window = (AddAccountWindow)EditorWindow.GetWindowWithRect(typeof(AddAccountWindow), rect);
window.Show();
onClickAdd = action;
}
public static void CloseWindow()
{
if (window != null)
{
window.Close();
}
}
}
}

View File

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

View File

@ -0,0 +1,348 @@
using System.IO;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using BF;
namespace BFEditor
{
[System.Serializable]
public class Role
{
public string id;
public string pwd;
public string serverName;
public string roleName;
}
[System.Serializable]
public class RoleList
{
public List<Role> list = new List<Role>();
public void SortSelf()
{
list.Sort((Role x, Role y) =>
{
return int.Parse(x.id) - int.Parse(y.id);
});
}
}
public class SelectAccontWindow : EditorWindow
{
const string KEY_ROLE_LIST = "ROLE_LIST";
const string KEY_ACCOUNT = "ACCOUNT";
string curId = "";
RoleList roleList = new RoleList();
Vector2 scrollPos;
List<Role> currentRoleList = new List<Role>();
GUIStyle yellowStyle;
GUIStyle greenStyle;
void OnEnable()
{
this.titleContent = new GUIContent("选择账号");
var curAccountStr = PlayerPrefs.GetString(KEY_ACCOUNT);
if (curAccountStr.Contains("|"))
{
curId = curAccountStr.Split('|')[0];
}
var roleListStr = PlayerPrefs.GetString(KEY_ROLE_LIST, "");
if (roleListStr != "")
{
roleList = JsonUtility.FromJson(roleListStr, typeof(RoleList)) as RoleList;
roleList.SortSelf();
}
RefreshCurrentRoleList();
}
void RefreshCurrentRoleList()
{
currentRoleList.Clear();
foreach (var role in roleList.list)
{
if (role.id == curId)
{
currentRoleList.Add(role);
}
}
}
void OnGUI()
{
yellowStyle = new GUIStyle(GUI.skin.label);
yellowStyle.normal.textColor = Color.yellow;
greenStyle = new GUIStyle(GUI.skin.label);
greenStyle.normal.textColor = Color.green;
DrawTop();
DrawCurrentAccount();
DrawScrollView();
}
void DrawTop()
{
GUILayout.Space(10);
GUILayout.BeginHorizontal();
GUILayout.Space(10);
if (GUILayout.Button("清除所有账号", GUILayout.Width(90), GUILayout.Height(25)))
{
OnClickClearAll();
}
if (GUILayout.Button("导入账号", GUILayout.Width(90), GUILayout.Height(25)))
{
OnClickImport();
}
GUILayout.EndHorizontal();
GUILayout.Space(2);
GUILayout.Box("", GUI.skin.box, GUILayout.Width(520), GUILayout.Height(2));
GUILayout.Space(5);
}
void OnClickClearAll()
{
if (EditorUtility.DisplayDialog("提示", "确认清楚所有账号吗", "确认", "取消"))
{
PlayerPrefs.DeleteKey(KEY_ROLE_LIST);
PlayerPrefs.DeleteKey(KEY_ACCOUNT);
roleList.list.Clear();
curId = "";
RefreshCurrentRoleList();
BFLog.Log("清除所有账号成功");
}
}
void OnClickImport()
{
AddAccountWindow.OpenWindow((string json) =>
{
if (string.IsNullOrEmpty(json))
{
EditorUtility.DisplayDialog("提示", "格式不正确", "ok");
return;
}
var addRoleList = JsonUtility.FromJson(json, typeof(RoleList)) as RoleList;
if (addRoleList == null || addRoleList.list.Count == 0)
{
EditorUtility.DisplayDialog("提示", "格式不正确", "ok");
return;
}
EditorUtility.DisplayDialog("提示", "导入成功", "ok");
AddAccountWindow.CloseWindow();
});
}
void DrawCurrentAccount()
{
GUILayout.BeginHorizontal();
GUILayout.Space(10);
GUILayout.Label("当前账号: " + curId, GUILayout.Width(130));
if (GUILayout.Button("导出", GUILayout.Width(50)))
{
OnClickExport(curId);
}
GUILayout.EndHorizontal();
GUILayout.Space(5);
GUILayout.BeginHorizontal();
GUILayout.Space(10);
GUILayout.Label("账号id", yellowStyle, GUILayout.Width(100));
GUILayout.Label("服务器", yellowStyle, GUILayout.Width(90));
GUILayout.Label("角色名", yellowStyle, GUILayout.Width(90));
GUILayout.EndHorizontal();
var count = 0;
foreach (var role in currentRoleList)
{
GUILayout.BeginHorizontal();
GUILayout.Space(10);
if (count == 0)
{
GUILayout.Label(role.id, GUILayout.Width(100));
}
else
{
GUILayout.Label("", GUILayout.Width(100));
}
GUILayout.Label(role.serverName, GUILayout.Width(90));
GUILayout.Label(role.roleName, GUILayout.Width(90));
GUILayout.EndHorizontal();
count++;
}
GUILayout.Space(5);
GUILayout.Box("", GUI.skin.box, GUILayout.Width(520), GUILayout.Height(2));
GUILayout.Space(5);
}
void OnClickExport(string id)
{
if (string.IsNullOrEmpty(id))
{
return;
}
var json = "";
var tempRoleList = new RoleList();
foreach (var role in roleList.list)
{
if (id == role.id)
{
tempRoleList.list.Add(role);
}
}
json = JsonUtility.ToJson(tempRoleList);
var te = new TextEditor();
te.text = json;
te.SelectAll();
te.Copy();
}
void DrawScrollView()
{
GUILayout.BeginHorizontal();
GUILayout.Space(10);
GUILayout.Label("所有账号:");
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Space(10);
GUILayout.Label("账号id", yellowStyle, GUILayout.Width(100));
GUILayout.Label("服务器", yellowStyle, GUILayout.Width(90));
GUILayout.Label("角色名", yellowStyle, GUILayout.Width(90));
GUILayout.EndHorizontal();
scrollPos = GUILayout.BeginScrollView(scrollPos, GUILayout.Width(510));
var removedId = "";
var idRecord = "";
foreach (var role in roleList.list)
{
if (role.id != idRecord && idRecord != "")
{
GUILayout.Space(15);
}
GUILayout.BeginHorizontal();
GUILayout.Space(10);
if (idRecord != role.id)
{
if (curId == role.id)
{
GUILayout.Label(role.id + "(✔)", greenStyle, GUILayout.Width(100));
}
else
{
GUILayout.Label(role.id, GUILayout.Width(100));
}
}
else
{
GUILayout.Label("", GUILayout.Width(100));
}
GUILayout.Label(role.serverName, GUILayout.Width(90));
GUILayout.Label(role.roleName, GUILayout.Width(90));
if (idRecord != role.id)
{
if (curId == role.id)
{
GUILayout.Label("(当前号✔)", greenStyle, GUILayout.Width(55));
}
else
{
if (GUILayout.Button("切换", GUILayout.Width(55)))
{
OnClickSelect(role);
}
}
if (GUILayout.Button("移除", GUILayout.Width(55)))
{
if (OnClickClear(role.id))
{
removedId = role.id;
}
}
if (GUILayout.Button("导出", GUILayout.Width(55)))
{
OnClickExport(role.id);
}
}
idRecord = role.id;
GUILayout.EndHorizontal();
}
GUILayout.EndScrollView();
if (removedId != "")
{
RemoveAccount(removedId);
}
}
void OnClickSelect(Role role)
{
var accountStr = role.id + "|" + role.pwd;
PlayerPrefs.SetString(KEY_ACCOUNT, accountStr);
curId = role.id;
RefreshCurrentRoleList();
BFLog.Log("选择账号:" + role.id);
//对于新版本 还要更新专用数据
PlayerPrefs.SetString("ULogin_Auto_Login_Key", string.Empty);
PlayerPrefs.SetString("DroidHang_Once_ShuMeiID", role.pwd);
PlayerPrefs.Save();
}
bool OnClickClear(string id)
{
if (EditorUtility.DisplayDialog("提示", "确认移除吗", "确认", "取消"))
{
if (id == curId)
{
curId = "";
PlayerPrefs.DeleteKey(KEY_ACCOUNT);
RefreshCurrentRoleList();
}
BFLog.Log("移除账号:" + id);
return true;
}
return false;
}
void RemoveAccount(string id)
{
for (int i = roleList.list.Count - 1; i >= 0; i--)
{
var role = roleList.list[i];
if (role.id == id)
{
roleList.list.RemoveAt(i);
}
}
PlayerPrefs.SetString(KEY_ROLE_LIST, JsonUtility.ToJson(roleList));
}
}
}

View File

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

View File

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

View File

@ -0,0 +1,564 @@
using System;
using System.IO;
using UnityEngine;
using UnityEditor;
using System.Text;
using XLua;
namespace BFEditor
{
public static class ExportExcelTools
{
private const string DESIGN_EXCEL_KEY = "bf_excel_tool_excel_key";
private static bool failFlag = false;
private static bool designSuccFlag = false;
private static string failLog = "";
/// <summary>
/// 提供给neo平台调用的一键导表
/// </summary>
public static void ExportExcelToLua()
{
var designExcelPath = PlayerPrefs.GetString(DESIGN_EXCEL_KEY, "");
if (string.IsNullOrEmpty(designExcelPath) || !Directory.Exists(designExcelPath))
{
Debug.LogError("[bferror]没有配置正确的excel路径 " + designExcelPath);
throw new Exception();
}
var luaPath = Application.dataPath + "/Developer/lua/app/config";
if (!Directory.Exists(luaPath))
{
Debug.LogError("[bferror]没有找到lua路径 " + luaPath);
throw new Exception();
}
var relitivePath = Application.dataPath;
relitivePath = relitivePath.Remove(relitivePath.Length - 6, 6);
var pythonToolPath = relitivePath + "Tools/tranexcel";
if (!Directory.Exists(pythonToolPath))
{
Debug.LogError("[bferror]没有找到pythonToolPath " + pythonToolPath);
throw new Exception();
}
var success = true;
var localizationPythonToolPath = relitivePath + "Tools/localization";
BFEditorUtils.RunCommond("python", "tran.py " + designExcelPath + "/ " + luaPath + "/", pythonToolPath, (str) =>
{
}, (str) =>
{
Debug.LogError("[bferror] " + str);
success = false;
});
BFEditorUtils.RunCommond("python", "exceltolua.py " + designExcelPath + " " + luaPath + " " + "1", localizationPythonToolPath, (str) =>
{
}, (str) =>
{
Debug.LogError("[bferror] " + str);
success = false;
});
SpecialProcessSkill(true, (isSuccess) =>
{
success = isSuccess;
if (!isSuccess)
{
Debug.LogError("[bferror] " + "技能表转换失败");
}
});
var sb = ExportExcelTools.CheckLuaConfig(true);
if (sb.ToString().Length > 0)
{
success = false;
Debug.LogError("[bferror] 导表规范检查异常:" + sb.ToString());
}
if (!success)
{
throw new Exception();
}
}
public static StringBuilder CheckLuaConfig(bool isDeveloper)
{
//加载lua配置测试
LuaEnv env = new LuaEnv();
env.AddLoader((ref string scriptPath) =>
{
var text = LoadGameLuaScriptText(scriptPath, isDeveloper);
return text;
});
// 检查多语言配置
String luaTextConfigPath = null;
String suffix = "";
if(isDeveloper)
{
suffix = "*.lua";
luaTextConfigPath = "Assets/developer/lua/app/config/strings";
}
else
{
suffix = "*.lua.bytes";
luaTextConfigPath = "Assets/lua/app/config/strings";
}
var luaTextDirInfo = new DirectoryInfo(luaTextConfigPath);
var luaTextFileInfos = luaTextDirInfo.GetFiles(suffix, SearchOption.AllDirectories);
var sb = new StringBuilder();
foreach (var file in luaTextFileInfos)
{
var name = file.FullName.Replace("\\", "/");
var index = name.IndexOf(luaTextConfigPath);
name = name.Substring(index + luaTextConfigPath.Length + 1).Replace(".lua", "").Replace(".bytes", "");
try
{
env.DoString("require " + "'app/config/strings/" + name + "'");
}
catch (Exception e)
{
sb.Append(name + " 配置异常\n" + e.Message);
}
}
// 检查monster
String luaConfigPath = null;
if(isDeveloper)
{
luaConfigPath = "Assets/developer/lua/app/config";
}
else
{
luaConfigPath = "Assets/lua/app/config";
}
var configDirInfo = new DirectoryInfo(luaConfigPath);
var configFileInfos = configDirInfo.GetFiles(suffix, SearchOption.TopDirectoryOnly);
string monsterConfigListLua = "{";
foreach (var file in configFileInfos)
{
var fileName = file.Name.ToLower();
if(fileName.Contains("monster_"))
{
monsterConfigListLua += "'" + fileName.Replace(".lua", "").Replace(".bytes", "") + "',";
}
}
monsterConfigListLua += "}";
var luaScriptString = "local ids = {}\n local MONSTER_LIST = " + monsterConfigListLua + "\n";
luaScriptString += @"local str = {}
for i, name in ipairs(MONSTER_LIST) do
if name ~= 'monster_base' and name ~= 'monster_position' and name ~= 'monster_position_base' then
local data = require('app/config/' .. name).data
for k, v in pairs(data) do
if ids[k] then
table.insert(str, name .. '和' .. ids[k] .. 'mosnter id:' .. k)
end
ids[k] = name
end
end
end
if #str > 0 then
return table.concat(str, '\n');
end
return ''";
var resultStr = env.DoString(luaScriptString);
if (resultStr.Length > 0)
{
foreach(var strObj in resultStr)
{
var str = Convert.ToString(strObj);
if(!String.IsNullOrEmpty(str))
{
sb.Append(str + "\n");
}
}
}
// 检查怪物的坐标信息
var luaScriptString2 = @"local MONSTER_POSITION_KEY = { 'monster_1','monster_2','monster_3','monster_4','monster_5','monster_6','monster_7','monster_8','monster_9','monster_10','monster_11','monster_12'}
local list = {'enemy_id_1', 'enemy_id_2', 'enemy_id_3'}
local list2 = {'enemy_position_1', 'enemy_position_2', 'enemy_position_3'}
local positionConf = require('app/config/monster_position_base').data
local monsterPositionConf = require('app/config/monster_position').data
local stage = require('app/config/story_stage').data
local str = {}
for k, v in pairs(stage) do
for k2, v2 in ipairs(list) do
if v[v2] then
local monsterPosition = v[list2[k2]]
for i, monsterId in ipairs(v[v2]) do
local monsterPositionInfo = monsterPositionConf[monsterPosition]
local positionInfo = positionConf[monsterPositionInfo[MONSTER_POSITION_KEY[i]]]
if positionInfo == nil then
table.insert(str, 'stage表的id为' .. k .. '' .. k2 .. '')
end
end
end
end
end
local stage2 = require('app/config/adventure_stage').data
for k, v in pairs(stage2) do
for k2, v2 in ipairs(list) do
if v[v2] then
local monsterPosition = v[list2[k2]]
for i, monsterId in ipairs(v[v2]) do
local monsterPositionInfo = monsterPositionConf[monsterPosition]
local positionInfo = positionConf[monsterPositionInfo[MONSTER_POSITION_KEY[i]]]
if positionInfo == nil then
table.insert(str, 'adventure_stage表的id为' .. k .. '' .. k2 .. '')
end
end
end
end
end
if #str > 0 then
return table.concat(str, '\n');
end
return ''";
var resultStr2 = env.DoString(luaScriptString2);
if (resultStr2.Length > 0)
{
foreach(var strObj in resultStr2)
{
var str = Convert.ToString(strObj);
if(!String.IsNullOrEmpty(str))
{
sb.Append(str + "\n");
}
}
}
env.Dispose();
return sb;
}
public static bool FastExportExcelToLua(bool isDeveloper, string designExcelPath)
{
failFlag = false;
designSuccFlag = true;
failLog = "";
string relitivePath = Application.dataPath;
relitivePath = relitivePath.Remove(relitivePath.Length - 6, 6);
string luaPath;
string pythonToolPath;
if(isDeveloper)
{
luaPath = Application.dataPath + "/Developer/lua/app/config";
pythonToolPath = relitivePath + "Tools/tranexcel";
}
else
{
luaPath = Application.dataPath + "/lua/app/config";
pythonToolPath = relitivePath + "Tools/tranexcel2";
}
string localizationPythonToolPath = relitivePath + "Tools/localization";
// 强制使用新的导表工具 python3.4以上
// pythonToolPath = relitivePath + "Tools/tranexcel_new";
// localizationPythonToolPath = relitivePath + "Tools/localization_new";
if (string.IsNullOrEmpty(pythonToolPath) || !Directory.Exists(pythonToolPath))
{
Debug.LogError(string.Format("python工具不存在请检查 {0}", pythonToolPath));
return false;
}
if (string.IsNullOrEmpty(luaPath) || !Directory.Exists(luaPath))
{
Debug.LogWarning(string.Format("lua config path not exist!, auto create {0}", luaPath));
Directory.CreateDirectory(luaPath);
}
string isDevStr = isDeveloper?"1":"0";
// BFEditorUtils.RunCommond("python", "tran.py " + designExcelPath + "/ " + luaPath + "/" + " " + isDevStr, pythonToolPath, DesignOnOutput, OnError);
BFEditorUtils.RunCommond("python", "tran.py " + designExcelPath + "/ " + luaPath + "/", pythonToolPath, DesignOnOutput, OnError);
BFEditorUtils.RunCommond("python", "exceltolua.py " + designExcelPath + " " + luaPath + " " + isDevStr, localizationPythonToolPath, ProceduralOnOutput, OnError);
// ExportExcelTools.SpecialProcessSkill(isDeveloper, SkillSpecialOnOutput);
// ExportExcelTools.dealMonsterConfig(isDeveloper);
// var sb = ExportExcelTools.CheckLuaConfig(isDeveloper);
// if (sb.ToString().Length > 0)
// {
// failFlag = true;
// designSuccFlag = false;
// Debug.Log("导表规范检查异常!!!");
// Debug.Log(sb.ToString());
// EditorUtility.DisplayDialog("导入失败配置", sb.ToString(), "ok");
// }
// else
// {
// Debug.Log("配置规范检查通过.");
// }
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
if(designSuccFlag && !failFlag)
{
return true;
}
return false;
}
private static void DesignOnOutput(string outputmsg)
{
if (outputmsg.Contains("转换成功"))
{
designSuccFlag = true;
}
if (outputmsg.Contains("转换失败"))
{
failFlag = true;
failLog = failLog + outputmsg;
}
Debug.Log(outputmsg);
}
private static void OnError(string errorMsg)
{
failFlag = true;
failLog = errorMsg;
Debug.Log(errorMsg);
}
private static void ProceduralOnOutput(string outputmsg)
{
if (outputmsg.Contains("转换失败"))
{
failFlag = true;
failLog = failLog + outputmsg;
}
Debug.Log(outputmsg);
}
private static void SkillSpecialOnOutput(bool isSuccess)
{
}
private static byte[] LoadGameLuaScriptText(string scriptPath, bool isDeveloper)
{
byte[] result = null;
String luaPath = null;
// 开发路径加载
if (isDeveloper)
{
luaPath = Application.dataPath + "/Developer/lua/" + scriptPath + ".lua";
}
else
{
luaPath = Application.dataPath + "/lua/" + scriptPath + ".lua.bytes";
}
if (File.Exists(luaPath))
{
result = File.ReadAllBytes(luaPath);
}
else
{
Debug.LogError("lua file is missing : " + luaPath);
}
return result;
}
/// <summary>
/// 特殊处理技能多语言表 (加入程序导表和neo平台调用)
/// </summary>
/// <param name="readLuaPath"></param>
public static void SpecialProcessSkill(bool isDeveloper, Action<bool> callback)
{
var result = ProcessSkillConfig(isDeveloper);
if (result)
{
if (callback != null)
{
callback(result);
}
}
}
public static bool ProcessSkillConfig(bool isDeveloper)
{
LuaEnv luaEnv = new LuaEnv();
luaEnv.AddLoader(Loader);
var rootPath = "";
if (isDeveloper)
{
rootPath = Application.dataPath + "/Developer/lua/app/config/strings";
}
else
{
rootPath = Application.dataPath + "/lua/app/config/strings";
}
if (!Directory.Exists(rootPath))
{
Debug.LogError("没有找到配置表strings 文件夹");
throw new Exception();
}
luaEnv.Global.Set<string, bool>("isDeveloper", isDeveloper);
luaEnv.DoString(@"require 'Editor/BFOthersTools/ExportExcelTools/processSkillConfigTools.lua'");
var dirInfo = new DirectoryInfo(rootPath);
var reWrite = luaEnv.Global.Get<LuaFunction>("reWrite");
foreach (var languagePath in dirInfo.GetDirectories())
{
var skillPath = "";
if (isDeveloper)
{
skillPath = languagePath.FullName + "/skill.lua";
}
else
{
skillPath = languagePath.FullName + "/skill.lua.bytes";
}
if (!File.Exists(skillPath))
{
Debug.LogWarning("skill表不存在 " + skillPath);
continue;
}
var str = File.ReadAllText(skillPath);
var result = reWrite.Call(languagePath.Name, str);
// Debug.Log(result[0].ToString());
if (result[0].ToString() != "Error")
{
//重新写入文件
File.WriteAllText(skillPath,result[0].ToString());
}
else
{
return false;
}
}
return true;
}
private static byte[] Loader(ref string path)
{
path = Application.dataPath + "/" + path;
if (!File.Exists(path))
{
Debug.LogWarning("无文件lua " + path);
return null;
}
return File.ReadAllBytes(path);
}
public static string GetDesignExcelPath()
{
return PlayerPrefs.GetString(DESIGN_EXCEL_KEY, "");
}
public static void SetDesignExcelPath(string path)
{
PlayerPrefs.SetString(DESIGN_EXCEL_KEY, path);
}
public static void dealMonsterConfig(bool isDeveloper)
{
//加载lua配置测试
LuaEnv env = new LuaEnv();
env.AddLoader((ref string scriptPath) =>
{
scriptPath = "app/config/" + scriptPath;
var text = LoadGameLuaScriptText(scriptPath, isDeveloper);
return text;
});
String suffix = "";
String luaConfigPath = null;
if(isDeveloper)
{
luaConfigPath = Application.dataPath + "/Developer/lua/app/config";
suffix = "*.lua";
}
else
{
luaConfigPath = Application.dataPath + "/lua/app/config";
suffix = "*.lua.bytes";
}
var configDirInfo = new DirectoryInfo(luaConfigPath);
var configFileInfos = configDirInfo.GetFiles(suffix, SearchOption.TopDirectoryOnly);
string monsterList = "local monsterList = {";
foreach (var file in configFileInfos)
{
var name = file.FullName.Replace("\\", "/");
var index = name.IndexOf(luaConfigPath);
name = name.Substring(index + luaConfigPath.Length + 1).Replace(".lua", "").Replace(".bytes", "");
if(name.Contains("monster_") && !name.Contains("position") && !name.Contains("monster_base"))
{
monsterList += "'" + name + "',";
}
}
monsterList += "}";
string luaScriptString = @"function tableConfigToString(value, tab, tabCount)
tab = tab or ' '
tabCount = tabCount and tabCount or 1
local t = {}
for k, value in pairs(value) do
local str = string.rep(tab, tabCount)
if type(k) == 'number' then
str = str .. '[' .. k .. ']' .. '='
elseif type(k) == 'string' then
str = str .. '[\'' .. k .. '\']' .. '='
end
if type(value) == 'number' then
str = str .. value
elseif type(value) == 'string' then
str = str .. '\'' .. value .. '\''
elseif type(value) == 'table' then
str = str .. '{\n' .. tableConfigToString(value, tab, tabCount + 1) .. '\n' .. string.rep(tab, tabCount) .. '}'
end
table.insert(t, str)
end
return table.concat(t, ',\n')
end
function configToFile(targetTable, tableLen, targetPath)
local str = 'local monster = {\n' .. tableConfigToString(targetTable) .. '\n}\nlcoal config={\ndata=monster,count=' .. tableLen .. '\n}\nreturn config'
local luaFile, msg = io.open(targetPath, 'w')
if not luaFile then
return
end
luaFile:write(str)
io.close(luaFile)
end
function dealMonster(targetPath, monsterList)
local monsterBase = require( 'monster_base')
local baseData = monsterBase.data
local monsterFullData = {}
local count = 0
local function handleMonsterGrow(name)
local monsterGrowConfig = require(name)
local growData = monsterGrowConfig.data
for k, v in pairs(growData) do
monsterFullData[k] = v
local data = baseData[v.monster_baseid]
if data then
monsterFullData[k].icon = data.icon
monsterFullData[k].model_id = data.model_id
else
Logger.logHighlight('not data monster_baseid = ' .. v.monster_baseid)
end
count = count + 1
end
end
for _, name in ipairs(monsterList) do
handleMonsterGrow(name)
end
configToFile(monsterFullData, count, targetPath)
end
" + monsterList + @"
dealMonster('" + luaConfigPath + "/monster" + suffix.Substring(1) + "', monsterList)";
env.DoString(luaScriptString);
}
}
}

View File

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

View File

@ -0,0 +1,84 @@
using System;
using System.IO;
using UnityEditor;
using UnityEngine;
using XLua;
using Debug = UnityEngine.Debug;
namespace BFEditor
{
public class ExportExcelWindow : EditorWindow
{
private string designExcelPath = "";
private bool designSuccFlag = false;
private bool skillSpecialSuccFlag = false;
private bool isDev = false;
public ExportExcelWindow()
{
this.titleContent = new GUIContent("导表工具");
}
void Awake()
{
var luaPath = Path.Combine(Application.dataPath, "Developer/lua/main.lua").Replace("\\", "/");
isDev = File.Exists(luaPath);
}
private void OnEnable()
{
designExcelPath = ExportExcelTools.GetDesignExcelPath();
}
private void OnGUI()
{
#region Excel路径
GUILayout.Space(18);
GUILayout.BeginHorizontal();
GUILayout.Label("选择excel路径");
GUILayout.TextField(designExcelPath, GUILayout.Width(300));
if (GUILayout.Button("选择", GUILayout.Width(80)))
{
string openPath = EditorUtility.OpenFolderPanel("select excel path", designExcelPath, "");
if (openPath.CompareTo("") != 0){
designExcelPath = openPath;
}
}
GUILayout.EndHorizontal();
GUILayout.Space(10);
#endregion
#region lua
GUILayout.BeginHorizontal();
GUILayout.Space(150);
if (GUILayout.Button("一键导表", GUILayout.Width(200), GUILayout.Height(40)))
{
bool succ = OnClickExport();
if (succ)
{
Close();
EditorUtility.DisplayDialog("成功", "导出成功!", "ok");
}
else
{
Close();
EditorUtility.DisplayDialog("失败", "导出失败!", "ok");
}
}
GUILayout.EndHorizontal();
GUILayout.Space(30);
#endregion
}
private bool OnClickExport()
{
if (string.IsNullOrEmpty(designExcelPath) || !Directory.Exists(designExcelPath))
{
EditorUtility.DisplayDialog("错误", "excel路径不存在请先设置正确路径", "ok");
return false;
}
ExportExcelTools.SetDesignExcelPath(designExcelPath);
return ExportExcelTools.FastExportExcelToLua(isDev, designExcelPath);
}
}
}

View File

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

View File

@ -0,0 +1,132 @@
using System;
using System.IO;
using UnityEditor;
using UnityEngine;
using System.Collections.Generic;
namespace BFEditor
{
public class ToggleCaseWindow : EditorWindow
{
private string folderPath = "";
//需要忽略的文件后缀
private Dictionary<string, bool> ignoreResSuffix = new Dictionary<string, bool>() {
{ ".meta", true },
{ ".ds_store", true },
};
public ToggleCaseWindow()
{
this.titleContent = new GUIContent("大小写转换");
}
void Awake()
{
}
private void OnEnable()
{
}
private void OnGUI()
{
#region Excel路径
GUILayout.Space(18);
GUILayout.BeginHorizontal();
GUILayout.Label("需要转换的文件夹路径");
GUILayout.TextField(folderPath, GUILayout.Width(280));
if (GUILayout.Button("选择", GUILayout.Width(80)))
{
string openPath = EditorUtility.OpenFolderPanel("select excel path", "", "");
if (openPath.CompareTo("") != 0){
folderPath = openPath.Replace("\\", "/");
}
}
GUILayout.EndHorizontal();
GUILayout.Space(10);
#endregion
#region lua
GUILayout.BeginHorizontal();
GUILayout.Space(150);
if (GUILayout.Button("一键转小写", GUILayout.Width(200), GUILayout.Height(40)))
{
bool succ = OnClickExport();
if (succ)
{
Close();
EditorUtility.DisplayDialog("成功", "转换成功!", "ok");
}
else
{
Close();
EditorUtility.DisplayDialog("失败", "转换失败!", "ok");
}
}
GUILayout.EndHorizontal();
GUILayout.Space(30);
#endregion
}
private bool CheckIgnoreOrHidden(FileInfo fi)
{
if (((fi.Attributes & FileAttributes.Hidden) > 0) || ((fi.Attributes & FileAttributes.System) > 0))
{
return true;
}
if (ignoreResSuffix.ContainsKey(fi.Extension.ToLower()))
{
return true;
}
return false;
}
private bool OnClickExport()
{
if (string.IsNullOrEmpty(folderPath) || !Directory.Exists(folderPath))
{
EditorUtility.DisplayDialog("错误", "路径不存在,请先设置正确路径", "ok");
return false;
}
var outFolderPath = folderPath + "_out";
if (!Directory.Exists(outFolderPath))
{
Directory.CreateDirectory(outFolderPath);
}
var dirInfo = new DirectoryInfo(folderPath);
var dirs = dirInfo.GetDirectories();
foreach (var subDirInfo in dirs)
{
var subDirPath = subDirInfo.FullName.Replace("\\", "/");;
var outSubDirPath = subDirPath.Replace(folderPath, outFolderPath);
if (!Directory.Exists(outSubDirPath))
{
Directory.CreateDirectory(outSubDirPath);
}
}
var files = dirInfo.GetFiles("*.*", SearchOption.AllDirectories);
var assetFiles = new List<string>();
for (var i = 0; i < files.Length; ++i)
{
var fileInfo = files[i];
if (CheckIgnoreOrHidden(fileInfo))
{
continue;
}
var relativePath = fileInfo.FullName.Substring(folderPath.Length + 1).ToLower();
var outFilePath = Path.Combine(outFolderPath, relativePath);
File.Copy(fileInfo.FullName, outFilePath, true);
}
return true;
}
}
}

View File

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

View File

@ -0,0 +1,524 @@
local skillConfigPath = ""
local subSkillConfigPath = ""
local buffConfigPath = ""
if isDeveloper then
skillConfigPath = 'Developer/lua/app/config/skill.lua'
subSkillConfigPath = 'Developer/lua/app/config/skillSub.lua'
buffConfigPath = 'Developer/lua/app/config/buff.lua'
else
skillConfigPath = 'Lua/app/config/skill.lua.bytes'
subSkillConfigPath = 'Lua/app/config/skillSub.lua.bytes'
buffConfigPath = 'Lua/app/config/buff.lua.bytes'
end
local pairsName = {
[1] = 'skillName',
[2] = 'desc',
[3] = 'desc2'
}
local skillConfig = require(skillConfigPath).data
local subSkillConfig = require(subSkillConfigPath).data
local buffConfig = require(buffConfigPath)
function getBuffIsPercent(type)
local data = buffConfig.keys.name[type]
if not data then
CS.UnityEngine.Debug.LogError("buff表中不存在类型" .. tostring(type))
return false
end
return data.isPercent and data.isPercent == 2 or false
end
function reWrite(languageStr, str)
--配置表无序 只能按匹配
local skillDescConfigPath = ""
if isDeveloper then
skillDescConfigPath = 'Developer/lua/app/config/strings/' .. languageStr .. '/skill.lua'
else
skillDescConfigPath = 'Lua/app/config/strings/' .. languageStr .. '/skill.lua.bytes'
end
local skillDescConfig = require(skillDescConfigPath).data
local caCheTable = {}
local caCheRatioTable = {}
for skillid, data in pairs(skillDescConfig) do
for _, field in ipairs(pairsName) do
if data[field] then
--匹配规则并计算出数值记录下来
local matchResult = string.match(data[field], "{(.+)_(.)}")
if matchResult then
--计算并排序
local mainSkill = skillConfig[skillid]
if not mainSkill then
-- CS.UnityEngine.Debug.LogError("skill表中不存在技能" .. tostring(skillid))
return "Error"
end
local skillCacheData = {}
local skillRatioCacheData = {}
----------主技能技能效果 整体概率
local mainSkillEffects = mainSkill.effect
local mainSkillHasChange = false
local subSkillHasChange = false
if not mainSkillEffects then
-- CS.UnityEngine.Debug.LogWarning("主技能没有技能效果" .. tostring(skillid))
else
for _, mainSkillEffect in ipairs(mainSkillEffects) do
if mainSkillEffect.type ~= 'changeAtk' and mainSkillEffect.type ~= 'changeSkill' then
--CS.UnityEngine.Debug.Log("insert main SkillEffect")
table.insert(skillCacheData,{
effect = {
type = mainSkillEffect.type,
v = mainSkillEffect.num,
p = mainSkillEffect.ratio,
r = mainSkillEffect.round,
},
})
else
mainSkillHasChange = true
end
end
end
table.insert(skillRatioCacheData, mainSkill.skillRatio or 0)
-----------子技能技能效果 整体概率
local subSkills = nil
if mainSkill.skillSub then
subSkills = mainSkill.skillSub
for _, subSkillId in ipairs(subSkills) do
local subSkill = subSkillConfig[subSkillId]
if not subSkill then
-- CS.UnityEngine.Debug.LogError("subskill表中不存在技能" .. tostring(subSkillId))
return "Error"
end
--子技能技能效果 整体概率
local subSkillEffects = subSkill.effect
if not subSkillEffects then
-- CS.UnityEngine.Debug.LogWarning("子技能没有技能效果" .. tostring(skillid))
else
for _, subSkillEffect in ipairs(subSkillEffects) do
if subSkillEffect.type ~= 'changeAtk' and subSkillEffect.type ~= 'changeSkill' then
--CS.UnityEngine.Debug.Log("insert sub SkillEffect")
table.insert(skillCacheData,{
effect = {
type = subSkillEffect.type,
v = subSkillEffect.num,
p = subSkillEffect.ratio,
r = subSkillEffect.round,
},
})
else
subSkillHasChange = true
end
end
end
table.insert(skillRatioCacheData, subSkill.skillRatio or 0)
end
end
local mainChangeAtkSkill = nil
local mainChangeSkillSkill = nil
local changeId = nil
local cacheChange = nil
if mainSkillHasChange then
---------主技能change技能
for _, mainSkillEffect in ipairs(mainSkillEffects) do
changeId = nil
if mainSkillEffect.type == 'changeAtk' then
changeId = mainSkillEffect.num
mainChangeAtkSkill = skillConfig[changeId]
cacheChange = mainChangeAtkSkill
if not mainChangeAtkSkill then
-- CS.UnityEngine.Debug.LogError("skill表中不存在技能" .. tostring(changeId))
return "Error"
end
end
if mainSkillEffect.type == 'changeSkill' then
changeId = mainSkillEffect.num
mainChangeSkillSkill = skillConfig[changeId]
cacheChange = mainChangeSkillSkill
if not mainChangeSkillSkill then
-- CS.UnityEngine.Debug.LogError("skill表中不存在技能" .. tostring(changeId))
return "Error"
end
end
if changeId then
local changeEffects = cacheChange.effect
if not changeEffects then
-- CS.UnityEngine.Debug.LogWarning("主机能chanage技能没有技能效果" .. tostring(skillid))
else
for _, changeEffect in ipairs(changeEffects) do
table.insert(skillCacheData,{
effect = {
type = changeEffect.type,
v = changeEffect.num,
p = changeEffect.ratio,
r = changeEffect.round,
},
})
end
end
table.insert(skillRatioCacheData, cacheChange.skillRatio or 0)
---------主技能change技能的子技能
local changeFirstSubIds = cacheChange.skillSub
if changeFirstSubIds then
for _, changeFirstSubId in ipairs(changeFirstSubIds) do
local changeFirstSub = subSkillConfig[changeFirstSubId]
if not changeFirstSub then
-- CS.UnityEngine.Debug.LogError("subSkill表中不存在技能" .. tostring(changeFirstSubId))
return "Error"
end
local changeEffects = changeFirstSub.effect
if not changeEffects then
-- CS.UnityEngine.Debug.LogWarning("主机能chanage子技能没有技能效果" .. tostring(skillid))
else
for _, changeEffect in ipairs(changeEffects) do
table.insert(skillCacheData,{
effect = {
type = changeEffect.type,
v = changeEffect.num,
p = changeEffect.ratio,
r = changeEffect.round,
},
})
end
end
table.insert(skillRatioCacheData, changeFirstSub.skillRatio or 0)
end
end
end
end
else
------------主技能isAct技能
if mainSkill.isAct then
local mainIsActSkill = skillConfig[mainSkill.isAct]
if not mainIsActSkill then
-- CS.UnityEngine.Debug.LogError("skill表中不存在技能" .. tostring(mainSkill.isAct))
return "Error"
end
local changeEffects = mainIsActSkill.effect
if not changeEffects then
-- CS.UnityEngine.Debug.LogWarning("主技能isAct技能没有技能效果" .. tostring(skillid))
else
for _, changeEffect in ipairs(changeEffects) do
table.insert(skillCacheData,{
effect = {
type = changeEffect.type,
v = changeEffect.num,
p = changeEffect.ratio,
r = changeEffect.round,
},
})
end
end
table.insert(skillRatioCacheData, mainIsActSkill.skillRatio or 0)
-------主技能isAct技能的子技能
local mainIsActSkillSubSkillIds = mainIsActSkill.skillSub
if mainIsActSkillSubSkillIds then
for _, mainIsActSkillSubSkillId in ipairs(mainIsActSkillSubSkillIds) do
local mainIsActSkillSubSkill = subSkillConfig[mainIsActSkillSubSkillId]
if not mainIsActSkillSubSkill then
-- CS.UnityEngine.Debug.LogError("subskill表中不存在技能" .. tostring(mainIsActSkillSubSkillId))
return "Error"
end
local changeEffects = mainIsActSkillSubSkill.effect
if not changeEffects then
-- CS.UnityEngine.Debug.LogWarning("主技能isAct技能的子技能没有技能效果" .. tostring(skillid))
else
for _, changeEffect in ipairs(changeEffects) do
table.insert(skillCacheData,{
effect = {
type = changeEffect.type,
v = changeEffect.num,
p = changeEffect.ratio,
r = changeEffect.round,
},
})
end
end
table.insert(skillRatioCacheData, mainIsActSkillSubSkill.skillRatio)
end
end
end
end
----------------子技能相关
local subSkillChangeAtkSkill = nil
local subSkillChangeSkillSkill = nil
local changeId = nil
local cacheChange = nil
if subSkills then
for _, subSkillId in ipairs(subSkills) do
local subSkill = subSkillConfig[subSkillId]
if not subSkill then
-- CS.UnityEngine.Debug.LogError("subskill表中不存在技能" .. tostring(subSkillId))
return "Error"
end
---------子技能isAct技能
if subSkill.isAct then
local subSkillActSkill = skillConfig[subSkill.isAct]
if not subSkillActSkill then
-- CS.UnityEngine.Debug.LogError("skill表中不存在技能" .. tostring(subSkillId))
return "Error"
end
local changeEffects = subSkillActSkill.effect
if not changeEffects then
-- CS.UnityEngine.Debug.LogWarning("子技能isAct技能没有技能效果" .. tostring(skillid))
else
for _, changeEffect in ipairs(changeEffects) do
table.insert(skillCacheData,{
effect = {
type = changeEffect.type,
v = changeEffect.num,
p = changeEffect.ratio,
r = changeEffect.round,
},
})
end
end
table.insert(skillRatioCacheData, subSkillActSkill.skillRatio or 0)
------------子技能isAct技能的子技能
local subIsActSkillSubSkillIds = subSkillActSkill.skillsub
if subIsActSkillSubSkillIds then
for _, subIsActSkillSubSkillId in ipairs(subIsActSkillSubSkillIds) do
local subIsActSkillSubSkill = subSkillConfig[subIsActSkillSubSkillId]
if not subIsActSkillSubSkill then
-- CS.UnityEngine.Debug.LogError("subskill表中不存在技能" .. tostring(subIsActSkillSubSkillId))
return "Error"
end
local changeEffects = subIsActSkillSubSkill.effect
if not changeEffects then
-- CS.UnityEngine.Debug.LogWarning("子技能isAct技能的子技能没有技能效果" .. tostring(skillid))
else
for _, changeEffect in ipairs(changeEffects) do
table.insert(skillCacheData,{
effect = {
type = changeEffect.type,
v = changeEffect.num,
p = changeEffect.ratio,
r = changeEffect.round,
},
})
end
end
table.insert(skillRatioCacheData, subIsActSkillSubSkill.skillRatio or 0)
end
end
else
local subSkillEffects = subSkill.effect
if not subSkillEffects then
-- CS.UnityEngine.Debug.LogWarning("子技能没有技能效果" .. tostring(skillid))
else
----------子技能change技能
for _, subSkillEffect in ipairs(subSkillEffects) do
changeId = nil
if subSkillEffect.type == 'changeAtk' then
changeId = subSkillEffect.num
subSkillChangeAtkSkill = skillConfig[changeId]
cacheChange = subSkillChangeAtkSkill
if not subSkillChangeAtkSkill then
-- CS.UnityEngine.Debug.LogError("skill表中不存在技能" .. tostring(changeId))
return "Error"
end
end
if subSkillEffect.type == 'changeSkill' then
changeId = subSkillEffect.num
subSkillChangeSkillSkill = skillConfig[changeId]
cacheChange = subSkillChangeSkillSkill
if not subSkillChangeSkillSkill then
-- CS.UnityEngine.Debug.LogError("skill表中不存在技能" .. tostring(changeId))
return "Error"
end
end
if changeId then
local changeEffects = cacheChange.effect
if not changeEffects then
-- CS.UnityEngine.Debug.LogWarning("子技能change技能没有技能效果" .. tostring(skillid))
else
for _, changeEffect in ipairs(changeEffects) do
table.insert(skillCacheData,{
effect = {
type = changeEffect.type,
v = changeEffect.num,
p = changeEffect.ratio,
r = changeEffect.round,
},
})
end
end
table.insert(skillRatioCacheData, cacheChange.skillRatio or 0)
---------子技能change技能的子技能
local subChangeFirstSubIds = cacheChange.skillSub
if subChangeFirstSubIds then
for _, subChangeFirstSubId in ipairs(subChangeFirstSubIds) do
local subChangeFirstSub = subSkillConfig[subChangeFirstSubId]
if not subChangeFirstSub then
-- CS.UnityEngine.Debug.LogError("subSkill表中不存在技能" .. tostring(subChangeFirstSubId))
return "Error"
end
local changeEffects = subChangeFirstSub.effect
if not changeEffects then
-- CS.UnityEngine.Debug.LogWarning("子技能change技能的子技能没有技能效果" .. tostring(skillid))
else
for _, changeEffect in ipairs(changeEffects) do
table.insert(skillCacheData,{
effect = {
type = changeEffect.type,
v = changeEffect.num,
p = changeEffect.ratio,
r = changeEffect.round,
},
})
end
end
table.insert(skillRatioCacheData, subChangeFirstSub.skillRatio or 0)
end
end
end
end
end
end
end
end
--CS.UnityEngine.Debug.Log(tostring(skillid) .. "写入数据" .. #skillCacheData)
caCheTable[skillid] = skillCacheData
caCheRatioTable[skillid] = skillRatioCacheData
break
end
end
end
end
--匹配每个id然后替换内部字符串(保证原有顺序)
local result = string.gsub(str, "%[(%d+)%]=(%b{})", function (id, infoStr)
id = tonumber(id)
--CS.UnityEngine.Debug.LogError("id" .. tostring(id))
local returnStr = string.gsub(string.sub(infoStr, 2), "(%b{})",function(str)
local _flag = string.sub(str,2,-2)
local _index = string.find(_flag, '_')
local flag = string.sub(_flag,1 ,_index - 1)
local readFlag = string.sub(_flag,_index + 1)
local index = tonumber(flag)
if not caCheTable[id] then
CS.UnityEngine.Debug.LogError(tostring(id) .. "没有读取到数据 请对比多语言表和skill表" .. caCheTable[id])
return ""
end
--CS.UnityEngine.Debug.Log(flag .. " " .. readFlag)
if readFlag == 'v' then
if not index then
CS.UnityEngine.Debug.LogError(tostring(id) .. "{x_x}格式配置错误" .. " v")
return ""
end
if not caCheTable[id][index] then
CS.UnityEngine.Debug.LogError(tostring(id) .. "读取参数错误 或者 配置错误" .. "读取配置长度:" .. #caCheTable[id] .. "index: " .. index .. " v")
return ""
end
local isPercent = getBuffIsPercent(caCheTable[id][index].effect.type)
local num = math.abs(caCheTable[id][index].effect.v)
if isPercent then
num = math.floor(num / 10)
end
return tostring(num)
elseif readFlag == 'p' then
if string.sub(flag,1, 1) == "s" then
index = tonumber(string.sub(flag, 2))
if not index then
CS.UnityEngine.Debug.LogError(tostring(id) .. "{x_x}格式配置错误" .. " s")
return ""
end
if not caCheRatioTable[id][index] then
CS.UnityEngine.Debug.LogError(tostring(id) .. "读取参数错误 或者 配置错误" .. "读取配置长度:" .. #caCheRatioTable[id] .. "index: " .. index .. " p")
return ""
end
if not caCheRatioTable[id][index] then
CS.UnityEngine.Debug.LogError(tostring(id) .. "技能没有整体概率")
end
return math.floor((caCheRatioTable[id][index] or 0) / 10)
else
if not index then
CS.UnityEngine.Debug.LogError(tostring(id) .. "{x_x}格式配置错误" .. " s")
return ""
end
if not caCheTable[id][index] then
CS.UnityEngine.Debug.LogError(tostring(id) .. "读取参数错误 或者 配置错误" .. "读取配置长度:" .. #caCheTable[id] .. "index: " .. index .. " p")
return ""
end
return math.floor(caCheTable[id][index].effect.p / 10)
end
elseif readFlag == 'r' then
if not index then
CS.UnityEngine.Debug.LogError(tostring(id) .. "{x_x}格式配置错误" .. " r")
return ""
end
if not caCheTable[id][index] then
CS.UnityEngine.Debug.LogError(tostring(id) .. "读取参数错误 或者 配置错误" .. "读取配置长度:" .. #caCheTable[id] .. "index: " .. index .. " r")
return ""
end
return caCheTable[id][index].effect.r
else
CS.UnityEngine.Debug.LogError(tostring(id) .. "{x_x}格式配置错误" .. "后缀不是vpr")
return ""
end
end)
return "[" .. tostring(id) .. "]={" .. returnStr
end)
return result
end

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 61cd7619fa40641459984f57ae4a5c9a
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 11500000, guid: 3b8b241bab4a4ac9a22fcce9c64f1242, type: 3}

View File

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

View File

@ -0,0 +1,32 @@
using UnityEditor;
using UnityEngine;
namespace BFEditor
{
public class GameSettingWindow : EditorWindow {
// private bool NotchScreenSimulate = false;
private void Awake () {
// NotchScreenSimulate = PlayerPrefs.GetInt (BF.SafeAreaManager.NOTCH_SCREEN_SIMULATE, 0) == 1;
}
public GameSettingWindow () {
titleContent = new GUIContent ("游戏设置");
}
void OnGUI () {
// GUILayout.BeginHorizontal ("刘海屏模拟");
// bool isSimulate = EditorGUILayout.Toggle ("刘海屏模拟(Editor):", NotchScreenSimulate, GUILayout.Width (300));
// if (isSimulate != NotchScreenSimulate) {
// NotchScreenSimulate = isSimulate;
// PlayerPrefs.SetInt (BF.SafeAreaManager.NOTCH_SCREEN_SIMULATE, NotchScreenSimulate ? 1 : 0);
// Debug.Log ("Editor模式 刘海屏模拟是否开启:" + NotchScreenSimulate);
// }
// GUILayout.EndHorizontal ();
}
public static void ShowWindow () {
var window = GetWindow<GameSettingWindow> ();
window.Show ();
}
}
}

View File

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

View File

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

View File

@ -0,0 +1,145 @@
using System;
using System.Reflection;
using UnityEditor;
namespace BFEditor
{
public static class GameViewUtils
{
static object gameViewSizesInstance;
static MethodInfo getGroup;
static GameViewUtils()
{
var sizesType = typeof(Editor).Assembly.GetType("UnityEditor.GameViewSizes");
var singleType = typeof(ScriptableSingleton<>).MakeGenericType(sizesType);
var instanceProp = singleType.GetProperty("instance");
getGroup = sizesType.GetMethod("GetGroup");
gameViewSizesInstance = instanceProp.GetValue(null, null);
}
public enum GameViewSizeType
{
AspectRatio,
FixedResolution
}
public static void SetSize(int index)
{
var gvWndType = typeof(Editor).Assembly.GetType("UnityEditor.GameView");
var selectedSizeIndexProp = gvWndType.GetProperty("selectedSizeIndex",
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
var gvWnd = EditorWindow.GetWindow(gvWndType);
selectedSizeIndexProp.SetValue(gvWnd, index, null);
}
public static void AddCustomSize(int width, int height, string text = "")
{
AddCustomSize(GameViewSizeGroupType.Standalone, width, height, text);
}
public static void AddCustomSize(GameViewSizeGroupType sizeGroupType, int width, int height, string text)
{
AddCustomSize(GameViewSizeType.FixedResolution, sizeGroupType, width, height, text);
}
public static void AddCustomSize(GameViewSizeType viewSizeType, GameViewSizeGroupType sizeGroupType, int width, int height, string text)
{
var group = GetGroup(sizeGroupType);
var addCustomSize = getGroup.ReturnType.GetMethod("AddCustomSize");
var gvsType = typeof(Editor).Assembly.GetType("UnityEditor.GameViewSize");
var ctor = gvsType.GetConstructor(new Type[] { typeof(Editor).Assembly.GetType("UnityEditor.GameViewSizeType"),
typeof(int), typeof(int), typeof(string) });
var newGvsType = typeof(Editor).Assembly.GetType("UnityEditor.GameViewSizeType.FixedResolution");
if (viewSizeType == GameViewSizeType.AspectRatio)
newGvsType = typeof(Editor).Assembly.GetType("UnityEditor.GameViewSizeType.AspectRatio");
var newSize = ctor.Invoke(new object[] { newGvsType, width, height, text });
addCustomSize.Invoke(group, new object[] { newSize });
}
public static bool SizeExists(GameViewSizeGroupType sizeGroupType, string text)
{
return FindSize(sizeGroupType, text) != -1;
}
public static bool SizeExists(int width, int height)
{
return SizeExists(GameViewSizeGroupType.Standalone, width, height);
}
public static bool SizeExists(GameViewSizeGroupType sizeGroupType, int width, int height)
{
return FindSize(sizeGroupType, width, height) != -1;
}
public static int FindSize(GameViewSizeGroupType sizeGroupType, string text)
{
var group = GetGroup(sizeGroupType);
var getDisplayTexts = group.GetType().GetMethod("GetDisplayTexts");
var displayTexts = getDisplayTexts.Invoke(group, null) as string[];
for (int i = 0; i < displayTexts.Length; i++)
{
string display = displayTexts[i];
int pren = display.IndexOf('(');
if (pren != -1)
display = display.Substring(0, pren - 1);
if (display == text)
return i;
}
return -1;
}
public static int FindSize(int width, int height)
{
return FindSize(GameViewSizeGroupType.Standalone, width, height);
}
public static int FindSize(GameViewSizeGroupType sizeGroupType, int width, int height)
{
var group = GetGroup(sizeGroupType);
var groupType = group.GetType();
var getBuiltinCount = groupType.GetMethod("GetBuiltinCount");
var getCustomCount = groupType.GetMethod("GetCustomCount");
int sizesCount = (int)getBuiltinCount.Invoke(group, null) + (int)getCustomCount.Invoke(group, null);
var getGameViewSize = groupType.GetMethod("GetGameViewSize");
var gvsType = getGameViewSize.ReturnType;
var widthProp = gvsType.GetProperty("width");
var heightProp = gvsType.GetProperty("height");
var indexValue = new object[1];
for (int i = 0; i < sizesCount; i++)
{
indexValue[0] = i;
var size = getGameViewSize.Invoke(group, indexValue);
int sizeWidth = (int)widthProp.GetValue(size, null);
int sizeHeight = (int)heightProp.GetValue(size, null);
if (sizeWidth == width && sizeHeight == height)
return i;
}
return -1;
}
static object GetGroup(GameViewSizeGroupType type)
{
return getGroup.Invoke(gameViewSizesInstance, new object[] { (int)type });
}
public static GameViewSizeGroupType GetCurrentGroupType()
{
var getCurrentGroupTypeProp = gameViewSizesInstance.GetType().GetProperty("currentGroupType");
return (GameViewSizeGroupType)(int)getCurrentGroupTypeProp.GetValue(gameViewSizesInstance, null);
}
public static void GetCurrentGameViewSize(out int width, out int height)
{
var t = System.Type.GetType("UnityEditor.GameView,UnityEditor");
var getMainGameView = t.GetMethod("GetMainGameView", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
var res = getMainGameView.Invoke(null, null);
var gameView = (UnityEditor.EditorWindow)res;
var prop = gameView.GetType().GetProperty("currentGameViewSize", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var gvsize = prop.GetValue(gameView, new object[0] { });
var gvSizeType = gvsize.GetType();
height = (int)gvSizeType.GetProperty("height", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance).GetValue(gvsize, new object[0] { });
width = (int)gvSizeType.GetProperty("width", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance).GetValue(gvsize, new object[0] { });
}
}
}

View File

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

View File

@ -0,0 +1,107 @@
using UnityEditor;
using System.IO;
using UnityEngine;
using BF;
namespace BFEditor
{
public static class OtherToolsMenu
{
[MenuItem("其他工具/账号/创建新号", false, 1)]
static public void ClearAccount()
{
BFLog.Log("创建新号 success!");
LocalData.SetInt("ACCOUNT", Random.Range(100000, 999999));
}
[MenuItem("其他工具/账号/切换账号", false, 2)]
static public void SelectAccont()
{
var window = (SelectAccontWindow)EditorWindow.GetWindowWithRect(typeof(SelectAccontWindow),
new Rect(Screen.width / 2, Screen.height / 2, 520, 750));
window.Show();
}
[MenuItem("其他工具/账号/清除数据(B5)", false, 3)]
static public void ClearGameData()
{
LocalData.SetString("LAST_LOGIN_INFO", "{}");
LocalData.SetString("SEND_QUEUE", "{}");
LocalData.SetString("SDK_UID", "");
LocalData.SetInt("LAST_MAIL_ID", 0);
BFLog.Log("清除数据成功 success!");
}
[MenuItem("其他工具/一键导表", false, 3)]
public static void ExportExcel()
{
var designExcelPath = ExportExcelTools.GetDesignExcelPath();
if (string.IsNullOrEmpty(designExcelPath) || !Directory.Exists(designExcelPath))
{
OpenClientExcelWindow();
return;
}
var luaPath = Path.Combine(Application.dataPath, "Developer/lua/main.lua").Replace("\\", "/");
var isDev = File.Exists(luaPath);
EditorUtility.DisplayProgressBar("提示", "导表中", 1.0f);
bool succ = ExportExcelTools.FastExportExcelToLua(isDev, designExcelPath);
EditorUtility.ClearProgressBar();
if (succ)
{
EditorUtility.DisplayDialog("成功", "导出成功!", "ok");
}
else
{
EditorUtility.DisplayDialog("失败", "导出失败!", "ok");
}
}
[MenuItem("其他工具/打开导表窗口", false, 4)]
public static void OpenClientExcelWindow()
{
var window = (ExportExcelWindow)EditorWindow.GetWindowWithRect(typeof(ExportExcelWindow),
new Rect(Screen.width / 2, Screen.height / 2, 500, 120), true);
window.Show();
}
[MenuItem("其他工具/生成pb配置", false, 5)]
static private void SetProtoGitPath()
{
SetProtoPathWindow.Init();
}
[MenuItem("其他工具/打开设置窗口", false, 6)]
static void OpenGameSettingMenu()
{
GameSettingWindow.ShowWindow();
}
[MenuItem("其他工具/批量大写转小写", false, 7)]
public static void OpenToggleCaseWindow()
{
var window = (ToggleCaseWindow)EditorWindow.GetWindowWithRect(typeof(ToggleCaseWindow),
new Rect(Screen.width / 2, Screen.height / 2, 500, 120), true);
window.Show();
}
[MenuItem("其他工具/截图", false, 8)]
public static void CaptureScreenshot()
{
var desktopDir = System.Environment.GetFolderPath(System.Environment.SpecialFolder.DesktopDirectory);
var path = System.IO.Path.Combine(desktopDir, "dz_screenshot_");
int index = 0;
while(true)
{
var screenshotPath = path + index.ToString() + ".png";
if (!File.Exists(screenshotPath))
{
UnityEngine.ScreenCapture.CaptureScreenshot(screenshotPath, 1);
break;
}
index++;
}
}
}
}

View File

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

View File

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

View File

@ -0,0 +1,173 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
using XLua;
using BF;
namespace BFEditor
{
public class GenProtoConfig
{
static private string ProtoToolDir = Application.dataPath + "/../Tools/proto";
static private string PbSaveDir = Application.dataPath + "/proto";
static private string LuaSaveDir = Application.dataPath + "/Developer/lua/app/proto/";
static private string MsgIdsProto = PbSaveDir + "/bf_msgids.bytes";
static public void GenProtoLuaConfig()
{
try
{
var protoPath = LocalData.GetProtoPath();
if (string.IsNullOrEmpty(protoPath) ||
!Directory.Exists(protoPath))
{
EditorUtility.DisplayDialog("提示", "没有正确配置Proto路径", "确定");
return;
}
BFEditorUtils.RunCommond("python", ProtoToolDir + "/proto.py " + protoPath + " " + LuaSaveDir, ProtoToolDir,
(msg) =>
{
},
(err) =>
{
}
);
var dirInfo = new DirectoryInfo(protoPath);
var files = dirInfo.GetFiles("*.pb", SearchOption.AllDirectories);
for (var i = 0; i < files.Length; ++i)
{
var fileInfo = files[i];
var filePath = fileInfo.FullName;
var outPath = Path.Combine(PbSaveDir, filePath.Substring(protoPath.Length + 1)).Replace(".pb", ".bytes");
if (File.Exists(outPath))
{
var inMD5 = GameLaunchUtils.GetFileMD5(filePath);
var outMD5 = GameLaunchUtils.GetFileMD5(outPath);
if (inMD5.CompareTo(outMD5) == 0)
{
continue;
}
}
File.Copy(filePath, outPath, true);
}
// LuaEnv luaEnv = new LuaEnv();
// luaEnv.AddBuildin("pb", XLua.LuaDLL.Lua.LoadLuaProfobuf);
// luaEnv.DoString(@"allNames = {}");
// var pbFullName = MsgIdsProto;
// var pbPath = BF.Utils.GetAssetPathByFullPath(pbFullName);
// var luaText = CreateLuaString(pbPath);
// luaEnv.DoString(luaText);
// var luaDir = LuaSaveDir;
// if (!Directory.Exists(luaDir))
// Directory.CreateDirectory(luaDir);
// var luaPath = luaDir + "protomsgtype.lua";
// luaEnv.DoString(SaveLuaFile(luaPath));
// luaEnv.Dispose();
// AssetDatabase.ImportAsset(pbPath);
// AssetDatabase.Refresh();
// AssetDatabase.DeleteAsset(pbPath);
// Proto2MarkDown.Pb2Markdown();
EditorUtility.DisplayDialog("提示", "生成Proto配置成功", "确定");
}
catch (Exception e)
{
throw;
}
finally
{
}
}
static private string CreateLuaString(string protoPath)
{
var lua =
@"
local pb = require 'pb'
assert(pb.loadfile '" + protoPath + @"')
local function split(input, delimiter)
input = tostring(input)
delimiter = tostring(delimiter)
if (delimiter=='') then return false end
local pos,arr = 0, {}
-- for each divider found
for st,sp in function() return string.find(input, delimiter, pos, true) end do
table.insert(arr, string.sub(input, pos, st - 1))
pos = sp + 1
end
table.insert(arr, string.sub(input, pos))
return arr
end
local function unique(number)
for _,info in ipairs(allNames) do
if info.number == number then
return false
end
end
return true
end
for name, number, type in pb.fields 'MsgId' do
-- print(name, number)
if number > 0 and unique(number) then
table.insert(allNames, {number = number, name = name})
end
end
table.sort(allNames, function(a, b)
return a.number < b.number
end)
";
return lua;
}
static private string SaveLuaFile(string luaPath)
{
var lua =
@"
local output = 'local ProtoMsgType = {\n'
output = output .. ' FromMsgId = {\n'
for i, info in ipairs(allNames) do
output = output .. ' ' .. '[' .. info.number .. ']' .. ' = \'' .. info.name .. '\',\n'
end
output = output .. ' },\n'
output = output .. ' FromMsgToId = {\n'
for i, info in ipairs(allNames) do
output = output .. ' ' .. info.name .. ' = ' .. info.number .. ',\n'
end
output = output .. ' },\n'
output = output .. ' FromMsgEnum = {\n'
for i, info in ipairs(allNames) do
output = output .. ' ' .. 'req' .. info.name .. ' = \'' .. 'req' .. info.name .. '\',\n'
output = output .. ' ' .. 'rsp' .. info.name .. ' = \'' .. 'rsp' .. info.name .. '\',\n'
end
output = output .. ' },\n'
output = output .. '}\n\n'
output = output .. 'return ProtoMsgType'
local file = assert(io.open('" + luaPath + @"', 'w'))
file:write(output)
file:close()
-- print(output)
";
return lua;
}
}
}

View File

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

View File

@ -0,0 +1,654 @@
using System.Linq;
using System.Text;
using System.Collections.Generic;
using System;
using System.IO;
using BF;
namespace BFEditor
{
public class ProtoEnum
{
public string enumName = "";
public int enumNum = 0;
public int moduleId = 0;
}
public class ProtoModule
{
public string moduleDesc = "";
public int moduleNum = 0;
}
public class ProtoMessage
{
public string commentStr = "";
public string messageName = "";
public List<ProtoField> fields = new List<ProtoField>();
}
public class ProtoStruct
{
public string typeName = "";
public List<ProtoField> fields = new List<ProtoField>();
}
public class ProtoField
{
public string commentStr = "";
public string fieldName = "";
public string typeName = "";
public bool isRepeated = false;
public bool isMap = false;
public string keyTypeName = "";
public string valueTypeName = "";
}
public class Proto2MarkDown
{
const string COMMENT_CONST = "comment = ";
const string MESSAGE_NAME_CONST = "message_name = ";
const string MARKDOWN_FILE_NAME = "/bf_proto.md";
static string[] TYPE_KEYWORDS = new string[] { "uint32", "int32", "uint64", "int64", "string", "bool" };
static string MarkdownPath { get { return LocalData.GetProtoPath(); } }
static string MsgIdsProtoPath { get { return LocalData.GetProtoPath() + "/bf_msgids.proto"; } }
static string CommonProtoPath { get { return LocalData.GetProtoPath() + "/bf_comm.proto"; } }
static string LogicProtoPath { get { return LocalData.GetProtoPath() + "/bf_logic.proto"; } }
static string BattleProtoPath { get { return LocalData.GetProtoPath() + "/bf_battle.proto"; } }
static string protoMessageCommentStr = COMMENT_CONST;
static string protoStructTypeNameStr = "";
static StringBuilder sb = new StringBuilder();
static bool readingProtoEnum = false;
static bool readingProtoMessage = false;
static bool readingProtoStruct = false;
static List<ProtoEnum> protoEnums = new List<ProtoEnum>();
static List<ProtoModule> protoModules = new List<ProtoModule>();
static List<ProtoMessage> protoMessages = new List<ProtoMessage>();
static List<ProtoStruct> protoStructs = new List<ProtoStruct>();
static List<int> moduleIds = new List<int>();
public static bool Pb2Markdown()
{
protoEnums.Clear();
protoModules.Clear();
protoMessages.Clear();
protoStructs.Clear();
moduleIds.Clear();
if (string.IsNullOrEmpty(MsgIdsProtoPath) || string.IsNullOrEmpty(CommonProtoPath) ||
string.IsNullOrEmpty(LogicProtoPath) || string.IsNullOrEmpty(MarkdownPath) || string.IsNullOrEmpty(BattleProtoPath))
{
return false;
}
ParsingEnum();
ParsingLogic();
ParsingCommon();
ParsingBattle();
SortEnum();
Export2Markdown();
return true;
}
static void ParsingEnum()
{
var reader = new StreamReader(new FileStream(MsgIdsProtoPath, FileMode.Open));
var logicLineStr = "";
while ((logicLineStr = reader.ReadLine()) != null)
{
if (logicLineStr == "")
{
continue;
}
if (logicLineStr == "enum MsgId")
{
readingProtoEnum = true;
continue;
}
if (readingProtoEnum)
{
if (logicLineStr == "{")
{
continue;
}
if (logicLineStr == "}")
{
readingProtoEnum = false;
continue;
}
ReadProtoEnum(logicLineStr);
}
}
reader.Close();
reader.Dispose();
}
static void ParsingLogic()
{
var reader = new StreamReader(new FileStream(LogicProtoPath, FileMode.Open));
var logicLineStr = "";
while ((logicLineStr = reader.ReadLine()) != null)
{
if (logicLineStr == "")
{
continue;
}
if (logicLineStr.Replace(" ", "").Replace("\t", "").StartsWith("//") && logicLineStr.Contains("========"))
{
ReadProtoModule(logicLineStr);
continue;
}
if (logicLineStr.Replace(" ", "").Replace("\t", "").StartsWith("//"))
{
protoMessageCommentStr = protoMessageCommentStr + logicLineStr.Replace("//", " ");
continue;
}
if (logicLineStr.Replace(" ", "").Replace("\t", "").StartsWith("message"))
{
readingProtoMessage = true;
sb.AppendLine(protoMessageCommentStr);
protoMessageCommentStr = COMMENT_CONST;
string messageName = MESSAGE_NAME_CONST + logicLineStr.Replace(" ", "").Replace("\t", "").Replace("message", "").Replace("{", "");
sb.AppendLine(messageName);
continue;
}
if (readingProtoMessage)
{
if (logicLineStr.Replace(" ", "").Replace("\t", "") == "}")
{
ReadProtoMessage(sb);
readingProtoMessage = false;
}
else
{
sb.AppendLine(logicLineStr);
}
continue;
}
}
reader.Close();
reader.Dispose();
}
static void ReadProtoEnum(string enumStr)
{
enumStr = enumStr.Replace(" ", "").Replace("\t", "");
enumStr = enumStr.Replace(";", "");
var index = enumStr.IndexOf("//");
if (index > 0)
{
enumStr = enumStr.Remove(index, enumStr.Length - index);
}
var equalIndex = enumStr.IndexOf("=");
var name = enumStr.Substring(0, equalIndex);
var num = Int32.Parse(enumStr.Substring(equalIndex + 1, enumStr.Length - equalIndex - 1));
var pe = new ProtoEnum();
pe.enumName = name;
pe.enumNum = num;
var moduleKey = (num - (num % 1000)) / 1000;
pe.moduleId = moduleKey;
protoEnums.Add(pe);
}
static void ReadProtoModule(string moduleStr)
{
moduleStr = moduleStr.Replace(" ", "").Replace("\t", "");
moduleStr = moduleStr.Replace("=", "");
moduleStr = moduleStr.Replace("//", "");
var pm = new ProtoModule();
pm.moduleDesc = moduleStr;
var num = Int32.Parse(System.Text.RegularExpressions.Regex.Replace(moduleStr, @"[^0-9]+", ""));
pm.moduleNum = num;
moduleIds.Add(num);
protoModules.Add(pm);
}
static void ReadProtoMessage(StringBuilder sb)
{
var message = sb.ToString();
var lines = message.Split(new char[] { '\n' });
var pm = new ProtoMessage();
foreach (var line in lines)
{
if (line == "")
{
continue;
}
if (line.Contains(COMMENT_CONST))
{
pm.commentStr = line.Replace(COMMENT_CONST, "");
continue;
}
if (line.Contains(MESSAGE_NAME_CONST))
{
pm.messageName = line.Replace(MESSAGE_NAME_CONST, "");
continue;
}
var pf = GetProtoField(line);
pm.fields.Add(pf);
}
protoMessages.Add(pm);
sb.Clear();
}
static void SortEnum()
{
protoEnums.Sort((x, y) => { return x.enumNum - y.enumNum; });
}
static void ParsingCommon()
{
var reader = new StreamReader(new FileStream(CommonProtoPath, FileMode.Open));
var commonLineStr = "";
while ((commonLineStr = reader.ReadLine()) != null)
{
if (commonLineStr == "")
{
continue;
}
if (commonLineStr.Replace(" ", "").Replace("\t", "").StartsWith("message"))
{
protoStructTypeNameStr = commonLineStr.Replace(" ", "").Replace("\t", "").Replace("message", "").Replace("{", "");
readingProtoStruct = true;
continue;
}
if (readingProtoStruct)
{
if (commonLineStr.Replace(" ", "").Replace("\t", "") == "}")
{
ReadProtoStruct(sb, protoStructTypeNameStr);
protoStructTypeNameStr = "";
readingProtoStruct = false;
}
else
{
sb.AppendLine(commonLineStr);
}
continue;
}
}
reader.Close();
reader.Dispose();
}
static void ParsingBattle()
{
var reader = new StreamReader(new FileStream(BattleProtoPath, FileMode.Open));
var lineStr = "";
while ((lineStr = reader.ReadLine()) != null)
{
if (lineStr == "")
{
continue;
}
if (lineStr.Replace(" ", "").Replace("\t", "").StartsWith("message"))
{
protoStructTypeNameStr = lineStr.Replace(" ", "").Replace("\t", "").Replace("message", "").Replace("{", "");
readingProtoStruct = true;
continue;
}
if (readingProtoStruct)
{
if (lineStr.Replace(" ", "").Replace("\t", "") == "}")
{
ReadProtoStruct(sb, protoStructTypeNameStr);
protoStructTypeNameStr = "";
readingProtoStruct = false;
}
else
{
sb.AppendLine(lineStr);
}
continue;
}
}
reader.Close();
reader.Dispose();
}
static void ReadProtoStruct(StringBuilder sb, string structName)
{
var ps = new ProtoStruct();
ps.typeName = structName;
var message = sb.ToString();
var lines = message.Split(new char[] { '\n' });
foreach (var line in lines)
{
if (line == "")
{
continue;
}
ProtoField pf = GetProtoField(line);
ps.fields.Add(pf);
}
sb.Clear();
protoStructs.Add(ps);
}
static ProtoField GetProtoField(string fieldLine)
{
var pf = new ProtoField(); // 解析filed
if (fieldLine.Replace(" ", "").Replace("\t", "").StartsWith("repeated"))
{
pf.isRepeated = true;
}
if (fieldLine.Contains("//"))
{
var index1 = fieldLine.IndexOf("//");
var comment = fieldLine.Substring(index1, fieldLine.Length - index1);
pf.commentStr = comment;
}
var index2 = fieldLine.IndexOf("=");
var fieldStr = fieldLine.Remove(index2).Replace("repeated", "");
if (fieldStr.Replace(" ", "").Replace("\t", "").StartsWith("map<")) // map结构
{
pf.isMap = true;
var index3 = fieldStr.IndexOf(">") + 1;
var fieldName = fieldStr.Substring(index3, fieldStr.Length - index3).Replace(" ", "").Replace("\t", "");
pf.fieldName = fieldName;
var keyValueStr = fieldStr.Replace("map<", "").Replace(">", "").Replace(fieldName, "").Replace(" ", "").Replace("\t", "");
var index4 = keyValueStr.IndexOf(",");
var keyName = keyValueStr.Substring(0, index4);
pf.keyTypeName = keyName;
var vauleName = keyValueStr.Substring(index4 + 1, keyValueStr.Length - index4 - 1);
pf.valueTypeName = vauleName.Replace(" ", "").Replace("\t", "");
}
else
{
while (fieldStr.Contains(" ")) // 这个处理不好
{
fieldStr = fieldStr.Replace(" ", " ");
}
var typeFiledNameStr = fieldStr.Split(new char[] { ' ' });
foreach (var item in typeFiledNameStr)
{
if (item == "")
{
continue;
}
if (pf.typeName == "")
{
pf.typeName = item.Replace(" ", "").Replace("\t", "");
continue;
}
else
{
pf.fieldName = item;
break;
}
}
}
return pf;
}
static void Export2Markdown()
{
var sw = new StreamWriter(MarkdownPath + MARKDOWN_FILE_NAME, false);
WriteTitle(sw);
WriteProtoModule(sw);
WriteProtoEnum(sw);
WriteProtoMessage(sw);
WriteProtoStructs(sw);
sw.Close();
sw.Dispose();
}
static void WriteTitle(StreamWriter sw)
{
sw.WriteLine("# *BF协议文档*");
sw.WriteLine(" ");
sw.WriteLine("<I><B>Auto Generated By: </B></I>");
sw.WriteLine(" ");
sw.WriteLine("bf_logic.proto (协议描述文件)\n");
sw.WriteLine("bf_comm.proto、bf_battle.proto (结构体描述文件)\n");
sw.WriteLine("bf_msgids.proto(协议枚举描述文件)\n");
sw.WriteLine(" ");
sw.WriteLine("<I><B>文档分为四部分:</B></I>\n");
sw.WriteLine("[功能模块](#一)、[协议枚举](#二)、[消息内容](#三)、[自定义结构体](#四)");
sw.WriteLine(" ");
sw.WriteLine("req: 前端请求, rsp: 服务器应答");
}
static void WriteProtoModule(StreamWriter sw)
{
sw.WriteLine(" ");
sw.WriteLine("## " + GetHtmlLink("top") + GetHtmlLink("一") + "第一部分 模块");
foreach (var item in protoModules)
{
sw.WriteLine(" - [" + "模块id: " + item.moduleNum + " // " + item.moduleDesc + "]" + "(#" + item.moduleNum + ")");
}
}
static void WriteProtoEnum(StreamWriter sw)
{
sw.WriteLine(" ");
sw.WriteLine("## " + GetHtmlLink("二") + "第二部分 ProtoEnum");
var moduleId = protoEnums[0].moduleId;
sw.WriteLine("### " + GetHtmlLink(moduleId.ToString()) + "模块" + moduleId + " // " + GetModuleDesc(moduleId));
foreach (var item in protoEnums)
{
if (item.moduleId != moduleId)
{
moduleId = item.moduleId;
sw.WriteLine("### " + GetHtmlLink(moduleId.ToString()) + "模块" + moduleId + " // " + GetModuleDesc(moduleId));
}
sw.WriteLine("> - [" + item.enumName + " = " + item.enumNum + "]" + "(#" + item.enumName + ")");
}
}
static string GetModuleDesc(int key)
{
foreach (var item in protoModules)
{
if (item.moduleNum == key)
{
return item.moduleDesc;
}
}
return "";
}
static void WriteProtoMessage(StreamWriter sw)
{
sw.WriteLine(" ");
sw.WriteLine("## " + GetHtmlLink("三") + "第三部分 ProtoMessage");
foreach (var item in protoEnums)
{
var rspPM = GetRspMessage(item.enumName);
var reqPM = GetReqMessage(item.enumName);
if (rspPM == null && reqPM == null)
{
continue;
}
string comment = "";
if (reqPM != null && reqPM.commentStr != "")
{
comment = reqPM.commentStr;
}
else
{
comment = rspPM.commentStr;
}
sw.WriteLine("## " + "__" + GetHtmlLink(item.enumName) + "消息 " + item.enumName + " //" + comment + "__");
WriteProtoMessage(sw, reqPM, "req : ");
sw.WriteLine(" ");
WriteProtoMessage(sw, rspPM, "rsp : ");
sw.WriteLine(" ");
DrawTopBtn(sw);
sw.WriteLine("<br>");
sw.WriteLine("<br>");
sw.WriteLine(" ");
}
}
static ProtoMessage GetRspMessage(string enumName)
{
const string RSP = "rsp";
foreach (var item in protoMessages)
{
if (item.messageName == RSP + enumName)
{
return item;
}
}
return null;
}
static ProtoMessage GetReqMessage(string enumName)
{
const string REQ = "req";
foreach (var item in protoMessages)
{
if (item.messageName == REQ + enumName)
{
return item;
}
}
return null;
}
static void WriteProtoMessage(StreamWriter sw, ProtoMessage pm, string title)
{
if (pm == null)
{
return;
}
sw.WriteLine("### " + title);
sw.WriteLine(" ");
WriteTableTitle(sw);
foreach (var item in pm.fields)
{
WriteField(sw, item);
}
}
static void WriteTableTitle(StreamWriter sw)
{
sw.WriteLine("| repeated | 类型 | 字段名 | 注释 |");
sw.WriteLine("| :------------ | :------------ | :------------ | :------------ |");
}
static void WriteField(StreamWriter sw, ProtoField pf)
{
var str = "| ";
if (pf.isRepeated)
{
str = str + "repeated |";
}
else
{
str = str + " |";
}
if (pf.isMap)
{
str = str + "map< " + GetTypeMarkdownStr(pf.keyTypeName) + " , " + GetTypeMarkdownStr(pf.valueTypeName) + "> |";
}
else
{
str = str + GetTypeMarkdownStr(pf.typeName) + " |";
}
str = str + pf.fieldName + " |";
str = str + pf.commentStr + " |";
sw.WriteLine(str);
}
static string GetTypeMarkdownStr(string typeName)
{
if (TYPE_KEYWORDS.Contains(typeName))
{
return typeName;
}
else
{
return "[" + typeName + "]" + "(#TYPE_" + typeName + ")";
}
}
static void WriteProtoStructs(StreamWriter sw)
{
sw.WriteLine(" ");
sw.WriteLine("## " + GetHtmlLink("四") + "第四部分 结构体");
foreach (var item in protoStructs)
{
WriteStruct(sw, item);
sw.WriteLine(" ");
DrawTopBtn(sw);
sw.WriteLine("<br>");
sw.WriteLine(" ");
}
}
static void WriteStruct(StreamWriter sw, ProtoStruct ps)
{
sw.WriteLine("### " + "__" + GetHtmlLink("TYPE_" + ps.typeName) + "结构体 " + ps.typeName + "__");
WriteTableTitle(sw);
foreach (var item in ps.fields)
{
WriteField(sw, item);
}
}
static string GetHtmlLink(string name)
{
return "<a name = \"" + name + "\" />";
}
static void DrawTopBtn(StreamWriter sw)
{
sw.WriteLine("<p align=\"right\"><a href=\"#top\"><I><U>回到顶部</U></I></a></p>");
}
}
}

View File

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

View File

@ -0,0 +1,51 @@
using System;
using BF;
using UnityEditor;
using UnityEngine;
namespace BFEditor
{
public class SetProtoPathWindow : EditorWindow
{
[SerializeField]
private string protoPath;
private SerializedObject serializedObject;
private SerializedProperty protoPathProperty;
static public void Init()
{
GetWindowWithRect<SetProtoPathWindow>(new Rect(Screen.width / 2, Screen.height / 2, 500, 120), true, "生成pb配置", true);
}
private void OnEnable()
{
protoPath = LocalData.GetProtoPath();
}
private void OnGUI()
{
GUILayout.Space(18);
GUILayout.BeginHorizontal();
GUILayout.Label("选择pb路径");
GUILayout.TextField(protoPath, GUILayout.Width(300));
if (GUILayout.Button("选择", GUILayout.Width(80)))
{
protoPath = EditorUtility.OpenFolderPanel("select pb path", Application.dataPath, "");
LocalData.SetProtoPath(protoPath);
}
GUILayout.EndHorizontal();
GUILayout.Space(10);
GUILayout.BeginHorizontal();
GUILayout.Space(190);
if (GUILayout.Button("生成", GUILayout.Width(110), GUILayout.Height(20)))
{
GenProtoConfig.GenProtoLuaConfig();
Close();
}
GUILayout.EndHorizontal();
GUILayout.Space(10);
}
}
}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,115 @@
using System;
using System.Linq;
using System.IO;
using UnityEngine;
using System.Collections.Generic;
namespace BFEditor.Resource
{
public class DependenciesCheckTool
{
static bool checkedOver = true;
public static bool CheckedOver
{
get{return checkedOver;}
private set{}
}
static Dictionary<string, List<string>> abnormalDependenciesDic = new Dictionary<string, List<string>>();
public static Dictionary<string, List<string>> AbnormalDependenciesDic
{
get{return abnormalDependenciesDic;}
private set{}
}
public static List<BaseDependenciesChecker> GetAllCheckers()
{
var result = new List<BaseDependenciesChecker>()
{
new AnimationDependenciesChecker(),
new AtlasDependenciesChecker(),
new BakedataDependenciesChecker(),
new EffectDependenciesChecker(),
new FirstDependenciesChecker(),
new FontDependenciesChecker(),
new LanguageDependenciesChecker(),
new LuaDependenciesChecker(),
new MaterialDependenciesChecker(),
new ModelDependenciesChecker(),
new ShaderDependenciesChecker(),
new ShakeDependenciesChecker(),
new SkyboxDependenciesChecker(),
new SoundDependenciesChecker(),
new SpineDependenciesChecker(),
new TextureDependenciesChecker(),
new TimelineDependenciesChecker(),
new VideoDependenciesChecker(),
new EffectPrefabDependenciesChecker(),
new TimelinePrefabDependenciesChecker(),
new SpritePrefabDependenciesChecker(),
new OtherPrefabDependenciesChecker(),
new ModelPrefabDependenciesChecker(),
new SpinePrefabDependenciesChecker(),
new CityPrefabDependenciesChecker(),
};
return result;
}
public static void Clear()
{
checkedOver = false;
abnormalDependenciesDic.Clear();
}
static AssetBundleManifest GetManifest()
{
var streamPath = Path.Combine(Application.streamingAssetsPath, "asset_bundle_manifest.ab");
if (!File.Exists(streamPath))
{
Debug.LogError("文件不存在 " + streamPath);
return null;
}
var assetBundle = AssetBundle.LoadFromFile(streamPath);
var manifest = assetBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
assetBundle.Unload(false);
return manifest;
}
public static Dictionary<string, List<string>> CheckDependenceies(Action<bool> checkOverAction = null, List<BaseDependenciesChecker> allCheckers = null)
{
if(allCheckers == null)
{
allCheckers = GetAllCheckers();
}
checkedOver = false;
abnormalDependenciesDic.Clear();
AssetBundleManifest manifest = GetManifest();
string[] allBundles = manifest.GetAllAssetBundles();
foreach(var bundle in allBundles)
{
List<string> abnormalDependencies = new List<string>();
string[] allDependencies = manifest.GetDirectDependencies(bundle);
foreach(BaseDependenciesChecker checker in allCheckers)
{
if(checker.Active && bundle.Contains(checker.GetCheckPathPrefix()))
{
abnormalDependencies = checker.GetAbnormalDenpendencies(bundle, allDependencies);
break;
}
}
if(abnormalDependencies.Count > 0)
{
abnormalDependenciesDic.Add(bundle, abnormalDependencies);
}
}
checkedOver = true;
if(checkOverAction != null)
{
checkOverAction(abnormalDependenciesDic.Keys.Count <= 0);
}
return abnormalDependenciesDic;
}
}
}

View File

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

View File

@ -0,0 +1,129 @@
using System.IO;
using System;
using UnityEditor;
using UnityEngine;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace BFEditor.Resource
{
public class DependenciesCheckWindow : EditorWindow
{
Vector2 scrollPosition = new Vector2(100, 100);
Action<bool> checkOverAction;
List<BaseDependenciesChecker> allChekers = new List<BaseDependenciesChecker>();
DependenciesCheckWindow()
{
this.titleContent = new GUIContent("资源依赖检查");
DependenciesCheckTool.Clear();
foreach(var checker in DependenciesCheckTool.GetAllCheckers())
{
allChekers.Add(checker);
}
checkOverAction = (bool result) =>
{
if (result)
{
EditorUtility.DisplayDialog("提示", "检查完成,符合标准", "ok");
}
};
}
void OnGUI()
{
DrawButton();
DrawResult();
}
void DrawButton()
{
int count = allChekers.Count;
int column = (int)Math.Ceiling(count / 6.0); //900 / 150 = 6
for(int i = 0; i < column; i++)
{
GUILayout.BeginHorizontal();
for(int j = 0; j < 6; j++)
{
int curIndex = i*6 + j;
if(curIndex < count)
{
allChekers[curIndex].Active = GUILayout.Toggle(allChekers[curIndex].Active, allChekers[curIndex].GetCheckerName(), GUILayout.Width(150));
}
}
GUILayout.EndHorizontal();
}
GUILayout.Space(15);
var style = new GUIStyle(GUI.skin.button);
style.fontSize = 15;
style.alignment = TextAnchor.MiddleCenter;
GUILayout.BeginHorizontal();
if (GUILayout.Button("检查所选", style, GUILayout.Width(100), GUILayout.Height(26)))
{
CheckAll();
}
if (GUILayout.Button("全选", style, GUILayout.Width(100), GUILayout.Height(26)))
{
for(int i = 0; i < allChekers.Count; i++)
{
allChekers[i].Active = true;
}
}
if (GUILayout.Button("取消全选", style, GUILayout.Width(100), GUILayout.Height(26)))
{
for(int i = 0; i < allChekers.Count; i++)
{
allChekers[i].Active = false;
}
}
GUILayout.EndHorizontal();
GUILayout.Space(15);
}
void CheckAll()
{
DependenciesCheckTool.CheckDependenceies(checkOverAction, allChekers);
}
void DrawResult()
{
if(DependenciesCheckTool.CheckedOver)
{
GUILayout.BeginHorizontal();
GUILayout.Label("AB包", GUILayout.Width(450));
GUILayout.Label("不正常依赖", GUILayout.Width(450));
GUILayout.EndHorizontal();
scrollPosition = GUILayout.BeginScrollView(
scrollPosition, GUILayout.Width(900), GUILayout.Height(800));
Dictionary<string, List<string>> abnormalDependenciesDic = DependenciesCheckTool.AbnormalDependenciesDic;
foreach(string bundle in abnormalDependenciesDic.Keys)
{
GUILayout.BeginHorizontal();
GUILayout.TextField(bundle, GUILayout.Width(450));
GUILayout.BeginVertical();
foreach(string dependent in abnormalDependenciesDic[bundle])
{
GUILayout.TextField(dependent);
}
GUILayout.EndVertical();
GUILayout.EndHorizontal();
GUILayout.Label("", GUILayout.Width(100));
}
GUILayout.EndScrollView();
}
}
void OnDestroy()
{
DependenciesCheckTool.Clear();
}
public static void ShowDependenciesCheckWindow()
{
var window = (DependenciesCheckWindow)GetWindowWithRect(typeof(DependenciesCheckWindow),
new Rect(Screen.width / 2, Screen.height / 2, 900, 800));
window.Show();
}
}
}

View File

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

View File

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

View File

@ -0,0 +1,26 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
namespace BFEditor.Resource
{
public class AnimationDependenciesChecker:BaseDependenciesChecker
{
public override string GetCheckerName()
{
return "animation";
}
public override string GetCheckPathPrefix()
{
return "arts/animations/";
}
public override string[] GetWhitePath()
{
string[] whitePath = {
"arts/shader"
};
return whitePath;
}
}
}

View File

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

View File

@ -0,0 +1,19 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
namespace BFEditor.Resource
{
public class AtlasDependenciesChecker:BaseDependenciesChecker
{
public override string GetCheckerName()
{
return "atlas";
}
public override string GetCheckPathPrefix()
{
return "arts/textures/";
}
}
}

View File

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

View File

@ -0,0 +1,19 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
namespace BFEditor.Resource
{
public class BakedataDependenciesChecker : BaseDependenciesChecker
{
public override string GetCheckerName()
{
return "bakedatas";
}
public override string GetCheckPathPrefix()
{
return "arts/bakedatas/";
}
}
}

View File

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

View File

@ -0,0 +1,88 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
namespace BFEditor.Resource
{
public abstract class BaseDependenciesChecker
{
private bool active = true;
public bool Active{
get{return active;}
set{active = value;}
}
public List<string> GetAbnormalDenpendencies(string bundleName, string[] allDependencies)
{
// 第一次筛选
List<string> abnormalDependencies = new List<string>();
foreach(var dependent in allDependencies)
{
bool skip = false;
foreach(var whiteItem in GetWhitePath())
{
if(dependent.Contains(whiteItem))
{
skip = true;
break;
}
}
if(skip)
{
continue;
}
abnormalDependencies.Add(dependent);
}
// 第二次筛选
List<string> subWhiteList = GetDetailWhitePath(bundleName);
for (var i = abnormalDependencies.Count - 1; i >= 0; i--)
{
foreach(var v in subWhiteList)
{
if(abnormalDependencies[i].Contains(v))
{
abnormalDependencies.RemoveAt(i);
break;
}
}
}
OnCheck(bundleName, ref abnormalDependencies);
return abnormalDependencies;
}
protected List<string> GetDetailWhitePath(string bundleName)
{
Dictionary<string, List<string>> detailWhiteDic = GetDetailWhiteDic();
List<string> subWhiteList = new List<string>();
foreach(var k in detailWhiteDic.Keys)
{
if(bundleName.Contains(k))
{
subWhiteList = detailWhiteDic[k];
break;
}
}
return subWhiteList;
}
public abstract string GetCheckerName();
public abstract string GetCheckPathPrefix();
public virtual string[] GetWhitePath()
{
string[] whitePath = {
// "xxxxxxxx",
};
return whitePath;
}
protected virtual Dictionary<string, List<string>> GetDetailWhiteDic()
{
Dictionary<string, List<string>> detailWhiteDic = new Dictionary<string, List<string>>(){
// {"prefabs/effects/underground_palace",
// new List<string>(){
// "prefabs/effects/underground_palace"
// }
// },
};
return detailWhiteDic;
}
public virtual void OnCheck(string bundleName, ref List<string> allDependencies){}
}
}

View File

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

View File

@ -0,0 +1,32 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
namespace BFEditor.Resource
{
public class CityPrefabDependenciesChecker:BaseDependenciesChecker
{
public override string GetCheckerName()
{
return "city-prefab";
}
public override string GetCheckPathPrefix()
{
return "prefabs/city/";
}
public override string[] GetWhitePath()
{
string[] whitePath = {
"arts/animations/ui/city",
"arts/effects/ui",
"arts/materials.ab",
"arts/textures/ui/city_build.ab",
"arts/spines/",
"arts/textures/ui/common.ab",
"prefabs/effects/ui_effect/ui_citybox.prefab.ab"
};
return whitePath;
}
}
}

View File

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

View File

@ -0,0 +1,27 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
namespace BFEditor.Resource
{
public class EffectDependenciesChecker:BaseDependenciesChecker
{
public override string GetCheckerName()
{
return "effects";
}
public override string GetCheckPathPrefix()
{
return "arts/effects/";
}
public override string[] GetWhitePath()
{
string[] whitePath = {
"arts/shader",
"arts/effects/common/textures/"
};
return whitePath;
}
}
}

View File

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

View File

@ -0,0 +1,85 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
namespace BFEditor.Resource
{
public class EffectPrefabDependenciesChecker:BaseDependenciesChecker
{
public override string GetCheckerName()
{
return "effect-prefab";
}
public override string GetCheckPathPrefix()
{
return "prefabs/effects/";
}
public override string[] GetWhitePath()
{
string[] whitePath = {
"arts/materials.ab",
"arts/textures/",
"arts/shader",
"arts/effects/common/",
};
return whitePath;
}
protected override Dictionary<string, List<string>> GetDetailWhiteDic()
{
Dictionary<string, List<string>> detailWhiteDic = new Dictionary<string, List<string>>(){
{"prefabs/effects/battle_effects/",
new List<string>(){
"arts/effects/ui/ui_battle.ab",
"arts/effects/battle"
}
},
{"prefabs/effects/ui",
new List<string>(){
"arts/effects/ui"
}
},
{"prefabs/effects/card_effects/",
new List<string>(){
"arts/effects/card_ui",
"arts/effects/card/"
}
},
{"prefabs/effects/underground_palace",
new List<string>(){
"arts/effects/underground_palace/",
"arts/models/maps/underground_palace"
}
},
{"prefabs/effects/city_build_effects/city_build_teardown",
new List<string>(){
"arts/effects/city_build/city_build_teardown",
}
},
};
return detailWhiteDic;
}
public override void OnCheck(string bundleName, ref List<string> abnormalDependencies)
{
List<string> dependencies = new List<string>();
string[] strs = bundleName.Split('/');
string name = strs[strs.Length - 1].Replace(".prefab.ab", string.Empty);
foreach(var dependent in abnormalDependencies)
{
if(dependent.Contains(name))
{
continue;
}
strs = dependent.Split('/');
string dependentName = strs[strs.Length - 1].Replace(".ab", string.Empty);
if(name.Contains(dependentName))
{
continue;
}
dependencies.Add(dependent);
}
abnormalDependencies = dependencies;
}
}
}

View File

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

View File

@ -0,0 +1,26 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
namespace BFEditor.Resource
{
public class FirstDependenciesChecker:BaseDependenciesChecker
{
public override string GetCheckerName()
{
return "first";
}
public override string GetCheckPathPrefix()
{
return "first/";
}
public override string[] GetWhitePath()
{
string[] whitePath = {
"arts/shaders.ab",
};
return whitePath;
}
}
}

View File

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

View File

@ -0,0 +1,26 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
namespace BFEditor.Resource
{
public class FontDependenciesChecker:BaseDependenciesChecker
{
public override string GetCheckerName()
{
return "fonts";
}
public override string GetCheckPathPrefix()
{
return "arts/fonts/";
}
public override string[] GetWhitePath()
{
string[] whitePath = {
"arts/shader"
};
return whitePath;
}
}
}

View File

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

View File

@ -0,0 +1,26 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
namespace BFEditor.Resource
{
public class LanguageDependenciesChecker:BaseDependenciesChecker
{
public override string GetCheckerName()
{
return "language";
}
public override string GetCheckPathPrefix()
{
return "arts/language/";
}
public override string[] GetWhitePath()
{
string[] whitePath = {
"arts/shader"
};
return whitePath;
}
}
}

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