引擎: 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.Skeleton
的 paused 或 timeScale 属性即可。
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的定义器:schedule
和 scheduleOnce
是没有返回值的。
但恢复指定单一对象的接口,需要传入 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类似的开关来控制,虽然不能真正暂停,但也能模拟使用下,代码就不再编写了。
理解可能有误,期待您的指出;如果觉得文章不错,欢迎点赞、留言和分享!
我是鹤九日,祝您生活愉快!