大版本优化,主要在Map,JiaZai,GameManager, 优化新内存方案进游戏慢的问题,重新调整了所有的分包加载逻辑顺序
This commit is contained in:
@@ -4,6 +4,11 @@ import { MiniGameSdk } from "../../Sdk/MiniGameSdk";
|
||||
import { GameTool } from "../Tool/GameTool";
|
||||
import BlockAssetManager from "../Tool/BlockAssetManager";
|
||||
|
||||
interface GameplayEntryPreparation {
|
||||
startedAt: number;
|
||||
downloads: Promise<boolean>;
|
||||
release: () => void;
|
||||
}
|
||||
|
||||
const { ccclass, property } = cc._decorator;
|
||||
@ccclass('GameConfig')
|
||||
@@ -74,6 +79,84 @@ export class GameConfig {
|
||||
private static sceneLoadRequest = 0;
|
||||
private static homeWarmupRequest = 0;
|
||||
private static cancelBlockLoading: () => void = null;
|
||||
private static gameplayScenePreload: Promise<void> = null;
|
||||
private static gameplaySceneForegroundPreload: Promise<void> = null;
|
||||
private static pendingEntryDownloadRelease: () => void = null;
|
||||
static gameplayEntryStartedAt = 0;
|
||||
// 仅本次启动有效;地图就绪即可完成首次进关,不等待弹窗实例。
|
||||
static firstGameplayEntryComplete = false;
|
||||
static firstGameplayEntryPending = false;
|
||||
|
||||
/** 仅合并正在进行的预加载,不能把历史完成标记当作永久有效的场景缓存。 */
|
||||
static preloadGameplayScene(foreground = false): Promise<void> {
|
||||
const key = foreground ? "gameplaySceneForegroundPreload" : "gameplayScenePreload";
|
||||
if (this[key]) return this[key];
|
||||
const request = this.timeGameplayStage(foreground ? "GameScene前台文件准备" : "首页GameScene预取",
|
||||
() => new Promise<void>((resolve, reject) => {
|
||||
const done = (error: Error) => error ? reject(error) : resolve();
|
||||
const bundle = cc.assetManager.bundles.find(bundle => !!bundle.getSceneInfo("GameScene"));
|
||||
if (!bundle) { reject(new Error("GameScene 所属 Bundle 尚未加载")); return; }
|
||||
// 不等待首页的低优先级任务;引擎复用已下载文件,前台请求使用 scene 调度配置。
|
||||
bundle.preloadScene("GameScene", { preset: "scene", priority: 10 }, done);
|
||||
}));
|
||||
this[key] = request;
|
||||
const clear = () => {
|
||||
if (this[key] === request) this[key] = null;
|
||||
};
|
||||
request.then(clear, clear);
|
||||
return request;
|
||||
}
|
||||
|
||||
static async timeGameplayStage<T>(stage: string, task: () => Promise<T>): Promise<T> {
|
||||
const start = Date.now();
|
||||
// console.log(`[进关耗时][${stage}] 开始`);
|
||||
try {
|
||||
const result = await task();
|
||||
// console.log(`[进关耗时][${stage}] ${result === false ? "未就绪" : "完成"},独立耗时=${Date.now() - start}ms`);
|
||||
return result;
|
||||
} catch (error) {
|
||||
// console.log(`[进关耗时][${stage}] 失败,独立耗时=${Date.now() - start}ms`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/** 点击进关即开始不依赖关卡 JSON 的任务,所有入口共用原有下载保障。 */
|
||||
static beginGameplayEntryPreparation(): GameplayEntryPreparation {
|
||||
const startedAt = Date.now();
|
||||
this.gameplayEntryStartedAt = startedAt;
|
||||
const previousRelease = this.takeEntryDownloadRelease();
|
||||
const releaseHold = BlockAssetManager.instance.holdBackgroundDownloads();
|
||||
if (previousRelease) previousRelease();
|
||||
const release = () => {
|
||||
releaseHold();
|
||||
if (this.pendingEntryDownloadRelease === release) this.pendingEntryDownloadRelease = null;
|
||||
};
|
||||
this.pendingEntryDownloadRelease = release;
|
||||
// console.log("[进关耗时] 开始进关准备(各并行阶段耗时不能相加)");
|
||||
const downloads = this.timeGameplayStage("五个弹窗分包磁盘检查/下载",
|
||||
() => GameTool.ensureGameplayPopupSubpackagesDownloaded()).catch(() => false);
|
||||
// 不把文件预取作为门槛;必要资源就绪后 loadScene 立即正式加载并复用已有文件。
|
||||
this.preloadGameplayScene(true).catch(error => cc.warn("场景文件预取失败,正式加载时重试", error));
|
||||
return { startedAt, downloads, release };
|
||||
}
|
||||
|
||||
/** 新 Map 接管自己的暂停凭证,旧 Map 销毁不能恢复新进关的后台派发。 */
|
||||
static takeEntryDownloadRelease(): () => void {
|
||||
const release = this.pendingEntryDownloadRelease;
|
||||
this.pendingEntryDownloadRelease = null;
|
||||
return release;
|
||||
}
|
||||
|
||||
/** 首页仅用来判断能否启动自动预加载,不暂停或接管任何在途回调。 */
|
||||
static isGameplayEntryPreparing(): boolean {
|
||||
return !!this.pendingEntryDownloadRelease;
|
||||
}
|
||||
|
||||
static shouldPrepareFirstGameplayEntry(): boolean {
|
||||
const scene = cc.director.getScene();
|
||||
return !this.firstGameplayEntryComplete && !!scene && scene.name === "HomeScene"
|
||||
&& GameTool.shouldPreloadGameplayPopupInstances();
|
||||
}
|
||||
|
||||
static getCurrentLevelBlockDefinitions(): any[] {
|
||||
const blocks = this.BLOCK_INFO && this.BLOCK_INFO[0] ? this.BLOCK_INFO[0] : [];
|
||||
@@ -175,59 +258,95 @@ export class GameConfig {
|
||||
}
|
||||
|
||||
/** 所有正式进关入口都在弹窗分包及当前关图片就绪后才切换 GameScene。 */
|
||||
static enterGameSceneWhenReady(time = 0): void {
|
||||
static enterGameSceneWhenReady(time = 0, preparation?: GameplayEntryPreparation): void {
|
||||
const id = ++this.sceneLoadRequest;
|
||||
const scene = cc.director.getScene();
|
||||
const blocks = this.BLOCK_INFO;
|
||||
const isCurrent = () => id === this.sceneLoadRequest && cc.director.getScene() === scene && this.BLOCK_INFO === blocks;
|
||||
const loading = this.createBlockLoading(isCurrent);
|
||||
const firstEntry = this.shouldPrepareFirstGameplayEntry();
|
||||
const entry = preparation || this.beginGameplayEntryPreparation();
|
||||
const { release, startedAt } = entry;
|
||||
|
||||
if (!GameTool.areGameplayPopupSubpackagesDownloaded()) loading.open();
|
||||
GameTool.ensureGameplayPopupSubpackagesDownloaded().then(popupReady => {
|
||||
if (firstEntry || !GameTool.areGameplayPopupSubpackagesDownloaded()) loading.open();
|
||||
// 只等待磁盘分包与当前关图片,不等待后台场景预加载回调。
|
||||
Promise.all([
|
||||
entry.downloads,
|
||||
this.timeGameplayStage("当前关方块+公共图集", () => this.prepareBlockWindow(loading.open)),
|
||||
]).then(([popupReady, ready]) => {
|
||||
if (!isCurrent()) {
|
||||
loading.close();
|
||||
return false;
|
||||
release();
|
||||
return;
|
||||
}
|
||||
if (!popupReady) {
|
||||
loading.close();
|
||||
release();
|
||||
const missing = GameTool.getMissingGameplayPopupSubpackages();
|
||||
this.showBlockLoadError(
|
||||
new Error(`游戏弹窗分包下载失败: ${missing.join(", ")}`),
|
||||
() => this.enterGameSceneWhenReady(time),
|
||||
"游戏界面资源下载失败\n请检查网络,点击此处重试"
|
||||
);
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
return this.prepareBlockWindow(loading.open);
|
||||
}).then(ready => {
|
||||
if (!ready) {
|
||||
loading.close();
|
||||
release();
|
||||
return;
|
||||
}
|
||||
// console.log(`[进关耗时][进关门槛就绪] 从开始累计=${Date.now() - startedAt}ms`);
|
||||
loading.close();
|
||||
if (!ready || !isCurrent()) return;
|
||||
cc.director.preloadScene("GameScene", error => {
|
||||
if (!isCurrent()) return;
|
||||
if (error) {
|
||||
this.showBlockLoadError(error, () => this.enterGameSceneWhenReady(time));
|
||||
if (firstEntry) {
|
||||
const home = cc.find("Canvas").getComponent("JiaZai");
|
||||
home.playGameEntryTransition(true);
|
||||
}
|
||||
const transitionStartedAt = Date.now();
|
||||
setTimeout(() => {
|
||||
if (!isCurrent()) {
|
||||
release();
|
||||
return;
|
||||
}
|
||||
setTimeout(() => {
|
||||
if (!isCurrent()) return;
|
||||
const retainPopupBundles = !!scene && scene.name === "GameScene"
|
||||
&& GameTool.beginGameplayPopupGameSceneReloadRetention();
|
||||
try {
|
||||
cc.director.loadScene("GameScene", () => {
|
||||
if (retainPopupBundles) {
|
||||
GameTool.finishGameplayPopupGameSceneReloadRetention();
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
const retainPopupBundles = !!scene && scene.name === "GameScene"
|
||||
&& GameTool.beginGameplayPopupGameSceneReloadRetention();
|
||||
// console.log(`[进关耗时][转场等待] 独立耗时=${Date.now() - transitionStartedAt}ms`);
|
||||
const sceneStartedAt = Date.now();
|
||||
// console.log("[进关耗时][GameScene正式加载及激活] 开始");
|
||||
try {
|
||||
if (firstEntry) this.firstGameplayEntryPending = GameTool.shouldPreloadGameplayPopupInstances();
|
||||
const accepted = cc.director.loadScene("GameScene", (error: Error) => {
|
||||
if (retainPopupBundles) {
|
||||
GameTool.finishGameplayPopupGameSceneReloadRetention();
|
||||
}
|
||||
this.showBlockLoadError(error, () => this.enterGameSceneWhenReady(time));
|
||||
if (error) {
|
||||
// console.log(`[进关耗时][GameScene正式加载及激活] 失败,独立耗时=${Date.now() - sceneStartedAt}ms`, error);
|
||||
release();
|
||||
if (isCurrent()) {
|
||||
this.firstGameplayEntryPending = false;
|
||||
this.showBlockLoadError(error, () => this.enterGameSceneWhenReady(time));
|
||||
}
|
||||
} else {
|
||||
// console.log(`[进关耗时][GameScene正式加载及激活] 完成,独立耗时=${Date.now() - sceneStartedAt}ms,从开始累计=${Date.now() - startedAt}ms`);
|
||||
}
|
||||
});
|
||||
if (accepted === false) {
|
||||
release();
|
||||
if (retainPopupBundles) GameTool.finishGameplayPopupGameSceneReloadRetention();
|
||||
this.firstGameplayEntryPending = false;
|
||||
this.showBlockLoadError(new Error("GameScene 切换未启动"), () => this.enterGameSceneWhenReady(time));
|
||||
}
|
||||
}, time);
|
||||
});
|
||||
} catch (error) {
|
||||
release();
|
||||
if (firstEntry) this.firstGameplayEntryPending = false;
|
||||
if (retainPopupBundles) {
|
||||
GameTool.finishGameplayPopupGameSceneReloadRetention();
|
||||
}
|
||||
this.showBlockLoadError(error, () => this.enterGameSceneWhenReady(time));
|
||||
}
|
||||
}, firstEntry ? Math.max(time, 1000) : time);
|
||||
}).catch(error => {
|
||||
loading.close();
|
||||
release();
|
||||
if (isCurrent()) this.showBlockLoadError(error, () => this.enterGameSceneWhenReady(time));
|
||||
});
|
||||
}
|
||||
@@ -543,7 +662,7 @@ export class GameConfig {
|
||||
vibrateOpen: true, //震动
|
||||
coinnum: 0, //每局的金币数
|
||||
paid_user: false, //是否是付费用户
|
||||
version: "1.0.47", //版本号
|
||||
version: "1.0.50", //版本号
|
||||
shushu_DistinctId: "", //数数访客ID
|
||||
shushu_AccountId: "", //数数账号ID
|
||||
uid: "", //用户和后端唯一id
|
||||
@@ -671,6 +790,8 @@ export class GameConfig {
|
||||
var self = this;
|
||||
const requestId = ++this.levelLoadRequest;
|
||||
this.sceneLoadRequest++;
|
||||
const previousEntryRelease = this.takeEntryDownloadRelease();
|
||||
if (previousEntryRelease) previousEntryRelease();
|
||||
if (this.cancelBlockLoading) this.cancelBlockLoading();
|
||||
|
||||
let name = null;
|
||||
@@ -701,17 +822,30 @@ export class GameConfig {
|
||||
|
||||
const scene = cc.director.getScene();
|
||||
const isCurrent = () => requestId === this.levelLoadRequest && (!type || cc.director.getScene() === scene);
|
||||
const firstEntryLoading = type && this.shouldPrepareFirstGameplayEntry() ? this.createBlockLoading(isCurrent) : null;
|
||||
if (firstEntryLoading) firstEntryLoading.open();
|
||||
const preparation = type ? this.beginGameplayEntryPreparation() : null;
|
||||
const configStartedAt = Date.now();
|
||||
// console.log(`[进关耗时][${name}/custom分包] 开始`);
|
||||
let bundleName = "custom"; // 替换为实际的分包名称
|
||||
cc.assetManager.loadBundle(bundleName, (err: Error, bundle: cc.AssetManager.Bundle) => {
|
||||
if (!isCurrent()) return;
|
||||
cc.assetManager.loadBundle(bundleName, { priority: 10 }, (err: Error, bundle: cc.AssetManager.Bundle) => {
|
||||
// console.log(`[进关耗时][${name}/custom分包] ${err ? "失败" : "完成"},独立耗时=${Date.now() - configStartedAt}ms`);
|
||||
if (!isCurrent()) { if (preparation) preparation.release(); return; }
|
||||
if (err) {
|
||||
if (preparation) preparation.release();
|
||||
if (firstEntryLoading) firstEntryLoading.close();
|
||||
if (type) this.showBlockLoadError(err, () => this.LEVEL_INFO_init(type, time, otherLevel, endless));
|
||||
return;
|
||||
}
|
||||
|
||||
bundle.load(name, cc.JsonAsset, (err: Error, res: cc.JsonAsset) => {
|
||||
if (!isCurrent()) return;
|
||||
const jsonStartedAt = Date.now();
|
||||
// console.log(`[进关耗时][${name}/JSON] 开始`);
|
||||
cc.assetManager.loadAny({ path: name, type: cc.JsonAsset, bundle: bundleName }, { priority: 10 }, (err: Error, res: cc.JsonAsset) => {
|
||||
// console.log(`[进关耗时][${name}/JSON] ${err ? "失败" : "完成"},独立耗时=${Date.now() - jsonStartedAt}ms`);
|
||||
if (!isCurrent()) { if (preparation) preparation.release(); return; }
|
||||
if (err) {
|
||||
if (preparation) preparation.release();
|
||||
if (firstEntryLoading) firstEntryLoading.close();
|
||||
if (bundle.getInfoWithPath(name, cc.JsonAsset)) {
|
||||
if (type) this.showBlockLoadError(err, () => this.LEVEL_INFO_init(type, time, otherLevel, endless));
|
||||
return;
|
||||
@@ -727,7 +861,7 @@ export class GameConfig {
|
||||
|
||||
return;
|
||||
}
|
||||
self.applyLevelData(res.json, type, time, Number(name.substring("Json/level".length)));
|
||||
self.applyLevelData(res.json, type, time, Number(name.substring("Json/level".length)), preparation);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1013,13 +1147,13 @@ export class GameConfig {
|
||||
const bundleName = "custom";
|
||||
const name = `Json/level${level}`;
|
||||
|
||||
cc.assetManager.loadBundle(bundleName, (err: Error, bundle: cc.AssetManager.Bundle) => {
|
||||
cc.assetManager.loadBundle(bundleName, { priority: 10 }, (err: Error, bundle: cc.AssetManager.Bundle) => {
|
||||
if (err) {
|
||||
reject(new Error(`加载分包失败: ${err.message}`));
|
||||
return;
|
||||
}
|
||||
|
||||
bundle.load(name, cc.JsonAsset, (err: Error, res: cc.JsonAsset) => {
|
||||
cc.assetManager.loadAny({ path: name, type: cc.JsonAsset, bundle: bundleName }, { priority: 10 }, (err: Error, res: cc.JsonAsset) => {
|
||||
if (err) {
|
||||
reject(new Error(`加载关卡资源失败: ${err.message}`));
|
||||
return;
|
||||
@@ -1111,7 +1245,7 @@ export class GameConfig {
|
||||
/**
|
||||
* 应用关卡数据
|
||||
*/
|
||||
static applyLevelData(jsonData: any, type: any, time: any, levelNumber = Number(jsonData.LEVEL_INFO[0].id)) {
|
||||
static applyLevelData(jsonData: any, type: any, time: any, levelNumber = Number(jsonData.LEVEL_INFO[0].id), preparation?: GameplayEntryPreparation) {
|
||||
this.loadedLevelNumber = levelNumber;
|
||||
this.BLOCK_INFO = jsonData["BLOCK_INFO"];
|
||||
this.LEVEL_INFO = jsonData["LEVEL_INFO"];
|
||||
@@ -1119,7 +1253,7 @@ export class GameConfig {
|
||||
console.log("关卡配置加载完成");
|
||||
|
||||
if (type) {
|
||||
this.enterGameSceneWhenReady(time);
|
||||
this.enterGameSceneWhenReady(time, preparation);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,25 +1,8 @@
|
||||
export const REVIVE_COST_AB_NAME = "revive_cost_ascend";
|
||||
export const MAX_REVIVE_COST_STEP = 5;
|
||||
// 第三层实验结束,所有玩家统一采用原 C 组复活费用。
|
||||
const REVIVE_COSTS = [500, 700, 900, 1100, 1300, 1500];
|
||||
export const MAX_REVIVE_COST_STEP = REVIVE_COSTS.length - 1;
|
||||
|
||||
const DEFAULT_REVIVE_COSTS = [500];
|
||||
const REVIVE_COSTS_BY_GROUP = {
|
||||
B: [500, 700, 900],
|
||||
C: [500, 700, 900, 1100, 1300, 1500]
|
||||
};
|
||||
|
||||
export function getABReviveCoinCost(assignment: any, reviveCount: any): number {
|
||||
const isTargetExperiment = assignment
|
||||
&& String(assignment.layer) === "3"
|
||||
&& assignment.name === REVIVE_COST_AB_NAME
|
||||
&& assignment.enabled !== false;
|
||||
const group = isTargetExperiment
|
||||
? String(assignment.group || "Default").toUpperCase()
|
||||
: "DEFAULT";
|
||||
const costs = group === "B"
|
||||
? REVIVE_COSTS_BY_GROUP.B
|
||||
: group === "C"
|
||||
? REVIVE_COSTS_BY_GROUP.C
|
||||
: DEFAULT_REVIVE_COSTS;
|
||||
export function getReviveCoinCost(reviveCount: any): number {
|
||||
const step = Math.max(0, Math.floor(Number(reviveCount) || 0));
|
||||
return costs[Math.min(step, costs.length - 1)];
|
||||
return REVIVE_COSTS[Math.min(step, MAX_REVIVE_COST_STEP)];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user