文章目录
Unity版本
- 使用Unity2022.3.16f1 LTS 版本
- 框架使用长期支持版均可
初始化项目
版本管理
- 使用Git进行版本管理
使用Gitkraken图形化工具
- Gitkraken官网: Gitkraken(可能需要科学上网)
- Gitkraken入门学习文章:Gitkraken学习-五仁烧饼-CSDN
- B站视频教程:Gitkraken入门视频-奇乐编程学院 / Gitkraken进阶视频-奇乐编程学院
- Gitkraken集成了Github,Gitlab等国外主流平台,但是国内网络有Push失败的可能,如果网络质量不佳或者无合适的梯子,可以选择提交到国内Gitee等平台
连接Gitee
1.打开Gitkraken,然后找到设置中的SSH,如果之前没有生成,点击生成后,点击SSH Public Key右边的复制按钮
2. 打开Gitee设置,复制到箭头处,自定义SSH标识名称
3.在Gitkraken中选择克隆即可
框架雏形
- 框架简单来说就是多个方便易用的工具代码集成在一起,能够更便利地解决实际问题的拥有一定结构的整体性代码。
基础目标
- 首先完成版本导出功能,方便未来新增内容
- 接下来拆分目标
了解Editor文件夹
- 只涉及编辑器的代码可以放在Editor文件夹(unity资源包导出可以是只关于编辑器行为的,所以可以直接放入Editor文件夹)
- Editor文件夹的代码会编译到Assembly-CSharp-Editor.dll中,它可以访问运行时DLL中的代码(也就是指具体的方法和变量),但是运行时DLL中的代码无法访问它,这是因为编辑模式下的代码不能在运行期间使用。
1.生成文件名:
- 核心API:
DateTime.Now.ToString("yyyyMMdd_HH")
- HH是指24小时制,hh是12小时显示
public static class GeneratePackageName
{
[MenuItem("ZFramework/1.生成 UnityPackage 包的名字", false, 0)]
static void MenuClick()
{
Debug.Log("ZFramework_" + DateTime.Now.ToString("yyyyMMdd_HH"));
}
}
2.复制字符串到剪贴板:
- 核心API:
GUIUtility.systemCopyBuffer
public static class CpyText2ClipBoard
{
[MenuItem("ZFramework/2.复制字符串到剪贴板", false, 1)]
static void MenuClick()
{
GUIUtility.systemCopyBuffer = "复制的文本";
}
}
3.直接生成名称并复制到剪贴板:
public static class GenerateName2ClipBoard
{
[MenuItem("ZFramework/3.生成包名到剪贴板", false, 2)]
static void Create()
{
var packageName = "ZFramework_" + DateTime.Now.ToString("yyyyMMdd_HH");
GUIUtility.systemCopyBuffer = packageName;
}
}
4.Export资源包
- 这个按钮在Asset菜单中,那么我们就可以调用代码进行(只要是能看到的一些操作或方法,很大概率有开发者可以调用的API)
AssetDatabase.ExportPackage()
public static class ExportZFramework
{
[MenuItem(EditorGlobal.ZFrameworkMenuItemPath + "4.导出包到文件夹", false, 3)]
static void CreateExport()
{
// Debug.Log("成功使用全局字符串设置");
// 得到所有子文件夹的路径
var assetPathNameArray = AssetDatabase.GetSubFolders("Assets");
// 最终导出的包的文件名,要带上后缀拓展名 + ".unitypackage"
var filename = "ZFramework_" + DateTime.Now.ToString("yyyyMMdd_HH") + EditorGlobal.UnityPackageStr;
// 方法第三个参数为导出设置,默认Default是只打包当前文件夹的内容,Recurse是递归所有子列表打包,Interactive是异步打包,完成后显示打包位置
AssetDatabase.ExportPackage(assetPathNameArray, filename,
ExportPackageOptions.Recurse | ExportPackageOptions.Interactive);
}
}
5.打开文件夹
- Application.dataPath是项目Assets的绝对路径
Application.OpenURL(path);
可以打开对应的文件夹路径
[MenuItem(EditorGlobal.ZFrameworkMenuItemPath + "4.打开Application.dataPath", false, 3)]
static void OpenFolder()
{
// 得到绝对路径
var path = Application.dataPath;
Debug.Log("输出 Application.dataPath 的路径为: " + path);
// 也能打开网页
Application.OpenURL(path);
}
6.MenuItem的复用
- 通过一个MenuItem调用其他的MenuItem,可以是多个
public static class ReuseMenuItem
{
[MenuItem(EditorGlobal.ZFrameworkMenuItemPath + "6.MenuItem的复用", false, 5)]
static void ReuseExecute()
{
Debug.Log("Unity编辑器的路径为: " + EditorApplication.applicationPath);
EditorApplication.ExecuteMenuItem(EditorGlobal.ZFrameworkMenuItemPath + "4.打开Application.dataPath");
}
}
7.自定义快捷键
public static class CustomShortCut
{
[MenuItem(EditorGlobal.ZFrameworkMenuItemPath + "7.自定义快捷键一键导出 %e", false, 6)]
static void ExecuteShortCut()
{
Debug.Log("快捷键输出: %e = Ctrl + E");
EditorApplication.ExecuteMenuItem(EditorGlobal.ZFrameworkMenuItemPath + "4.导出包到文件夹");
}
}
第二部分
- 整合第一阶段的方法
- 目前第一阶段的方法是放到Editor文件夹,那么代表了这些方法都是不可以被逻辑部分调用的,但是其中有一部分方法在游戏逻辑中可能需要,避免重复编写。
- 以及直接使用MenuItem的方法不能传参数,也不能有返回值,所以现在要提取具体方法。
- 或者直接将所有C#脚本移出Editor文件夹,对每个UnityEditor的内容使用宏定义
#if UNITY_EDITOR 中间是要包裹的内容 #endif
- 为了解决以上问题,得出最终方案:
- 首先把第一阶段的所有Editor方法,集成到一个C# 脚本文件中
- 该脚本文件命名为:FirstStageEditor, 表示第一阶段的Editor方法,然后设置全局常量路径字符串,避免打错字符
- 将之前的类名写成方法名,且方法名加上前缀ZEditor,表示这是ZFramework中Editor文件夹的方法
- 新建一个Editor文件夹之外的脚本文件,将具体方法实现从Editor文件夹中提取出来,供游戏逻辑中调用,可以写参数,可以有返回值
Editor脚本类,放在Editor脚本下
public static class FirstStageEditor
{
const string ZFrameworkMenuItemPath = "ZFramework/";
[MenuItem(ZFrameworkMenuItemPath + "第一阶段方法集合/" + "1.生成 UnityPackage 包的名字", false, 0)]
static void ZEditorGeneratePackageName()
{
Debug.Log(GlobalEditorFunction.GeneratePackageName());
}
[MenuItem(ZFrameworkMenuItemPath + "第一阶段方法集合/" + "2.复制包名到剪贴板", false, 1)]
static void ZEditorCopyText2ClipBoard()
{
GlobalEditorFunction.GenerateNameCopy2ClipBoard();
}
[MenuItem(ZFrameworkMenuItemPath + "第一阶段方法集合/" + "3.打开 Application.dataPath 路径", false, 2)]
static void ZEditorOpenFolder()
{
// 得到绝对路径
var path = Application.dataPath;
Debug.Log("输出 Application.dataPath 的路径为: " + path);
GlobalEditorFunction.OpenFolder(path);
}
[MenuItem(ZFrameworkMenuItemPath + "第一阶段方法集合/" + "4.一键导出Unity包,含有MenuItem复用 %E", false, 3)]
static void ZEditorExportZFrameworkPackage()
{
// 设置所有子文件夹的路径
var folderList = new List<string>
{
"Assets/Editor",
"Assets/Plugins",
"Assets/ZFramework"
};
// 控制台同时输出一次包名
EditorApplication.ExecuteMenuItem(ZFrameworkMenuItemPath + "第一阶段方法集合/" + "1.生成 UnityPackage 包的名字");
// 最终导出包的名字,带上后缀拓展名
var fileName = GlobalEditorFunction.GeneratePackageName() + ".unitypackage";
// 方法第三个参数为导出设置,Default 是不递归子文件夹,Recurse 是向下递归所有子文件夹,Interactive 是异步打包并打开对应文件夹
AssetDatabase.ExportPackage(folderList.ToArray(), fileName,
ExportPackageOptions.Recurse | ExportPackageOptions.Interactive);
}
}
全局Editor方法类
- 提取之前可能被游戏逻辑调用的方法
public static class GlobalEditorFunction
{
/// <summary>
/// 生成 Unity 包的名称
/// </summary>
/// <returns>ZFramework前缀的包名</returns>
public static string GeneratePackageName()
{
var packName = "ZFramework_" + DateTime.Now.ToString("yyyyMMdd_HH");
return packName;
}
/// <summary>
/// 把传入的字符串复制到剪贴板
/// </summary>
/// <param name="txt">传入的字符串</param>
/// <returns>传入的字符串</returns>
public static string CopyText2ClipBoard(string txt)
{
GUIUtility.systemCopyBuffer = txt;
return txt;
}
/// <summary>
/// 生成 Unity 包的名称,并立刻复制到剪贴板
/// </summary>
public static void GenerateNameCopy2ClipBoard()
{
CopyText2ClipBoard(GeneratePackageName());
}
/// <summary>
/// 打开对应路径或网页
/// </summary>
/// <param name="path">绝对路径或网页</param>
/// <returns>参数字符串返回</returns>
public static string OpenFolder(string path)
{
// 也能打开网页
Application.OpenURL(path);
return path;
}
}
总结
- 目前可以使用一键导出unitypackage包,或者使用快捷键迅速导出,可以方便之后积累自己的方法库,比如框架包导入实际项目,通过项目整合一些方法,然后写入框架文件夹,再一键导出,如此形成一个正向循环
- 将引用了Unity.Editor的方法类,置于Editor文件夹中,可以避免打包错误,同时相比宏定义更加清晰,类具有单一职责原则,而且不容易出错,不存在忘记标识# if UNITY EDITOR的可能,在遵循该规范的前提下,会大大减少# if UNITY EDITOR 宏定义的使用(绝大部分应该可以分离开)