当前位置: 首页 > article >正文

Unity之一键创建自定义Package包

内容将会持续更新,有错误的地方欢迎指正,谢谢!
 

Unity之一键创建自定义Package包
     
TechX 坚持将创新的科技带给世界!

拥有更好的学习体验 —— 不断努力,不断进步,不断探索
TechX —— 心探索、心进取!

助力快速掌握 自定义Package 快速创建

为初学者节省宝贵的学习时间,避免困惑!


前言:

  在之前的文章从零开始创建Unity自定义包Package:一步一步实现您的功能,我已经详细的介绍了该如何一步一步的去创建自己的自定义Package包,但是我相信有的小伙伴还是觉得文章很长不想看,或者觉得创建一个自定义包很麻烦,又要创建包,又要导入包的。

  今天小伙伴们可以轻松的去做这件事了,你可以直接一键的去生成包和导入包到Unity,真正的做到了省心、省时、省力,接下来就让我们一起来看怎么实现的吧。

TechX 教程效果:

在这里插入图片描述


文章目录

  • 一、绘制package创建器面板
  • 二、创建包
    • 1、创建文件夹和Assembly Definition
    • 2、创建包清单文件 (package.json)
    • 3、创建CHANGELOG.md
    • 4、创建README.md
  • 三、安装包
  • 四、项目地址


一、绘制package创建器面板


创建器面板的绘制基本上是package.json文件中的内容,包含大多数的包清单中的内容,包含:包名,包版本,包展示名,包描述,Unity版本,Unity发布版本号,文档地址,改变日志地址,证书地址,依赖列表,关键词列表,作者信息,案例列表等。

其中还包含了创建包时的选项,包含是否创建Scripts文件夹,是否创建Tests文件夹,是否创建CHANGELOG文件,是否创建ReadMe文件。

public class PackageCreatorWindow : EditorWindow
{
	private bool isCreteScriptsFolder = true;
	private bool isCreteTestsFolder = false;
	private bool isCreateChangeLog = true;
	private bool isCreateReadme = true;
	
	private string packageName = "";
	private string version = "";
	private string displayName = "";
	private string description = "";
	private string unity = "";
	private string unityRelease = "";
	private string documentationUrl = "";
	private string changelogUrl = "";
	private string licensesUrl = "";
	private List<Dependency> dependencies = new List<Dependency>();
	private List<string> keywords = new List<string>();
	private Author author = new Author();
	private List<Sample> samples = new List<Sample>();
	
	private ReorderableList keywordsReorderableList;
	private ReorderableList samplesReorderableList;
	private ReorderableList dependenciesReorderableList;
	
	private void OnGUI()
	{
	    DrawGUI();
	}

	private void DrawGUI()
	{
	    GUILayout.Space(5f);
	
	    GUILayout.Label("Package Config", new GUIStyle { fontSize = 20, padding = new RectOffset() { left = 5 }, fontStyle = FontStyle.Bold, normal = { textColor = Color.white } }); ;
	
	    GUILayout.Space(5f);
	
	    scrollPos = EditorGUILayout.BeginScrollView(scrollPos,EditorStyles.helpBox);
	
	    isCreteScriptsFolder = EditorGUILayout.ToggleLeft("Is Crete Scripts Folder", isCreteScriptsFolder);
	 	isCreteTestsFolder = EditorGUILayout.ToggleLeft("Is Crete Tests Folder", isCreteTestsFolder);
		isCreateChangeLog = EditorGUILayout.ToggleLeft("Is Create ChangeLog File", isCreateChangeLog);
		isCreateReadme = EditorGUILayout.ToggleLeft("Is Create ReadMe File", isCreateReadme);
		packageName = EditorGUILayout.TextField("Package Name *", packageName);
		version = EditorGUILayout.TextField("Version *", version);
		displayName = EditorGUILayout.TextField("Display Name *", displayName);
		description = EditorGUILayout.TextArea(description, GUILayout.Height(50));
		unity = EditorGUILayout.TextField("Unity Version *", unity);
		unityRelease = EditorGUILayout.TextField("Unity Release *", unityRelease);
		documentationUrl = EditorGUILayout.TextField("Documentation URL", documentationUrl);
		changelogUrl = EditorGUILayout.TextField("Changelog URL", changelogUrl);
		licensesUrl = EditorGUILayout.TextField("Licenses URL", licensesUrl);
		dependenciesReorderableList.DoLayoutList();
		keywordsReorderableList.DoLayoutList();
		autorToogleGroup = EditorGUILayout.BeginFoldoutHeaderGroup(autorToogleGroup, "Author");
		if (autorToogleGroup)
		{
		    EditorGUI.indentLevel += 1;
		    author.name = EditorGUILayout.TextField("Name", author.name);
		    author.email = EditorGUILayout.TextField("Email", author.email);
		    author.url = EditorGUILayout.TextField("Url", author.url);
		    EditorGUI.indentLevel -= 1;
		}
		EditorGUILayout.EndFoldoutHeaderGroup();
		samplesReorderableList.DoLayoutList();
	    EditorGUILayout.EndScrollView();
	
	    //创建之前要验证面板信息
	    if (GUILayout.Button("Create Package"))
	    {
	        string selectPath = EditorUtility.OpenFolderPanel("Select Folder for New Package", "", "");
	        bool isSuccess = CreateNewPackage(selectPath, packageName, out string packagePath);
	        if (isSuccess == false) return;
	        InstallPackage(selectPath, packagePath);
	    }
	}
}


二、创建包


1、创建文件夹和Assembly Definition


为创建的包创建一些需要的文件夹,比如Scripts/Editor、Scripts/Runtime、Tests/Editor、Tests/Runtime文件夹。
Scripts/Editor文件夹包含编辑器使用的功能,放置的脚本只在编辑器环境中使用。
Scripts/Runtime文件夹包含游戏运行时使用的功能,放置的脚本将在游戏运行时执行。
Tests/Editor用于编辑器测试,Tests/Runtime用于运行时测试。

/// <summary>
/// 创建包文件夹
/// </summary>
private void CreatePackageFolder(string pacakgePath, string packageName)
{
	//TODO 创建Scripts文件夹。。。。
    string scriptsFolderPath = Path.Combine(pacakgePath, "Scripts");
    Directory.CreateDirectory(scriptsFolderPath);

    //创建Editor和Runtime文件夹
    string editorFolderPath = Path.Combine(scriptsFolderPath, "Editor");
    string runtimeFolderPath = Path.Combine(scriptsFolderPath, "Runtime");

    Directory.CreateDirectory(editorFolderPath);
    Directory.CreateDirectory(runtimeFolderPath);

    // Create .asmdef files
    //这里的asmdef的文件的包名是否首字母大写??
    string editorAsmdefPath = Path.Combine(editorFolderPath, $"{packageName}.Editor.asmdef");
    string runtimeAsmdefPath = Path.Combine(runtimeFolderPath, $"{packageName}.Runtime.asmdef");

    AsmdefConfigProcess.CreateAsmdefContent(editorAsmdefPath, true);
    AsmdefConfigProcess.CreateAsmdefContent(runtimeAsmdefPath, false);
    
    //TODO 创建Tests文件夹。。。。
    string testsFolderPath = Path.Combine(pacakgePath, "Tests");
    Directory.CreateDirectory(testsFolderPath);

    string testseEditorFolderPath = Path.Combine(testsFolderPath, "Editor");
    string testseRuntimeFolderPath = Path.Combine(testsFolderPath, "Runtime");

    Directory.CreateDirectory(testseEditorFolderPath);
    Directory.CreateDirectory(testseRuntimeFolderPath);

    string testseEditorAsmdefPath = Path.Combine(testseEditorFolderPath, $"{packageName}.Editor.Tests.asmdef");
    string testseRuntimeAsmdefPath = Path.Combine(testseRuntimeFolderPath, $"{packageName}.Runtime.Tests.asmdef");

    AsmdefConfigProcess.CreateAsmdefContent(testseEditorAsmdefPath, true);
    AsmdefConfigProcess.CreateAsmdefContent(testseRuntimeAsmdefPath, false);
}

在每个文件夹创建完成之后,我们都需要在文件夹中定义一个程序集文件,注意在不同的文件夹中,程序集文件的名称是不一样的,同时也要注意,对于运行时和编辑器下的程序集平台也是不一样的,运行时的平台一般是Any Platform,而编辑器的平台是Editor。

public class AsmdefConfig
{
    public string name;
    public string rootNamespace;
    public List<string> references;
    public List<string> includePlatforms;
    public List<string> excludePlatforms;
    public bool allowUnsafeCode;
    public bool overrideReferences;
    public List<string> precompiledReferences;
    public bool autoReferenced;
    public List<string> defineConstraints;
    public List<string> versionDefines;
    public bool noEngineReferences;
}

