AnimationClip结合spine直接在编辑器内预览的一种方式

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:
动画

2赞

确实实用,官方能不能提供这个功能 @boyue

mark!

@boyue

我想问一下这个文件是编辑了放在哪的