CocosCreator 之 部分暂停update、tween动画、spine动画、schedule定时器的功能汇总

引擎: CocosCreator 3.8.5

环境:Mac

语言:TypeScript

您好,我是鹤九日!



前言


项目开发中的暂停功能,我们通常使用director提供的方法,比如:

// 暂停正在运行的场景
director.pause();
// 恢复暂停场景
director.resume();

然而,此种方式会将一切运动可变的东西都停止,这是不满足部分运动、部分停止的需求的。

倘若,实现部分停止,我们可能需要考虑这么几点:

一、关于生命周期回调 update 的暂停

二、关于CocosCreator提供的 schedule 定时器的暂停

三、关于 tween 动画的暂停

四、关于 spine 骨骼动画的暂停

此篇文章将这些暂停功能的实现分享出来,先看效果:

请添加图片描述

注:效果的逻辑可能有考虑不周的地方,但依然希望对您有用!

注: 不同引擎之间的版本接口存在差异,请仔细甄别!



生命周期update的暂停、恢复


update的暂停、恢复是最为简单的,我们只需要在update接口处添加一个开关标记即可。

private _isPauseUpdate: boolean = false;
protected update(dt: number): void {
    if (this._isPauseUpdate) return;

    this._updateTime += dt;
    this.updateLabel.string = `update: ${this._updateTime.toFixed(2)}`;
}

// 按钮回调
public updatePause() {
    this._isPauseUpdate = !this._isPauseUpdate;
    this.updateTitle.string = this._isPauseUpdate ? "update恢复" : "update暂停";
}


Spine动画的暂停、恢复


spine动画的暂停、恢复也会简单,直接设置 sp.SkeletonpausedtimeScale 属性即可。

private _isPauseSpine: boolean = false;
public spinePause() {
    this._isPauseSpine = !this._isPauseSpine;
    this.spineTitle.string = this._isPauseSpine ? "spine恢复" : "spine暂停";

    // 方式1: 使用paused 
    //this.spine.paused = this._isPauseSpine;

    // 方式2: 使用timeScale:0表示引擎就不会再驱动动画帧更新,等同暂停
    this.spine.timeScale = this._isPauseSpine ? 0 : 1;
}


Tween动画的暂停、恢复


tween动画的实现,先在start处,随便添加一个无限移动的动画。

protected start(): void {
    // tween动画
    const pos = this.tweenNode.position;
    tween(this.tweenNode)
    .repeatForever(
        tween()
            .to(1, {position: v3(pos.x + 100, pos.y)})
            .to(1, {position: v3(pos.x - 100, pos.y)})  
    )
    .start();
}

它的暂停、恢复主要接口引擎提供的接口类:ActionManager

该类,官方文档有着这样的一段说明:

一、ActionManager 是可以管理动作的单例类,通常你并不需要直接使用这个类。

二、99%的情况您将使用 Tween 的接口,但也有一些情况下,您可能需要使用这个类。

针对于暂停、恢复, 主要的接口有:

接口说明
pauseTarget(target: T)暂停指定对象 所有正在运行的动作和新添加的动作都将会暂停
resumeTarget(target: T)让指定目标恢复运行
pauseAllRunningActions()暂停所有正在运行的动作
返回一个包含了那些动作被暂停了的目标对象的列表
resumeTargets(targetrs: Array)让一组指定对象恢复运行
(用来逆转 pauseAllRunningActions 效果的便捷函数)

注:简单理解:tween的暂停、恢复,支持单一和多个对象。

private _isPauseTween: boolean = false;
private _actions: any[] = [];
public tweenPause() {
    this._isPauseTween = !this._isPauseTween;
    this.tweenTitle.string = this._isPauseTween ? "tween恢复" : "tween暂停";

    // 是否暂停所有
    const isAll = true;
    // 动作类接口
    const actionMgr = TweenSystem.instance.ActionManager;
    if (!isAll) {
        // 停止单一
        if (this._isPauseTween) {
            actionMgr.pauseTarget(this.tweenNode);
        } else {
            actionMgr.resumeTarget(this.tweenNode);
        }
    } else {
        // 停止所有
        if (this._isPauseTween) {
            this._actions = actionMgr.pauseAllRunningActions();
        } else {
            if (this._actions && this._actions.length > 0) {
                actionMgr.resumeTargets(this._actions);
                this._actions = [];
            }
        }
    }
}