public class AsmdefConfigProcess
{
    /// <summary>
    /// 创建.asmdef
    /// </summary>
    /// <param name="path">创建的位置</param>
    /// <param name="isEditor">适用于运行时还是编辑器下</param>
    public static void CreateAsmdefContent(string filePath, bool isEditor)
    {
        string fileName = Path.GetFileNameWithoutExtension(filePath) ;
        AsmdefConfig asmdefClass = new AsmdefConfig();
        asmdefClass.name = fileName;
        asmdefClass.rootNamespace = "";
        asmdefClass.references = new List<string>();
        asmdefClass.includePlatforms = isEditor ? new List<string> { "Editor" } : new List<string>();
        asmdefClass.excludePlatforms = new List<string>();
        asmdefClass.allowUnsafeCode = false;
        asmdefClass.overrideReferences = false;
        asmdefClass.precompiledReferences = new List<string>();
        asmdefClass.autoReferenced = true;
        asmdefClass.defineConstraints = new List<string>();
        asmdefClass.versionDefines = new List<string>();
        asmdefClass.noEngineReferences = false;

        JObject asmdefJson = JObject.FromObject(asmdefClass);

        File.WriteAllText(filePath, asmdefJson.ToString());
    }
}

2、创建包清单文件 (package.json)


每个Unity Package都必须包含一个名为package.json的清单文件。这个文件包含了有关包的元信息,如名称、版本、依赖项等。

/// <summary>
/// 创建package.json文件
/// </summary>
private void CreatePackageFile(string pacakgePath)
{
    // Create package.json
    PackageConfigProcess.CreatePackageJson(pacakgePath, GetPackageJsonContent());
}

这里定义了包清单的一些相关类型,当需要创建package.json文件时,从面板上获取到包相关信息,并生成PackageConfig实例,将该实例转换成JSON字符串写入到json文件中。

[System.Serializable]
public class Author
{
    public string name = "";
    public string email = "";
    public string url = "";
}

[System.Serializable]
public class Dependency
{ 
    public string packageName;
    public string version;
}

[System.Serializable]
public class Sample
{
    public string displayName;
    public string description;
    public string path;
}

public class PackageConfig
{
    public string name = "";
    public string version = "";
    public string displayName = "";
    public string description = "";
    public string unity = "";
    public string unityRelease = "";
    public string documentationUrl = "";
    public string changelogUrl = "";
    public string licensesUrl = "";
    public JObject dependencies = new JObject();
    public List<string> keywords = new List<string>();
    public Author author = new Author();
    public List<Sample> samples = new List<Sample>();
}

public class PackageConfigProcess
{
    public static void CreatePackageJson(string path, PackageConfig packageConfig)
    {
        JObject packageConfigJson = JObject.FromObject(packageConfig);

        string fullPath = Path.Combine(path, $"package.json");

        File.WriteAllText(fullPath, packageConfigJson.ToString());
    }
}

3、创建CHANGELOG.md


CHANGELOG.md文件是版本信息改变的日志文件,建议在每次发布新版本时更新CHANGELOG.md文件。在文件中记录新增功能、改进和错误修复。

/// <summary>
/// 创建CHANGELOG.md文件
/// </summary>
private void CreateChangeLogFile(string pacakgePath)
{
    // Create CHANGELOG.md
    string changeLogPath = Path.Combine(pacakgePath, "CHANGELOG.md");
    File.WriteAllText(changeLogPath, "# Changelog\nAll notable changes to this package will be documented in this file.\n\n");
}

4、创建README.md


README.md文件是关于该插件的介绍和如何使用的。

/// <summary>
/// 创建README.md文件
/// </summary>
private void CreateReadMeFile(string pacakgePath)
{
    // Create README.md
    string readmePath = Path.Combine(pacakgePath, "README.md");
    File.WriteAllText(readmePath, $"# {Path.GetFileName(pacakgePath)}\n\n");
}


三、安装包


在包创建完成后,会把包创建到本地的某个文件夹中,但是这个时候并没有把包安装到Unity中,按照一般的方法,在创建完成包后,可以通过Package Manager中的Add package form disk选项去文件夹中选择包的package.json文件来安装本地包。

但是在这里我们在创建完成包后,直接根据包的路径来,直接使用代码来模拟Add package form disk选择package.json文件来安装包。

注意:当我们把包直接创建到工程的Packages文件中时,那么我们就不需要通过代码来添加包到工程中,因为Unity会自动完成这一步。

/// <summary>
/// 安装包
/// </summary>
/// <param name="creteResult"></param>
/// <param name="selectPath"></param>
/// <param name="packagePath"></param>
private void InstallPackage(string selectPath, string packagePath)
{
    //如果直接创建到了Packages文件加中,那么就不需要手动添加到工程中,Unity会自动添加
    if (File.Exists(Path.Combine(selectPath, "manifest.json"))) return;

    //TODO 添加包到工程中
    PackageInstaller.InstallPackageFromDisk(packagePath);
}

从本地安装包的方式有两种,一种是从文件夹安装,要求包含package.json文件,并且符合包清单的条件,一种是通过.tgz压缩包的方式安装包。

