AnimationClip结合spine直接在编辑器内预览的一种方式
最近在项目战斗开发中用到 spine,想充分利用 AnimationClip 和 spine 制作战斗技能,但看 3.8.6 那么强大居然不支持场景或 prefab 编辑过程中预览 spine。这对策划或者美术编辑战斗技能效果很不友好,很多效果微调的工作量会转移到开发同学,而且通过 AnimationClip 关键帧修改 spine 组件的 _animationIndex的方式播放动画时会有 spine 动画卡死的现象,遂做了以下的猴子补丁,希望能有所帮助。
技术实现方式
本方案采用**原型链劫持(Prototype Hijacking)**技术。通过修改引擎核心类的原型方法,在编辑器运行时动态覆盖原有实现,达到不修改引擎源码即可扩展功能的目的。
优化内容详解
1. AnimationClip 动画轨道创建绑定优化
const origincreateRuntimeBinding = TrackBinding.prototype.createRuntimeBinding;
TrackBinding.prototype.createRuntimeBinding = function(...) {
// 保留原逻辑
let res = origincreateRuntimeBinding.call(...);
// 劫持setValue方法
const originSet = res.setValue;
res.setValue = function(value: any) {
// 针对spine动画的特殊处理
if (res.target instanceof sp.Skeleton) {
// 值变化检查避免重复执行
if (value != res.getValue()) {
originSet.call(this, value);
}
} else {
originSet.call(this, value);
}
}
return res;
}
技术特征:
-
通过值比对机制防止重复调用
-
保持非spine对象的原有逻辑
-
解决动画剪辑频繁触发spine播放的问题
2. 编辑器预览增强
const originProload = sp.Skeleton.prototype.__preload;
sp.Skeleton.prototype.__preload = function() {
originProload.call(this);
// 强制刷新状态
this.paused = false;
this._updateSkeletonData();
this._updateDebugDraw();
}
实现效果:
-
激活编辑器内的骨骼数据实时更新
-
解除预览态暂停限制
-
支持所见即所得的调试体验
附扩展源代码:
// cocos-extend.ts
import { _decorator, js, sp } from "cc";
const { ccclass, executeInEditMode } = _decorator;
let TrackBinding = js.getClassByName("cc.animation.TrackBinding");
const origincreateRuntimeBinding = TrackBinding.prototype.createRuntimeBinding;
TrackBinding.prototype.createRuntimeBinding = function (target: any, poseOutput: any, isConstant: boolean) {
let res = origincreateRuntimeBinding.call(this, target, poseOutput, isConstant);
const originSet = res.setValue;
res.setValue = function (value: any) {
if (res.target instanceof sp.Skeleton) {
if (value != res.getValue()) {
originSet.call(this, value);
}
} else {
originSet.call(this, value);
}
}
return res;
}
const originProload = sp.Skeleton.prototype.__preload;
sp.Skeleton.prototype.__preload = function () {
originProload.call(this);
this.paused = false;
this._updateSkeletonData();
this._updateDebugDraw();
}
ps:
扩展后的效果基本满足了我的需求,但是任然存在一些瑕疵,例如在animationclip时间轴中循环播放的spine动画有时会只播放一次,而不是循环播放,不过这个问题影响不大。
附效果gif:
