在本文笔者将带大家走完 UPM(Unity PackageManager) 插件包的开发流程,在本文你将学会 UPM 插件包的正确构建与调试流程、GitHub发布、插件更新与下载使用。从此Github 将是你的超级在线插件包~
友情提示:建议从下往上翻, 或者上半部分快速翻阅,毕竟上面的内容较为过时了~
Tips :Unity 2018.3 及之后版本, Unity Package Manager (UPM) 已经支持 Git 了.
https://forum.unity.com/threads/git-support-on-package-manager.573673/
这个更新使得我们能够快速的安装那些托管在诸如 GitHub,Gitlab,Gitblit 等代码托管服务上的插件包。
这样,只要给个git链接就能安装插件/更新插件了,并且这个工作流可以让插件的开发和维护与用户工程的开发文件完全隔离,做到不污染用户工程又能将插件用于实际开发中,UPM 提供的这种便利在不远的将来将是 Unity developer 的一场狂欢,并且这一天并不遥远。
言归正传,下面我们创建一个 UPM 插件包吧~
本教程中,我们将开发一个名为 “RotateMe” 的插件包,它包含一些用于驱动游戏对象旋转的组件。
本文不涉及远程仓库的创建以及Git 安装
简要步骤
- 像往常那样开发。
- 添加 asmdef 文件
- 添加 package.json 和必要文档
- 执行"git subtree split" 和tag命令
分支策略
在本教程,我们使用 2 个分支来开发这个插件包。看个人喜好,如有必要可以增加分支(比如:develop 分支)。
- master 分支: 插件包开发环境,也就是一个正常的Unity工程。
- upm 分支: 插件包发布的分支,也是供 UPM 读取和安装的分支。
请注意,不能直接往 upm 分支提交,也不要使用 git branch upm
的方式手动创建此分支。
目录结构
UPM包的公共目录结构如下:
将上述插件包资源使用一个根节点统筹之,这个文件夹建议以插件名来命名,如下:
怎么样,是不是就像往常开发那样的流程呢?
开发插件包的过程无不透露着熟悉的味道哈。
开发插件包
OK,现在我们在 "master" 分支中开发并调试我们的插件。
首先撰写一个用于驱动物体旋转的示例代码并放在这个文件夹下:
Assets/RotateMe/Scripts/RotateMe.cs
示例代码如下:
using UnityEngine;
public class RotateMe : MonoBehaviour
{
public float angularVelocity = 10;
void Update ()
{
var rot = Quaternion.AngleAxis (angularVelocity * Time.deltaTime, Vector3.up);
transform.localRotation = rot * transform.localRotation;
}
}
当然,别忘了添加一个 Demo 场景。
要知道 Demo 场景可是一个可交互的手册呢,有了它用户就可以轻松的掌握怎么使用插件咯!
添加 asmdef 文件
如果要开发包含脚本的插件包,这边建议您添加一个asmdef文件的呢,亲~
https://docs.unity3d.com/2018.3/Documentation/Manual/ScriptCompilationAssemblyDefinitionFiles.html
这个 asmdef 文件能将它所在的文件夹及其子文件夹的脚本打到一个独立的程序集中,表象上就是这些个脚本打到了独立的 dll 中了。
简单的说下 asmdef 文件的优势.
- 更短的编译时间
- 把"internal"访问修饰符用到了极致(要知道以往的源代码插件,因为与用户脚本编译在了同一个程序集,所以它的 Internal 修饰符并没起到应有的作用,暴露了太多,就是一个掩耳盗铃的迪米特法则罢)
- 允许使用 unsafe code
- .dll 文件可以指定特定的程序集引用。
如下图,如果你勾选了 "Test Assembly",程序集只在编辑器下生效而不会出现在打包的程序中。
搬运的原教程中没有涉及到到 AssemblyDefinition 的相互引用关系,故而笔者在这里补充下。
目录管理
-
建议使用Editor 文件夹汇总所有的编辑器脚本,而将运行时的脚本使用 Runtime 文件夹汇总,参考 TextMeshPro 插件包的结构:
- 在Editor 和 Runtime 文件夹下各自创建一个 AssemblyDefinition 。
-
Editor 文件夹下的 AssemblyDefinition 中 Platform 只能选择 Editor,并且 Reference 必须添加上 Runtime 中的那个 AssemblyDefiniion ,依旧参考 TextMeshPro :
添加文档
UPM 插件包需要一个 package.json 包体描述文件,保存在如下路径:"Assets/RotateMe/package.json".
(插件根节点)
{
"name": "com.coffee.rotate-me",
"displayName": "Rotate Me",
"description": "Rotate the object!",
"version": "1.0.0",
"unity": "2018.3",
"license": "MIT",
"dependencies": {
}
}
更多详情请见: https://docs.npmjs.com/files/package.json.
README.md, CHANGELOG.md, LICENSE.md 都可选但可以给用户带来更多信息。
将这些文档放在"Assets/RotateMe" 文件夹.
当然也可以放在"Assets/RotateMe/Document~",需要注意的是文件夹名称以~
结尾不会显示在Project窗口
完成开发
完成了上面的几步,我们的插件包就算是完成啦。
最终 "master" 分组看起来就像这样:
(如果你暂时不考虑 UPM ,可将这个文件夹导出成 unitypackage 发布)
发布UPM包
如果想要发布 UPM 包,我们必须通过如下git 命令把插件包所在的文件夹创建为 subtree。
git subtree split --prefix=Assets/RotateMe --branch upm
这个命令将 "Assets/RotateMe" 目录放到“ upm” 分支。
通过这一步其他目录及文件不会出现在 upm 分支中。
接下来,为这个分支添加一个 tag 并推到远端。
推荐使用 semver作为 tag 的命名规则。
git tag 1.0.0 upm
git push origin upm --tags
大功告成!
这样,我们的 “RotateMe” UPM包就发布好了!
最终,分支图像这样子:
安装 UPM 包
现在,我们试试把 RotateMe 使用 UPM 安装到其他工程中。
找到 "Packages/manifest.json" 插入如下一行:
{
"dependencies": {
"com.coffee.rotate-me": "https://github.com/mob-sakai/RotateMe.git#1.0.0",
...
},
}
回到编辑器 Unity 就能自动加载咱们的插件啦,就像这样:
或者使用 UpmGitExtension来安装。
版本更新
- 在 "master" 分支开发和调试然后在 package.json 更新版本信息
- 执行 "git subtree split"
- 添加 tag 然后 push。
如此简单~
你也可以把已经存在的仓库使用同样的操作将其适配 UPM 。
本教程测试用的仓库如下,不受限使用。
https://github.com/mob-sakai/RotateMe
总结
仅需几步,就能将 git 仓库直接变得支持 UPM。
如果 Github 上的Unity 工程都支持 UPM 那会有多棒!?
现在,就把你工程的 UPM 支持搞起来吧~
2020.3.4补充:
- 使用 file 协议,形如:
"com.bshsf.commoncode":"file:../../Common"
,可以将UnityPackage包放在本地,这样多个工程就可以使用一个公共代码,也方便同步修改,比如在socket通信的客户端与服务端使用这种方式同步消息数据结- 来自笔者的示例。- file 协议中,默认的根节点文件夹是工程目录下与Assets 文件夹同级的 Packages 文件夹,也就是说如果你的Pacakge路径写成
file:Common
,那你的插件包请放在Packages文件夹下。 - 一般来说,我们会把插件包放在工程之外,方便其他工程引用,可以使用2个"../" 往上走2个目录层级,比如
"com.bshsf.commoncode":"file:../../Common"
表示这个插件包是跟工程根目录同级的了。
- file 协议中,默认的根节点文件夹是工程目录下与Assets 文件夹同级的 Packages 文件夹,也就是说如果你的Pacakge路径写成
- 如果你安装了git 并设置了git的 Path环境变量,但是Unity还是报相关错误,这可能是因为你的环境变量路径没有指定正确,64位Windows 10 我给的参考路径是:
"C:\Program Files\Git\mingw64\libexec\git-core"
,非默认安装路径的自行合理拼接 。
2020年10月26日 更新
在新版的Unity中(笔者使用的是Unity 2019.4.12),PackageManager 支持直接输入 git 链接了,这样带来两个好处:
- 不需要手动改 package.json
-
依旧支持打tag 做插件包的更新哦!
2021年2月28日 更新
随着 UPM 的日趋完善,现在这个工具已经支持配置自定义 Scope Registry 了,如此一来便解决了包的依赖问题了。
安装目标插件时,UPM 会自动根据你声明的依赖包 id 从你配置的 Scope Registry 服务器上拉取依赖的包。
目前,OpenUPM 作为野生 Unity 插件包的集聚地已然是独具规模了,通过它你可以公开发布你的插件,同时还可以轻轻松松(写上依赖包ID就行)随意引用和依赖这个Registry 中的其他开发者的包。
如果你正在找这方面的教程,你可以看看这个良心之作:使用OpenUPM发布自己的Unity项目-有木酱的小屋
当然,你也可以搭建 Scope Registry 私服,MixedRealityToolkit-Unity就是微软自己的Scope Registry,我相信你也可以搭建成功的(PS:OpenUPM 开源哦~)。
另外,你更可以通过查找 NPM 私服相关的教程和开源项目来搭建 UPM 私服,因为他俩本质上没区别,有很多网友就是这么做的,并且有迹可循 Unity 官方的 package 甚至都有在 npm 上托管。
UPM Scope Registry 在哪儿设置:
2021年4月2日更新
在有些没有外网的公司,UPM 会报错:Cannot perform upm operation:connect ETIMEDOUT 35.238.138:443 [NotFound]
以及一个 Error Searching for packages
报错,如图示。
可以参考这个连接尝试去解决 : 自己搭建Unity Package Server - 知乎
2021年8月27日更新
有朋友问我提交新版本可不可以不打 tag 值,开发阶段频繁更新 tag 对同事不友好。
回答是可以不打,因为打 Tag 主要只是告诉 git 插件目录在哪儿,其次才是附加值:标记插件版本、触发 UPM 加载
其实,笔者知道的,要访问插件目录,不限于这种https://github.com/mob-sakai/RotateMe.git#1.0.0
带 Tag 形式的 git 链。
还有以下 3 种形式:
- 使用形如
https://github.com/mob-sakai/RotateMe.git#upm
通过直接指定分支名称。 - 使用形如
https://github.com/wlgys8/LitTask.git
不带 tag 的 git 链,其特点是插件目录本身即为 git 仓库。 - 使用形如
https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask
的带 QuaryString 参数的 git 链 (需要新版本 Unity 以支持对 querystring 参数解析)
↑ 鉴于笔者并非 git 精通,故而只能列举四种方式,毋需置疑,这些肯定也只是冰山一角。如果笔者见到了新的方式亦会慢慢补全↑
关于终端用户不友好,遵循这种工作流就不会产生困惑了:
对于开发者,累计功能点阶段性推版本
对于终端用户,小迭代自发更新,大迭代提醒强制更新。
最后
- 还是建议大家使用 Tag ,因为毕竟相较于另外2种形式,它能够正常触发 UPM 更新
- 其他 2 种形式需要自己找方法触发更新(比如一顿删除再粘贴 git 链)。
- 除第三种方式,另外 3 种方式是支持一个仓库存多个插件的,自己扩展一下也能实现插件之间的引用。
2022年1月22日更新
上一次更新说到的 UPM 支持 4 种协议,现在可以更新一下了,下面是更完善的支持的协议说明:
官方中文手册 :https://docs.unity3d.com/cn/current/Manual/upm-git.html-
另外,Unity Registry 托管的插件包支持直接将形如
com.unity.learn.iet-framework@2.0.0
这种 【包ID+版本】链接写入到UPM git url 中获取插件包,下面是参考链接以及其上下文截图:
Tutorial Authoring Tools | 1.0.0
2022年5月3日更新:
Unity 已经开发出了 UPM Package 开发工具,只需要简单一步就能够创建 Package 大致框架,剩下的仅仅是代码的搬运了。
目前,这个 UPM 开发工具还是预览阶段,有可能在 UPM 的 Unity Registry 中还看不到。
点击 + 号 点击“Add Package from git” 粘贴上 com.unity.upm.develop
点击 “Add” 即可安装。
使用见下图,只需要点击图示菜单即可构建一个 package 环境
预设 Package 开发环境如下,可见留给用户的工作量不多了:代码+Readme 补全
同时 UPM 包开发工具提供了一套校验性质的功能点入口,如图示,依次是:单例测试、包有效性校验(检测包配置是否合理)、真机导入测试、包的发布。
2022年6月14日更新
还是要说一下,除了 .asmdef
文件,Unity 还给了一个 .asmref
文件,全称为: Assembly Definition Reference,这个文件能够解决咱们在架构插件时 .asmdef
不能跨文件夹的痛点。
有了 .asmref
文件,可以将各处的文件夹的源文件打进一个程序集中,很多时候避免了目录结构的非必要重构。
使用也是很方便,只要你对着文件夹点右键,选中 Assembly Definition Reference ,接下来的操作便能够心领神会了。
2022年10月13日更新
插件中部分代码想要选择性的参与编译,可以通过在 Assembly Definition File 中 的 Version Define 自定义宏,并选择宏开启的条件,条件一般为:
- 在特定的程序集存在的情况下启用宏(图中 Resource 展开选择即可)
- 在特定版本的程序集存在的情况下启用宏,如对版本不做限制,输入
0.0.0
或 “0”或者留空即可
注意:
- 笔者选择
com.unity.modules.ui
仅作演示,宏:UNITY_UGUI_ENABLED
也是随手写的,用户需自行修改为存在性存疑的程序集 - 当我们按上图所示修改此 asmdef 文件后,使用
UNITY_UGUI_ENABLED
即可约定哪些代码是准备选择性编译的啦。 - 如果确信程序集存在且版本正确,但是宏未启用,请为插件重新生成vs项目,如下图所示(再不行重启 Unit y 和 IDE(如 VS),哈哈哈)
如果想要自己整理的 upm 插件导入时自动安装依赖(的 upm),只需要在你的插件的 package.json 的 dependencied
成员中加入依赖包的 id 和 版本号即可,如下图所示。
Tips:
- 如果版本不想写死,那可以用
default
代替具体的版本号试试吧 - git url 的依赖应该暂时不支持,大家可以试试,Unity 不停的更新,指不定有惊喜呢?
2023年5月25日 更新
本次更新用于解决如何将已经预编译好的程序集(已经存在的程序集)关联到 assembly definition file (.asmdef文件)中呢?
实现上面的需求很简单,Unity 官方单例测试框架中就有涉及到,下面通过三张图片来说明:
常规 | 带预编译 dll列表 | 实现方式 |
---|---|---|
没有指定预编译程序集 | 预先制定了依赖的预编译程序集 | 实现方式仅仅是添加了一个图示json成员 |
Tips:只有勾选 Override References 才会显示这个下拉菜单。
扩展阅读
- Unity - Manual: Creating custom packages
- Unity - Manual : Git URLs - Unity手册:git 链接格式
- UpmGitExtension
- semver
- 为 Unity Package Manager 管理的插件配置示例 - 简书
- Open Source Unity Package Registry (UPM) | OpenUPM
- 利用asmref机制无侵入的实现将我们的程序变为其他程序集的友元程序集
本文上部分内容由笔者搬运并翻译自 Tutorial: How to develop a package for UnityPackageManager