这两种包在安装时都需要在路径前"file:"才能正确的安装包。

public class PackageInstaller
{
    /// <summary>
    /// 从本地路径安装包
    /// 1、可以通过本地文件夹安装包,格式file:pathtopackagefolder,如file:E:/UPMProject/UPM/com.fxb.test
    /// 2、从.tgz文件安装包,格式file:pathtopackage.tgz,如file:E:/UPMProject/UPM/com.fxb.test.tgz
    /// </summary>
    /// <param name="packagePath"></param>
    public static void InstallPackageFromDisk(string packagePath)
    {
        //从本地文件夹安装包
        if (Directory.Exists(packagePath))
        {
            // 构造package.json文件的完整路径
            string packageJsonPath = Path.Combine(packagePath, "package.json");

            // 检查package.json文件是否存在
            if (!File.Exists(packageJsonPath))
            {
                Debug.LogError("The provided folder does not contain a valid 'package.json' file and is not a valid Unity package.");
                return;
            }
        }
        //从.tgz文件安装包
        else if (File.Exists(packagePath))
        {
            if (!packagePath.EndsWith(".tgz"))
            {
                Debug.LogError($"{packagePath} file is not a valid Unity package.");
                return;
            }
        }
        else
        {
            Debug.LogError($"The package at path {packagePath} does not exist.");
            return;
        }

        // 构建正确的标识符
        string identifier = $"file:{packagePath}";
        // 如果存在package.json,那么这是一个有效的包,可以继续安装
        AddRequest request = Client.Add(identifier);
        CallbackFunction onAddUpdate = null;
        onAddUpdate = () =>
        {
            if (request.IsCompleted)
            {
                if (request.Status == StatusCode.Success)
                    Debug.Log($"Package from {packagePath} installed successfully.");
                else
                    Debug.LogError($"Failed to install package from {packagePath}: {request.Error.message}");

                // 移除更新回调
                EditorApplication.update -= onAddUpdate;
            }
        };

        // 注册更新事件
        EditorApplication.update += onAddUpdate;
    }
}


四、项目地址


以下是项目地址,已经整理成了Package包,有需要的小伙伴门可以自取:

https://gitcode.com/CTLittleNewbie/com.fxb.unitypackagecreator_v1.0.0/overview





TechX —— 心探索、心进取!

每一次跌倒都是一次成长

每一次努力都是一次进步


END
感谢您阅读本篇博客!希望这篇内容对您有所帮助。如果您有任何问题或意见,或者想要了解更多关于本主题的信息,欢迎在评论区留言与我交流。我会非常乐意与大家讨论和分享更多有趣的内容。
如果您喜欢本博客,请点赞和分享给更多的朋友,让更多人受益。同时,您也可以关注我的博客,以便及时获取最新的更新和文章。
在未来的写作中,我将继续努力,分享更多有趣、实用的内容。再次感谢大家的支持和鼓励,期待与您在下一篇博客再见!

http://www.kler.cn/a/415605.html

相关文章:

  • 单点登录原理
  • 基于Java的小程序电商商城开源设计源码
  • FFmpeg 简介与编译
  • 遗传算法与深度学习实战(25)——使用Keras构建卷积神经网络
  • docker 僵尸进程问题
  • 优先算法 —— 双指针系列 - 快乐数
  • 题目 3209: 蓝桥杯2024年第十五届省赛真题-好数
  • 信息学奥赛一本通 1448:【例题1】电路维修 | 洛谷 P4667 [BalticOI 2011 Day1] Switch the Lamp On 电路维修
  • 《使用Python进行数据挖掘:理论、应用与案例研究》
  • spine 动画层 动态权重
  • brew安装mongodb和php-mongodb扩展新手教程
  • 智启未来 扬帆5G:江苏移动打造“5G + 智慧教育”典范,引领教育新风尚
  • 个人博客接入github issue风格的评论,utteranc,gitment
  • Nuxt.js 应用中的 render:response 事件钩子
  • 【Java面试题】消息队列中,如何保证消息的顺序性?
  • SQL进阶——子查询与视图
  • Prophet时间序列算法总结及python实现案例
  • 关于Spring @Transactional事务传播机制详解
  • 前端面试题-1(详解事件循环)
  • Cesium CZML绘制Entity
  • 深入理解 MongoDB:一款灵活高效的 NoSQL 数据库
  • 2020 NHOI小学(C++)
  • C# yield 关键字
  • github通过修改hosts访问
  • RiceChem——用于评估大语言模型在教育领域自动长答卷评分 (ALAG) 的数据集
  • Elasticsearch集群如何实现高可用和一致性