Schedule定时器的暂停、恢复


开始之前,先在 start 处构建一个定时器:

private _scheduleTime: number = 0;
protected start(): void {
    // schedule定时器
    this.schedule((dt: number) => {
        this._scheduleTime += 0.1;
        this.scheduleLabel.string = `schedule:${this._scheduleTime.toFixed(2)}`;
    }, 0.1, macro.REPEAT_FOREVER);
}

定时器的暂停、恢复主要借助引擎提供的调度器,即:director.getScheduler(), 主要接口有:

接口说明
pauseAllTargets()暂停所有对象的所有定时器。 不要调用这个方法,除非你知道你正在做什么。
pauseAllTargetsWithMinPriority()暂停所有优先级的值大于指定优先级的定时器。 你应该只暂停优先级的值大于 PRIORITY_NON_SYSTEM_MIN 的定时器。
resumeTargets()恢复指定数组中所有对象的定时器
pauseTarget(target: ISchedulable)暂停指定对象的定时器。
指定对象的所有定时器都会被暂停 如果指定的对象没有定时器,什么也不会发生。
resumeTarget(target: ISchedulable)恢复指定对象的所有定时器。 指定对象的所有定时器将继续工作。
如果指定的对象没有定时器,什么也不会发生。
isTargetPaused(target: ISchedulable)返回指定对象的定时器是否处于暂停状态

针对这几个接口要说明下,尤其标红部分:

一、虽然提供了 pauseAllTargets()的接口,但不要使用它。

它会停止所有定时器的使用,包含引擎内部使用的系统定时器,这样很容易导致不可预测的行为。

二、暂停功能的使用,我们只需要关注用户自定义的定时器即可。

这样便有了针对于系统定时器、用户定时器的优先级区分,这个便是PRIORITY_NON_SYSTEM_MIN优先级。

注:关于优先级的数据,我没有找到具体的索引,只能借助AI设定为-128,如有小伙伴得知,可告知下,感谢!

三、Creator的定义器:schedulescheduleOnce 是没有返回值的。

但恢复指定单一对象的接口,需要传入 ISchedulable的参数,这就尴尬了。

最后我们只能使用的接口便是:pauseAllTargetsWithMinPriority

private _isPauseSchedule: boolean = false;
private _shedulealbes: ISchedulable[] = [];
public schedulePause() {
    this._isPauseSchedule = !this._isPauseSchedule;
    this.scheduleTitle.string = this._isPauseSchedule ? "schedule恢复" : "schedule暂停";

    const scheduler = director.getScheduler();
    const PRIORITY_NON_SYSTEM_MIN = -128; // 非系统定时器的最小优先级
    if (this._isPauseSchedule) {
        // 暂停所有优先级的值大于指定优先级的定时器
        this._shedulealbes = scheduler.pauseAllTargetsWithMinPriority(PRIORITY_NON_SYSTEM_MIN);
    } else {
        if (this._shedulealbes && this._shedulealbes.length > 0) {
            // 恢复指定数组中所有对象的定时器
            scheduler.resumeTargets(this._shedulealbes);
        }
    }
}

注:此处的代码,针对于优先级的设定存在不严谨之处,请注意使用。



最后


关于功能的部分暂停、恢复到这里就结束了。

这里小小的延伸一点: setInterval是浏览器的原生 API, 无法通过调度器来控制。

关于setInterval的临时解决方案便是在回调用设置同update类似的开关来控制,虽然不能真正暂停,但也能模拟使用下,代码就不再编写了。

理解可能有误,期待您的指出;如果觉得文章不错,欢迎点赞、留言和分享!

我是鹤九日,祝您生活愉快!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鹤九日

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值