feat: game time scale

feature/ads-smart-display
tiendat3699 2024-06-07 14:08:39 +07:00
parent 0d4aa0b801
commit 239aa5b411
5 changed files with 280 additions and 47 deletions

View File

@ -1562,6 +1562,7 @@
"__type__": "6bb6fbw5PNPYq7zksYK1kHJ",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 33
},
@ -1883,6 +1884,7 @@
"__type__": "6bb6fbw5PNPYq7zksYK1kHJ",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 41
},
@ -2466,6 +2468,7 @@
"__type__": "6bb6fbw5PNPYq7zksYK1kHJ",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 49
},
@ -3021,6 +3024,7 @@
"__type__": "6bb6fbw5PNPYq7zksYK1kHJ",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 63
},
@ -3370,6 +3374,7 @@
"__type__": "6bb6fbw5PNPYq7zksYK1kHJ",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 77
},
@ -3719,6 +3724,7 @@
"__type__": "6bb6fbw5PNPYq7zksYK1kHJ",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 86
},
@ -7124,6 +7130,7 @@
"__type__": "ce7b7e0d6BHlaRFoKBZN0h/",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 135
},
@ -7794,6 +7801,7 @@
"__type__": "e3b5fa7tUtAbK3rQJ/G2Rox",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 212
},
@ -8260,6 +8268,7 @@
"__type__": "671bfEgTTVFgLo1v+x/wd06",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 221
},
@ -11829,6 +11838,7 @@
"__type__": "58a5aCWE8VEqJK+0wznWL24",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 311
},
@ -14643,6 +14653,7 @@
"__type__": "c5e2dm0qXVOuKP1ju7uyMtJ",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 458
},
@ -15877,6 +15888,7 @@
"__type__": "ecf07La1rNA8YjY0h97yfWf",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 237
},
@ -17260,6 +17272,7 @@
"__type__": "204c1A707JPjYYnhqfMvkXj",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 586
},
@ -17726,6 +17739,7 @@
"__type__": "fe619HtEX1OQq7oeACKrANd",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 608
},
@ -18380,6 +18394,7 @@
"__type__": "02a884WNWtExrO21yAjQDWZ",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 221
},
@ -19025,6 +19040,7 @@
"__type__": "bed15fuvPJLIp1O3BWg43ad",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 647
},
@ -19668,6 +19684,7 @@
"__type__": "bed15fuvPJLIp1O3BWg43ad",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 663
},
@ -20311,6 +20328,7 @@
"__type__": "bed15fuvPJLIp1O3BWg43ad",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 682
},
@ -20954,6 +20972,7 @@
"__type__": "bed15fuvPJLIp1O3BWg43ad",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 701
},
@ -21633,6 +21652,7 @@
"__type__": "85a1du42gFMrIHUG6qev0l/",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 722
},
@ -22219,6 +22239,7 @@
"__type__": "85a1du42gFMrIHUG6qev0l/",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 737
},
@ -22805,6 +22826,7 @@
"__type__": "85a1du42gFMrIHUG6qev0l/",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 752
},
@ -23376,6 +23398,7 @@
"__type__": "85a1du42gFMrIHUG6qev0l/",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 767
},
@ -23549,6 +23572,7 @@
"__type__": "ca8a0x4KJRGNJFfMRlEhSED",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 783
},
@ -31054,6 +31078,7 @@
"__type__": "402d0NRh/pCFotZG+o5aE8p",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 788
},
@ -33513,6 +33538,7 @@
"__type__": "1f970S32ylOnofatPpSgI1Z",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 998
},
@ -35530,6 +35556,7 @@
"__type__": "1f970S32ylOnofatPpSgI1Z",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 1133
},
@ -36013,6 +36040,7 @@
"__type__": "85b9aaRsZBAn5sUxS5RQ3EF",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 9
},
@ -36039,7 +36067,7 @@
"y": -1200,
"z": 0
},
"_timePlay": 10,
"_timePlay": 120,
"_clockIcon": {
"__uuid__": "f7bd5166-9d5f-4d43-a3d3-58ae9a4957fc@f9941",
"__expectedType__": "cc.SpriteFrame"

View File

@ -1,12 +1,17 @@
import {
AudioSource,
Component,
Event,
Game,
IVec2Like,
IVec3Like,
Label,
Node,
NodeSpace,
Sprite,
Vec2,
Vec3,
game,
randomRangeInt,
toDegree,
toRadian,
@ -14,8 +19,14 @@ import {
import ObjectPool from '../Pool/ObjectPool';
declare module 'cc' {
interface Game {
timeScale: number;
}
interface Component {
setNodeActive(isActive: boolean): void;
setNodeActive(event: Event, customEventData?: string): void;
setNodeActive(a: boolean | Event, b?: string): void;
}
interface Node {
@ -32,7 +43,7 @@ declare module 'cc' {
setScaleX(x: number): void;
setScaleY(y: number): void;
setScaleZ(z: number): void;
releaseToPool(): void;
releaseToPool(): boolean;
}
interface Vec2 {
@ -48,7 +59,22 @@ declare module 'cc' {
}
interface Label {
setString(value: string | number);
setString(value: string | number): void;
}
interface Sprite {
setFillRange(value: number): void;
}
interface AudioSource {
getPlaybackRate(): number;
/**
* Set playbackRate of this audio source
*
* Note: playbackRate control may be ineffective on some platforms.
*/
setPlaybackRate(value: number): void;
}
}
@ -64,10 +90,35 @@ declare global {
}
}
//#region GAME
Game.prototype.timeScale = 1;
// @ts-ignore
game._calculateDT = function (useFixedDeltaTime: number) {
this._useFixedDeltaTime = useFixedDeltaTime;
if (useFixedDeltaTime) {
this._startTime = performance.now();
return this.frameTime / 1000;
}
const now = performance.now();
this._deltaTime = now > this._startTime ? (now - this._startTime) / 1000 : 0;
if (this._deltaTime > Game.DEBUG_DT_THRESHOLD) {
this._deltaTime = this.frameTime / 1000;
}
this._startTime = now;
return this._deltaTime * this.timeScale;
};
//#endregion
//#region COMPONENT
Component.prototype.setNodeActive = function (isActive: boolean) {
this.node.setActive(isActive);
Component.prototype.setNodeActive = function (a: boolean | Event, b?: string) {
if (a instanceof Event) {
this.node.active = b?.toLowerCase() === 'true';
} else {
this.node.active = a;
}
};
//#endregion
@ -127,7 +178,7 @@ Node.prototype.setScaleZ = function (z: number): void {
};
Node.prototype.releaseToPool = function () {
ObjectPool.release(this);
return ObjectPool.release(this);
};
//#endregion
@ -190,6 +241,14 @@ Label.prototype.setString = function (value: string | number) {
//#endregion
//#region SPRITE
Sprite.prototype.setFillRange = function (value: number) {
this.fillRange = value;
};
//#endregion
//#region STRING
String.prototype.jsonParse = function () {
@ -222,3 +281,54 @@ Array.prototype.getRandom = function (weights?: number[]) {
};
//#endregion
//#region AUDIO SOURCE
//support audio playbackRate
//@ts-ignore
AudioSource.prototype._playbackRate = 1;
//@ts-ignores
AudioSource.prototype._syncStates = function () {
if (this._player) {
this._player.loop = this._loop;
this._player.volume = this._volume;
// this._player._sourceNode.playbackRate = this._playbackRate;
this._operationsBeforeLoading.forEach((opInfo): void => {
if (opInfo.op === 'SEEK') {
this._cachedCurrentTime = (opInfo.params && opInfo.params[0]) as number;
if (this._player) {
// eslint-disable-next-line @typescript-eslint/no-empty-function
this._player.seek(this._cachedCurrentTime).catch((e): void => {});
}
} else {
this[opInfo.op]?.();
}
});
try {
this._player._player._sourceNode.playbackRate.value = this._playbackRate;
} catch (e) {
console.log(e);
}
this._operationsBeforeLoading.length = 0;
}
};
AudioSource.prototype.getPlaybackRate = function () {
return this._playbackRate;
};
AudioSource.prototype.setPlaybackRate = function (value: number) {
if (this._player) {
try {
this._player._player._sourceNode.playbackRate.value = value;
} catch (e) {
console.log(e);
}
this._playbackRate = this._player.playbackRate;
} else {
this._playbackRate = value;
}
};
//#endregion

View File

@ -1,7 +1,12 @@
import { _decorator, AudioClip, AudioSource, sys } from 'cc';
import 'howler';
import { _decorator, AudioClip, AudioSource } from 'cc';
export class SfxSource {
interface ISoundOptions {
volume?: number;
loop?: boolean;
playbackRate?: number;
}
export class SoundSource {
private _source: AudioSource;
public get playing() {
@ -41,6 +46,22 @@ export class SfxSource {
this._mute = value;
}
public get clip() {
return this._source.clip;
}
public set clip(value: AudioClip) {
this._source.clip = value;
}
public get playbackRate() {
return this._source.getPlaybackRate();
}
public set playbackRate(value: number) {
this._source.setPlaybackRate(value);
}
constructor(audioClip: AudioClip) {
this._source = new AudioSource();
this._source.playOnAwake = false;
@ -65,8 +86,9 @@ export class SfxSource {
}
export default class AudioManager {
private static _audioSourcesSfx: Map<AudioClip, SfxSource> = new Map();
private static _audioSourceBgm: Howl;
private static readonly storageKey = 'gad-game-galaxy-seeker-mute';
private static _audioSourcesSfx: Map<AudioClip, SoundSource> = new Map();
private static _audioSourceBgm: SoundSource;
private static _isMute: boolean = false;
public static get mute() {
@ -80,33 +102,44 @@ export default class AudioManager {
}
public static setMute(mute: boolean) {
this._isMute = mute;
this._audioSourceBgm.mute(this._isMute);
this._audioSourceBgm.mute = mute;
this._audioSourcesSfx.forEach((source) => {
source.mute = this._isMute;
source.mute = mute;
});
this._isMute = mute;
}
public static playBGM(audio: AudioClip, opts?: { volume?: number; loop?: boolean; rate?: number }) {
this._audioSourceBgm?.stop();
this._audioSourceBgm = new Howl({
src: audio.nativeUrl,
public static playBGM(audio: AudioClip, opts?: ISoundOptions) {
const config: ISoundOptions = {
volume: 1,
loop: true,
mute: this._isMute,
playbackRate: 1,
...opts,
});
};
if (this._audioSourceBgm) {
this._audioSourceBgm.stop();
this._audioSourceBgm.clip = audio;
} else {
this._audioSourceBgm = new SoundSource(audio);
}
this._audioSourceBgm.loop = config.loop;
this._audioSourceBgm.volume = config.volume;
this._audioSourceBgm.playbackRate = config.playbackRate;
this._audioSourceBgm.mute = this._isMute;
this._audioSourceBgm.play();
}
public static setPlayRateBGM(rate: number) {
this._audioSourceBgm.rate(rate);
this._audioSourceBgm.playbackRate = rate;
}
public static playSfx(audioClip: AudioClip, opts?: { volume?: number; loop?: boolean }) {
public static playSfx(audioClip: AudioClip, opts?: ISoundOptions) {
let soundSource = this._audioSourcesSfx.get(audioClip);
const config = {
const config: ISoundOptions = {
volume: 1,
loop: false,
playbackRate: 1,
...opts,
};
@ -118,7 +151,7 @@ export default class AudioManager {
return;
}
soundSource = new SfxSource(audioClip);
soundSource = new SoundSource(audioClip);
soundSource.loop = config.loop;
soundSource.volume = config.volume;
soundSource.mute = this._isMute;
@ -138,7 +171,7 @@ export default class AudioManager {
this._audioSourcesSfx.forEach((sfx) => sfx.stop());
}
public static findAudioSourcesSfx(audioClip: AudioClip): SfxSource {
public static findAudioSourcesSfx(audioClip: AudioClip): SoundSource {
return this._audioSourcesSfx.get(audioClip);
}
}

View File

@ -1,4 +1,4 @@
import { Component, Node, Prefab, director, instantiate } from 'cc';
import { _decorator, Component, director, instantiate, Node, Prefab } from 'cc';
import IPoolable from './IPoolable';
export default class ObjectPool {
@ -6,13 +6,13 @@ export default class ObjectPool {
private _actives: Node[] = [];
private _prefab: Prefab;
private _expandable;
private _poolHandlerComp: new () => any;
private _poolHandlerComp: (new () => any) | string;
public get actives() {
public get listActive() {
return [...this._actives];
}
public get inActives() {
public get listInactive() {
return [...this._inactive];
}
@ -32,7 +32,12 @@ export default class ObjectPool {
return this.countInactive + this.countActive;
}
constructor(prefab: Prefab, size: number, expandable = true, poolHandlerComp?: new () => any | string) {
constructor(
prefab?: Prefab,
size?: number,
expandable: boolean = true,
poolHandlerComp?: (new () => any) | string,
) {
if (!!!prefab) {
console.error('prefab cant be null or undefine');
return;
@ -45,14 +50,14 @@ export default class ObjectPool {
let obj = instantiate(this._prefab); // create node instance
obj.removeFromParent();
this._inactive.push(obj);
ObjectPool._poolLookUp[obj.uuid] = this;
ObjectPool._poolLookUp.set(obj, this);
}
}
//#region Static
private static _poolLookUp: { [key: string]: ObjectPool } = {};
public static release(obj: Node) {
ObjectPool._poolLookUp[obj.uuid].release(obj);
private static _poolLookUp: Map<Node, ObjectPool> = new Map();
public static release(obj: Node): boolean {
return ObjectPool._poolLookUp.get(obj)?.release(obj);
}
//#endregion
@ -81,7 +86,7 @@ export default class ObjectPool {
} else if (this._expandable) {
// if not enough node in the pool, we call cc.instantiate to create node
obj = instantiate(this._prefab);
ObjectPool._poolLookUp[obj.uuid] = this;
ObjectPool._poolLookUp.set(obj, this);
} else {
obj = this._actives.shift();
obj.removeFromParent();
@ -90,45 +95,52 @@ export default class ObjectPool {
obj.setParent(parent);
this._actives.push(obj);
// Invoke pool handler
const handler = this._poolHandlerComp ? obj.getComponent(this._poolHandlerComp) : null;
let handler: Component = null;
if (typeof this._poolHandlerComp == 'string') {
handler = this._poolHandlerComp ? obj.getComponent(this._poolHandlerComp) : null;
} else {
handler = this._poolHandlerComp ? obj.getComponent(this._poolHandlerComp) : null;
}
if (handler) {
(handler as unknown as IPoolable)?.onGet();
}
if (classConstructor) {
return handler == classConstructor ? handler : obj.getComponent(classConstructor);
return obj.getComponent(classConstructor);
}
return obj;
}
public release(obj: Node): void;
public release<T extends Component>(obj: T): void;
public release<T extends Component>(obj: T | Node): void {
public release(obj: Node): boolean;
public release<T extends Component>(obj: T): boolean;
public release<T extends Component>(obj: T | Node): boolean {
let node = obj instanceof Node ? obj : obj.node;
const index = this._actives.indexOf(node);
//check obj is belongs to pool
if (index === -1) return;
if (index === -1) return false;
this._actives.splice(index, 1);
this._inactive.push(node);
// Invoke pool handler
const handler = this._poolHandlerComp ? node.getComponent(this._poolHandlerComp) : null;
let handler: Component = null;
if (typeof this._poolHandlerComp == 'string') {
handler = this._poolHandlerComp ? node.getComponent(this._poolHandlerComp) : null;
} else {
handler = this._poolHandlerComp ? node.getComponent(this._poolHandlerComp) : null;
}
if (handler) {
(handler as unknown as IPoolable)?.onRelease();
}
// Remove from parent, but don't cleanup
node.removeFromParent();
return true;
}
public clear() {
this.all.forEach((obj) => obj.destroy());
this._inactive = [];
this._actives = [];
Object.keys(ObjectPool._poolLookUp).forEach((key) => {
if (ObjectPool._poolLookUp[key] === this) {
delete ObjectPool._poolLookUp[key];
}
});
ObjectPool._poolLookUp.clear();
}
public releaseAll() {

View File

@ -0,0 +1,50 @@
<html>
<head>
<link rel="icon" href="./favicon.ico" />
<meta charset="utf-8" />
<title>
<%=title%>
</title>
<meta name="viewport"
content="width=device-width,user-scalable=no,initial-scale=1,minimum-scale=1,maximum-scale=1,minimal-ui=true" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="full-screen" content="yes" />
<meta name="screen-orientation" content="portrait" />
<meta name="x5-fullscreen" content="true" />
<meta name="360-fullscreen" content="true" />
<meta name="renderer" content="webkit" />
<meta name="force-rendering" content="webkit" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<link rel="stylesheet" type="text/css" href="./index.css" />
</head>
<body style="overflow: hidden;">
<%- include(cocosToolBar, {config: config}) %>
<div id="content" class="content" style="overflow: hidden;">
<div class="contentWrap">
<div id="GameDiv" class="wrapper">
<div id="Cocos3dGameContainer">
<canvas id="GameCanvas" tabindex="-1" style="background-color: '';"></canvas>
</div>
<div id="splash">
<div class="progress-bar stripes"><span></span></div>
</div>
<div id="bulletin">
<div id="sceneIsEmpty" class="inner">
<%=tip_sceneIsEmpty%>
</div>
</div>
<!-- <div class="error" id="error">
<div class="title">Error <i>(Please open the console to see detailed errors)</i></div>
<div class="error-main"></div>
<div class="error-stack"></div>
</div> -->
</div>
</div>
</div>
<%- include(cocosTemplate, {}) %>
</body>
</html>