-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adjustment of spine runtime basic API #109
Comments
感觉整体结构是不是有点奇怪,应该先整体看 Spine 组件需要给开发者提供什么能力,再考虑如何实现 |
addSeparateSlot 这个不要的结论推导不是很理解,首先应该是考虑开发者是否需要吧?而不是 “目前这个方法可以删除,没有对应的插件实现绘制顺序的调整,这个暂时可以删除掉”? |
这里我的理由确实没有写清楚,我补充一下。核心原因我认为是引擎API 应该与的编辑器功能有关联,unity 提供了一个专门的拆分组件,这个方法是为这个组件服务的。 |
好的👌 RFC在结构上确实需要改进,我增加一个章节对 spine 提供的整体能力增加调研对比,以归纳出核心且必要的一些能力。基于整体API的对比调研结果,再结合我们的API来提出修改方案。 |
背景
过去的 Spine-runtime API 存在多个版本(SpineAnimation, SpineRenderer),杂乱且不统一。在 1.3 里程碑中,对 API 进行了初步的整合与优化。然而,当前的 API 距离最终版本仍有差距,尚需进一步的审视与优化,以确保在未来能够更好地满足开发者的需求,提供更优的开发体验。
整体 API 调研
为确保新的 Spine-runtime API 版本能够在功能性和易用性上满足开发者的需求,本次调研分析了多个流行的 Spine 运行时,包括 :Unity、Unreal Engine、Godot、Pixi 、Cocos 和 LayaAir。通过比较这些引擎在 API 设计、功能实现、扩展性等方面的不同,我们可以借鉴他们的优点,并识别现有 API 的改进方向,以实现更高效的一致性和可用性。
以下是各引擎的调研整理,****在对比不同的引擎 API 后,会整理出核心且必备的 API,然后结合目前 Galacean runtime 的 API 提出改造方案。 以下是不同引擎API的调研和总结。
Unity Spine Runtime
概要
API 概览
Unity 对外暴露的 API 非常多且杂,但是基本上可以划分为以下几个类别:
Unity Spine Runtime 提供了 SkeletonAnimation 和 SkeletonRenderer 两个类来实现 spine 动画的渲染。二者是继承关系,且均为MonoBehaviour。
详细 API 如下:
SkeletonAnimation 对外暴露的 API 有:
SkeletonRenderer 对外暴露的 API 有:
在Unity 编辑器中
组件检查项中,和渲染有关的参数都统一放到了 Advanced 折叠块下,检查项与 API 是对应的~
除此之外的检查项有:
后三个是 unity 的 2D 配置项,以上检查项在API中都能够找到对应项。
总结:
Unreal Engine
概要
API 概览
UE 同样提供了渲染组件和动画组件两个组件,蓝图提供的节点与组件暴露的方法略有差异。UE 暴露的 API 看似很多,但是都是基于 spineCore 的 AnimationState 和 Skeleton 对象的方法。
API 可以划分为这几类:
详细 API 如下:
动画组件 API:
蓝图节点:
以上都是基于 SpineCore 的 AnimationState 的 API 提供的蓝图节点
代理方法:
渲染组件 API:
蓝图方法:
以上蓝图节点都是基于 SpineCore.Skeleton 对象提供的
在UE 编辑器中
UE 编辑器组件中只有这四个选项,分别是骨架数据,atlas 数据,以及两个预览的名称。
在蓝图编辑器中,能够使用组件暴露的蓝图节点:
总结:
Godot
概览
API 概览
Godot 的 API 可以分为以下几个类别:
详细 API 如下:
事件:
以下四个是四种混合模式下的材质设置与获取:
其他对外 API 是一些 debug 方法。这里就不贴了。
在编辑器中:
godot 提供的检查项有:
检查项与API对应
总结:
Pixi
概览
API 概览
Pixi 的 API 可以分为:
详细 API:
PS: hackTexture 相关的方法只在 Pixi-spine 中存在。官方运行时并未提供相关的动态替换纹理的方法。
总结:
Cocos
概览
API 概览
cocos 暴露的 API 非常多,主要分为以下几类:
详细 API:
在cocos编辑器中:
对比对外暴露的 API 能够发现,Animation,DefaultSkin 没有对应的 API。这两个检查项的 API 是 internal 的。
其余的检查项与 API 均能够对应。
总结:
Laya
概览
API 概览
Laya 的 API 主要分为以下几类:
详细 API:
在Laya编辑器中:
Laya 的检查项较少,只有:
上述检查项都能找到与之对应的 API。ExternalSKins 是比较特殊的一个 API,仅laya提供了这个API。但是文档中没有介绍这个检查项的功能。
总结:
根据以上 6 个引擎的 API 以及编辑器的检查项的调研,能够总结出必备的 API 类型有:
除此之外,其他类型的 API 都是额外的一些高级功能或者特殊处理。
下文中,会结合调研,总结目前 Galacean spine 的 API 要改什么,要加什么。
目前的 API 梳理
目前运行时对外的 API 如下:
根据上述调研结果,对已有 API 进行分类:
API 调整总览
spine 骨架/动画操作:目前的两个 API 能够满足开发者的诉求了,无需调整。理由如下:
骨架与动画操作底层都依赖 SpineCore 的 Skeleton 和 AnimationState API,直接暴露出这两个对象的是最简单最直接的方式。这种情况下,用户没有额外的学习成本。
调研的引擎中,cocos ,laya,ue 没有直接暴露 Skeleton 和 AnimationState 这两个对象,而是直接暴露的二次封装的方法,他们这样做的原因如下:
ue 是为了提供蓝图节点,暴露的函数与原本的 API 基本一致。
cocos 由于增加了动画烘焙缓存,所以无论是动画播放,还是附件替换,这些都进行了二次封装,但是功能和原生 API 是一致的。
laya 的纯粹是为了提供使用方法,部分方法会关闭 laya 的性能优化开关,所以也进行了封装。
综上,如果引擎没有特殊的实现或者操作,无需额外封装新的方法,cocos 和 laya 封装的方法,函数名/功能和原生的SpineCore API 也是一致的。
生命周期相关方法:缺少较底层的生命周期方法,但使用场景不多,可以暂时先不增加。Spine 默认的动画事件足够满足开发诉求了。
渲染相关参数与方法: tint 和 pma 缺失,pma 非常需要,tint 不太常用。pma 本次里程碑会支持
其他:本次会增加一些性能优化的参数开关(预乘)
API 修改调整方案
以下是各 API 调整的详细方案,每个API 都包含修改的方向以及背后对各个引擎针对该API 的调研。
resource
修改方向
具体方案:
针对资产 API 的调研:
Unity 在运行时能够做到动态切换,这段代码在 spine 实例化时也会调用。
除此之外,还提供了运行时实例化的功能,但是并不推荐这种方式:https://zh.esotericsoftware.com/spine-unity#%E9%AB%98%E7%BA%A7-%E8%BF%90%E8%A1%8C%E6%97%B6%E5%AE%9E%E4%BE%8B%E5%8C%96
优势: unity 提供了对外的 API 实现实例化以及动态替换素材,灵活性强,能够满足各种需求。
劣势:动态替换素材会重新构建 mesh,反复切换素材是比较 waste 的操作,严格上来说,不算劣势。这也是不同运行时都会面临的问题。
Pixi 没有暴露资产相关的 API 不可动态替换。只能通过 from 静态方法或者 contructor 来创建。
优势:from 静态方法能够很好的和 Pixi 的 Loader 相结合使用,但是前提是需要预加载 skeleton 和 atlas 素材。而通过 contructor 创建,则需要用户手动创建出 SkeletonData,灵活性更高但是需要引入更多的 paser 来预处理素材。
劣势:无法动态替换素材,预加载的方式非常多,为了实现预加载,多 page 需要在 loader options 传入非常多额外参数。
用户学习成本很高。具体可以参考官方提供的 example ,非常繁多。
ue 的实现其实与 unity 类似,但是官方文档中,没有告知用户运行时实例化和动态修改素材的方式。在代码中,还是存在对应的接口,
这使得能够通过蓝图,替换素材:
但是官方文档中提供的方式还是直接设置素材,而非蓝图:
优势:ue 的优势主要体现在能够结合蓝图一起使用。
劣势:与 unity 一样,当切换了素材时,同样会重新 buildMesh。
godot 的 SpineSprite 同样提供了方法动态修改 resource
语意非常明确: set_skeleton_data_res,并且提供了相应的回调函数。
在 SpineSprite.cpp 的回调实现中,修改素材后,同样会重新创建 Mesh
优势:与 unreal 和 unity 相同,godot 提供的组件功能也非常全面与灵活。但是文档中并没有告知用户动态替换素材的方法。
劣势:与 unreal 和 unity 相同,切换素材时,同样会重新初始化,构建 mesh。
都有动态替换 spine 资产的 API。但是 laya 的 API (source, url)语意不太明确
cocos:
素材加载:
https://docs.cocos.com/creator/3.8/manual/zh/asset/spine.html#%E5%8A%A0%E8%BD%BD%E6%96%87%E6%9C%AC%E6%A0%BC%E5%BC%8F%E7%9A%84-spine-%E8%B5%84%E6%BA%90
laya:
cocos和laya 也能够运行时替换 spine 素材,但是都需要重新load素材,然后调用API加载。重新加载时,也都会重新buildMesh。
state & skeleton
这两个 API 放到一起说。
修改方向
调研:
unity 组件对外暴露了 spine-core 的这两个对象,左边是动画组件,暴露了 state:spine.AnimationState 对象,右边是渲染组件,暴露了 Spine.Skeleton 对象
ue 的实现和 unity 一样,也有两个组件一个是 skeleton 组件,一个是 animation 组件。后者继承于前者。
同样,也暴露了 skeleton state 的对象的 API,但是不是以对象的方式。而是把 API 拍平了挨个暴露出去,并且对于原本的 API 有二次封装,目的是为了更好地整合 Spine 动画系统,利用 UE 的内置特性,如蓝图、反射系统和垃圾回收机制。
提供了 get 方法来获取这两个对象。
API 与 spine-core 一致。
暴露了 spine-core 的 Skeleton 和 AnimationState 对象。
没有暴露这两个对象,但是基于这两个对象的方法,封装了常用的几个函数,比如:播放动画,替换附件,设置皮肤,还有一些 util 方法,比如:骨架归位,修改骨骼 Transform 等。之所以二次封装的理由上面也提到了,是因为运行时有一些额外的实现(动画烘焙,性能优化)。
addSeparateSlot
改造方向
目前这个方法可以删除,运行时的 API 需要在编辑器有对应功能,在添加分割插件前不需要这个 API
根据目前的调研,目前仅有 unity 提供了类似方法,用于处理一些特殊的遮挡情况:
针对拆分功能的调研
unity 的渲染组件中包含一个参数:separatorSlots
在 separatorSlots 中的插槽会用于单独创建一个独立的 subMesh。这个参数会被插件组件 SkeletonRenderSeparator 使用。SkeletonRenderSeparator 能够设置分离槽位的渲染顺序。
defaultState
改造方向
调研的引擎中,都有对应的参数来设置初始化的动画状态。不过部分引擎提供的参数只能够用于编辑器预览,无法应用到运行时。
调用后,个人认为,这些初始化的参数,直接压平放到运行时不合理,会让用户觉得这是提供出来用于修改皮肤和动画的的util API。
在保证初始化功能的前提下,为了不让用户对 API 有混淆,保留 defaultState 这一层,收拢所有初始化相关的参数。
针对默认状态 API 的调研:
unity 也能够设置初始化的 spine 状态,对应的 API 分别是:
AnimationName,还有一个单独的 loop 参数
initialSkinName
该参数在实例化时,会生效,用于设置 spine 的初始皮肤。
缩放只能够通过 flip 来设置初始的正反。
ue 没有提供对外的接口设置初始化的动画和皮肤,但是提供了两个设置项,用来预览动画和皮肤。
godot 没有提供初始化的 API ,而是提供了 preview_skin,preview_animation 用于设置预览的皮肤和动画。
如果脚本没有更新皮肤和动画,那么会直接应用 preview 这里设置的属性。
但是经过我测试,动画并没有应用成功,而且还搜索到类似的 bug:EsotericSoftware/spine-runtimes#2530
没有提供动画,皮肤的初始化接口
没有对外暴露 初始化API,但是提供了内置的 API 且对应编辑器的接口:包括 Animation, SkinName。但是初始动画 的loop 则直接对外暴露。
有 animationName,loop,skinName 三个 getter setter,用于设置皮肤与动画。
setting
setting 目前管理了几个渲染相关参数,有useClipping(是否开启裁减) 和 zSpacing(层之间的间隙)。
改造方向
针对渲染参数的调研:
unity
类似的渲染参数是直接平铺在 Renderer 内的。
ue
有一个 DepthOffset 参数但是没有暴露出来是固定值
没有类似参数,阅读了代码似乎是靠 index 顺序来控制绘制顺序的
没有这两个参数,z 轴顺序是靠 mesh 的 zIndex 。
有一个 tint 开关参数,没有其他的渲染参数了
没有类似的渲染参数
新增实例化 API
目前只提供了 API 替换 spine 的 resource,但是没有提供 API 进行 atlas 的替换。
altas 素材的替换是常见的需求,详见 spine forum 帖子:
https://zh.esotericsoftware.com/forum/d/26252-swapping-atlases-based-on-screen-resolution
https://zh.esotericsoftware.com/forum/d/15659-how-to-change-the-quotactivequot-atlas-asset-at-runtime/2
https://zh.esotericsoftware.com/forum/d/18098-runtime-change-spineatlasasset/3
由于 1.3 没有实现 Spine atlas 素材的单独上传,所以替换 atlas 也没有实现。
调研
unity:
unity 提供了一个 createRuntimeInstance 方法来创建一个 SkeletonDataAsset 对象,接收 skeleton 文件和 atlas 图集文件,创建新的SkeletonDataAsset:
优势:提供了方法在运行时创建并替换 spine 动画资产,灵活性强。
劣势:但是,运行时创建资产时,需要手动指定 skeleton 和 atlas 的关联关系。
ue:
ue 没有对外提供更新的方法,但是引擎内部有对应的实现,当 atlas 和 skeleton 两个数据发生改变时,会重新调用 GetSkeletonData 来加载并重新创建 Skeleton 和 AnimationState 对象,这和 unity 的操作是一样的。只不过 unity 会在 initialize 方法中执行加载和创建的逻辑
劣势:ue 替换素材的方法没有对外暴露,无法运行时手动创建 spine 动画资产。
godot:
goto 提供了一个 set_skeleton_data_res 方法用于设置 spine 资产。当资产修改后,会在内部调用一个更新方法,重新执行加载逻辑:
劣势:Godot 替换素材的 skeleton文件与atlas文件的关联关系无法修改,重新设置资产只能设置加载完毕的 skeleton_data_res 对象,灵活性没有 unity 高。
pixi
pixi 动态替换 atlas,需要重新加载骨架和图集素材,调用 from 方法重新创建新的 spine 动画对象。
劣势:pixi 中,skeleton 和 atlas 的关联关系,只能手动建立。由于缺乏编辑器上传流程,假设文件不对应,会导致无法渲染或者渲染出错。
cocos
cocos 替换 atlas 的方式和 unity 类似,也需要重新创建新的 skelentonData 素材:
优势:灵活性强,能够运行时修改 spine 动画资产,还可以自定义 atlas 关联的图片素材的路径。
劣势:不同资产的关联关系需要手动建立。假设文件不对应,会导致无法渲染或者渲染出错。
laya
laya 没有提供替换 atlas 的方式,只能加载新的 spine 动画素材:
劣势:与 godot 类似,无法修改 skeleton 和 atlas 文件的关联关系。
结论:
综合调研,最好的方案需要支持以下功能:
4.能够加载已经建立了关联关系的素材
5.提供能力手动建立素材间的关联关系
具体方案:
1和2目前已经支持:
Galacean 的编辑器资产和运行时使用的资产是通过 Loader 来完成转化的。目前skeletonDataAsset,spineAtlasAsset都有对应的 Loader。即已经存在方法在运行时创建运行时使用的资产了。( 这种情况下,素材的关联关系已经在上传素材时就确定好了)
这几种资产的运行时资产如下:
skeletonDataAsset 的运行时资产是SkeletonDataResource
spineAtlasAsset 的运行时资产是TextureAtlas
texture的运行时资产是 Texture2D
3.提供新的创建运行时使用的资产的方法,并且支持手动建立资产关联关系。
createSpineResource(skeletonFile: string, atlas: TextureAtlas ): SpineResource {}
crreateTextureAtlas(atlasFile: string, textureFiles?:string[]): TextureAtlas {}
额外科普:
为什么 spine 动画再替换 atlas 后,需要重新初始化构建 mesh 呢?
重新初始化的原因如下:
第一条是最关键的一点原因。Spine 动画是基于 SpineCore.SkeletonData 数据对象来创建的,SpineCore.SkeletonData 则是基于 SpineCore.TextureAtlas 创建的。也就是说,Spine的动画资源里,Spine 的骨架和atlas两个资源是绑定在一起的。所以如果需要替换图集,需要重新创建 SkeletonData 对象,并重新进行初始化。
替换图集后,虽然顶点位置不变,但是UV很可能发生变化。这是因为每个图集中可能存在不同的纹理布局(Atlas Region),新图集的区域和旧图集的区域可能不一致。如果不重新构建mesh来适应新的UV坐标,可能会出现错误的纹理映射。
Spine 通常是使用一个大buffer来容纳顶点,uv,颜色数据的,所以UV更新时,需要重新构建 mesh
第一条提到过Spine的动画资源里,Spine 的骨架和atlas两个资源是绑定在一起的,如果图集对应的纹理个数发生变化,那么肯定要重新替换 Spine 动画资源。
至于为什么要重新构建mesh,
重新初始化后,相当于替换了一个新的 spine 素材。buffer 数据肯定会发生变化,所以调研的6款引擎都重新构建了新的 mesh。
那么可以针对替换 atlas 这种换肤场景,进行优化(不更新顶点)吗?
没必要。
生命周期方法
原生的几个方法已经能够满足开发需求了,高级的代理方法根据调研,目前只有 Unity 和 Godot 有提供。两个引擎提供的代理方法也不一样。所以现在就新增额外的生命周期方法不合适。暂时不额外添加。
The text was updated successfully, but these errors were encountered: