import type GameplayIntro from "../GameplayIntro/script/GameplayIntro"; import { getLevelGameplay } from "./module/Tool/LevelGameplay"; // Learn TypeScript: // - https://docs.cocos.com/creator/manual/en/scripting/typescript.html // Learn Attribute: // - https://docs.cocos.com/creator/manual/en/scripting/reference/attributes.html // Learn life-cycle callbacks: // - https://docs.cocos.com/creator/manual/en/scripting/life-cycle-callbacks.html import MapConroler from "./Map"; import LoadingCatAnimation from "./LoadingCatAnimation"; import Utils from "./module/Pay/Utils"; import { starterPackRemaining } from "./module/Config/StarterPack"; import NumberToImage from "./NumberToImage"; import { MiniGameSdk } from "./Sdk/MiniGameSdk"; const { ccclass, property } = cc._decorator; interface DynamicPopupState { bundleName: string; prefabPath: string; root: cc.Node; bundle: cc.AssetManager.Bundle; loading: boolean; requestId: number; callbacks: Array<(error: Error, root?: cc.Node) => void>; } @ccclass export default class SceneManager extends cc.Component { private static activeInstance: SceneManager = null; private isPopupRuntimeAlive(): boolean { return cc.isValid(this, true) && SceneManager.activeInstance === this && !this.isDestroying && !!this.node && cc.isValid(this.node, true); } /** 旧场景的异步回调不能释放新 GameScene 正在使用的同名 Bundle。 */ private static releaseOrphanedPopupBundle(bundleName: string, bundle: cc.AssetManager.Bundle): void { // The gift shares action_bundle with Home; its lifetime is not owned by this popup. if (bundleName === "action_bundle") return; if (!bundle) return; setTimeout(() => { if (bundleName !== "gameplay_intro" && cc.fx.GameTool && cc.fx.GameTool.shouldRetainGameplayPopupBundlesForGameSceneReload && cc.fx.GameTool.shouldRetainGameplayPopupBundlesForGameSceneReload()) return; const active = SceneManager.activeInstance; if (active && cc.isValid(active, true)) { if (bundleName !== "gameplay_intro") return; // 旧场景晚到的介绍请求,仅在新场景确实接管此包时保留。 if (Object.keys(active.dynamicPopupStates || {}).some(name => { const state = active.dynamicPopupStates[name]; return state.bundle === bundle || (state.loading && state.bundleName === bundleName); })) return; } if (cc.assetManager.getBundle(bundleName) !== bundle) return; bundle.releaseAll(); if (cc.assetManager.getBundle(bundleName) === bundle) { cc.assetManager.removeBundle(bundle); } }, 0); } private releaseStalePopupBundle(bundleName: string, bundle: cc.AssetManager.Bundle): void { if (!bundle) return; if (this.isPopupRuntimeAlive()) { this.releaseDynamicPopupBundleIfUnused(bundleName, bundle); } else { SceneManager.releaseOrphanedPopupBundle(bundleName, bundle); } } /** GameScene 的 Loading 始终使用引擎允许的最高层级,并保持为最后一个同级节点。 */ private ensureLoadingOnTop(): cc.Node { const loading = this.node && this.node.getChildByName("Loading"); if (loading) { loading.zIndex = cc.macro.MAX_ZINDEX; if (loading.parent) { loading.setSiblingIndex(loading.parent.childrenCount - 1); } } return loading; } ensureGameShop(): cc.Node { const game = this.node && this.node.getChildByName("Game"); if (!game) return null; let shop = game.getChildByName("shop"); // 编辑器仍使用旧场景时,同步迁移顶部入口,避免新脚本与旧层级不一致。 if (!shop) { const top = cc.find("GameNode/Top", game); shop = top && top.getChildByName("shop"); if (!shop) return null; const topWidget = top.getComponent(cc.Widget); if (topWidget) topWidget.updateAlignment(); const position = game.convertToNodeSpaceAR(shop.convertToWorldSpaceAR(cc.v2(0, 0))); shop.parent = game; shop.setPosition(position); } if (!shop.getComponent(cc.Widget)) { const widget = shop.addComponent(cc.Widget); widget.isAlignTop = true; widget.top = game.height * (1 - game.anchorY) - shop.y - shop.height * (1 - shop.anchorY) * shop.scaleY; widget.alignMode = cc.Widget.AlignMode.ON_WINDOW_RESIZE; } shop.zIndex = cc.macro.MAX_ZINDEX - 1; return shop; } /** 动态弹窗加载期间使用;层级高于业务弹窗,低于 Loading 和失败重试层。 */ private ensureDynamicPopupIntercept(): cc.Node { if (!this.node || !cc.isValid(this.node)) return null; const intercept = this.node.getChildByName("intercept"); if (intercept) { intercept.zIndex = cc.macro.MAX_ZINDEX - 1; } return intercept; } // 缓存 shop 预制体 private static cachedShopPrefab: cc.Prefab | null = null; // 缓存 shop 节点 private shopNode: cc.Node | null = null; private starterPackOpening = false; // GameScene 中从商城打开 Jungle Treasure 时使用。 private static cachedJungleTreasurePrefab: cc.Prefab | null = null; private jungleTreasureNode: cc.Node | null = null; private jungleTreasureServerData: any = null; private jungleTreasureDataLoading: boolean = false; private jungleTreasureBundleLoading: boolean = false; private jungleTreasureRequestId: number = 0; private jungleTreasureRequestTimeout: any = null; private jungleTreasureOpenSource: string = "shop_page"; // 缓存 reward 预制体 private static cachedRewardPrefab: cc.Prefab | null = null; // 缓存 reward 奖励窗口 节点 private RewardNode: cc.Node | null = null; //缓存月卡 private static cachedMonthlyCardPrefab: cc.Prefab | null = null; // 缓存月卡节点 private monthlyCardNode: cc.Node | null = null; static clearShopPrefabCaches(): void { SceneManager.cachedShopPrefab = null; SceneManager.cachedRewardPrefab = null; SceneManager.cachedMonthlyCardPrefab = null; } /** releaseAll 不会同步清空 JS 静态字段;使用前先丢弃已经失效的 Prefab。 */ private static clearInvalidShopPrefabCaches(): void { if (SceneManager.cachedShopPrefab && !cc.isValid(SceneManager.cachedShopPrefab)) { SceneManager.cachedShopPrefab = null; } if (SceneManager.cachedRewardPrefab && !cc.isValid(SceneManager.cachedRewardPrefab)) { SceneManager.cachedRewardPrefab = null; } if (SceneManager.cachedMonthlyCardPrefab && !cc.isValid(SceneManager.cachedMonthlyCardPrefab)) { SceneManager.cachedMonthlyCardPrefab = null; } } @property(cc.Label) label: cc.Label = null; @property text: string = 'hello'; @property(cc.Node) freeze: cc.Node = null; @property(cc.Node) hammer: cc.Node = null; @property(cc.Node) magic_wand: cc.Node = null; @property(cc.Node) pause: cc.Node = null; @property(cc.Node) level: cc.Node = null; //time 弹窗的金币数 @property(cc.Node) timeCoin: cc.Node = null; //赢了win金币数 @property(cc.Node) winCoin: cc.Node = null; @property(cc.Node) nextMode: cc.Node = null; @property({ type: cc.Prefab, tooltip: "旧版 B 方案:结算后飞金币预制体,不配则使用 flyCoinSpriteFrame" }) flyCoinPrefab: cc.Prefab = null; @property({ type: cc.SpriteFrame, tooltip: "旧版 B 方案:结算后飞金币图片,未配置预制体时使用" }) flyCoinSpriteFrame: cc.SpriteFrame = null; @property({ tooltip: "旧版 B 方案:结算后飞金币数量" }) flyCoinCount: number = 16; @property({ tooltip: "旧版 B 方案:结算后飞金币单个飞行时间" }) flyCoinDuration: number = 1; @property({ tooltip: "旧版 B 方案:结算后飞金币间隔" }) flyCoinInterval: number = 0.05; @property({ type: [cc.Prefab], tooltip: "方块数组" }) Block_Array: Array = []; @property({ type: [cc.Prefab], tooltip: "墙壁数组" }) Wall_Prefab: Array = []; @property(cc.SpriteAtlas) avatar: cc.SpriteAtlas = null; @property(cc.Prefab) messageNode: cc.Prefab = null; particleEffects: cc.ParticleAsset[]; // @property({type: [cc.ParticleSystem], tooltip:"粒子数组"}) // particleEffects : Array = []; load1: boolean = false; load2: boolean = false; load3: boolean = false; btnName: string = ''; callBack: any; share_time: boolean = true; private flyCoinLayer: cc.Node = null; private isDestroying: boolean = false; private winLevelName: number = 0; private dynamicPopupLoads: { [popupName: string]: boolean } = {}; private dynamicPopupRetryNode: cc.Node = null; private dynamicPopupRetryName: string = ""; private dynamicPopupRetries: { [popupName: string]: { displayName: string; retry: () => void } } = {}; private dynamicPopupStates: { [popupName: string]: DynamicPopupState } = {}; private popupBundleWarmupStarted: boolean = false; private gameplayIntroPredownloadScheduled: boolean = false; /** 地图就绪且弹窗预热结束后调用;持续空闲一秒才派发,只下载,不 loadBundle。 */ scheduleGameplayIntroPredownload() { //@ts-ignore if (cc.sys.platform !== cc.sys.WECHAT_GAME || typeof wx === "undefined" || !wx.preDownloadSubpackage) return; if (this.gameplayIntroPredownloadScheduled || !this.isPopupRuntimeAlive()) return; this.gameplayIntroPredownloadScheduled = true; let idle = false; const check = () => { if (!this.isPopupRuntimeAlive()) return; const tool = cc.fx.GameTool; if (tool.isGameplayIntroDownloaded()) return; const busy = tool.hasPendingGameplayResourceLoads() || cc.fx.GameConfig.isGameplayEntryPreparing() || Object.keys(this.dynamicPopupLoads).length > 0 || Object.keys(this.dynamicPopupStates).some(name => this.dynamicPopupStates[name].loading); if (busy || !idle) { idle = !busy; this.scheduleOnce(check, 1); return; } tool.predownloadGameplayIntro().then(ready => { if (!ready && this.isPopupRuntimeAlive()) { idle = false; this.scheduleOnce(check, 10); } }); }; this.scheduleOnce(check, 1); } warmupGameplayPopupBundles( preloadInstances: boolean = false, preloadWin?: (onComplete: (error?: Error) => void) => void, onComplete?: (error?: Error) => void, ) { if (!this.isPopupRuntimeAlive() || this.popupBundleWarmupStarted) return; this.popupBundleWarmupStarted = true; if (preloadInstances) { const gameRoot = this.node && this.node.getChildByName("Game"); const requests = [ { popupName: "pause", bundleName: "pause", prefabPath: "prefab/Pause", parent: this.node }, { popupName: "win", bundleName: "win", prefabPath: "prefab/Win", parent: gameRoot }, { popupName: "propWindow", bundleName: "propWindow", prefabPath: "prefab/PropWindow", parent: gameRoot }, { popupName: "newMode", bundleName: "NewMode", prefabPath: "prefab/NewMode", parent: gameRoot }, { popupName: "lose", bundleName: "lose", prefabPath: "prefab/Lose", parent: gameRoot }, ]; let warmupError: Error = null; const preloadNext = (index: number) => { if (!this.isPopupRuntimeAlive()) return; if (!cc.fx.GameTool.shouldPreloadGameplayPopupInstances()) { this.popupBundleWarmupStarted = false; if (onComplete) onComplete(); return; } if (index >= requests.length) { if (onComplete) { if (warmupError) this.popupBundleWarmupStarted = false; onComplete(warmupError); } return; } const request = requests[index]; const startedAt = Date.now(); // console.log(`[进关耗时][弹窗预热/${request.bundleName}] 开始(不阻塞进关)`); const logDone = (error?: Error) => { // console.log(`[进关耗时][弹窗预热/${request.bundleName}] ${error ? "失败" : "完成"},独立耗时=${Date.now() - startedAt}ms`); }; // 即使资源全命中内存,也不要在同一帧连续实例化五个弹窗。 const next = () => this.scheduleOnce(() => preloadNext(index + 1), 0); if (request.popupName === "win") { if (preloadWin) { preloadWin(error => { if (!this.isPopupRuntimeAlive()) return; logDone(error); if (error) { warmupError = error; cc.warn("Popup instance warmup failed: win", error.message || error); } next(); }); } else { next(); } return; } this.loadDynamicPopup( request.popupName, request.bundleName, request.prefabPath, request.parent, (error: Error) => { if (!this.isPopupRuntimeAlive()) return; logDone(error); if (error) { warmupError = error; cc.warn(`Popup instance warmup failed: ${request.bundleName}`, error.message || error); // loadDynamicPopup 已清理失败资源。用户可能正共用此请求, // 这里不能清除其输入拦截/重试状态。 } next(); }, false, ); }; preloadNext(0); return; } // iOS 低内存机型只提前创建 Pause Bundle,不加载 Prefab 和图片。 const shouldWarmupPauseBundle = cc.sys.platform === cc.sys.WECHAT_GAME && !!cc.fx.GameTool && !!cc.fx.GameTool.isIOSDevice && cc.fx.GameTool.isIOSDevice(); if (!shouldWarmupPauseBundle) { if (onComplete) onComplete(); return; } const loadedPauseBundle = cc.assetManager.getBundle("pause"); if (loadedPauseBundle) { console.log("[PopupBundle] already loaded: pause (iOS low-memory warmup)"); if (onComplete) onComplete(); return; } cc.assetManager.loadBundle("pause", (error: Error, bundle: cc.AssetManager.Bundle) => { if (!this.isPopupRuntimeAlive()) { if (bundle) SceneManager.releaseOrphanedPopupBundle("pause", bundle); return; } if (error) { cc.warn("[PopupBundle] loadBundle failed: pause (iOS low-memory warmup)", error.message || error); if (onComplete) onComplete(error); return; } console.log("[PopupBundle] loadBundle complete: pause (iOS low-memory warmup)"); if (onComplete) onComplete(); }); } beginDynamicPopupLoad(popupName: string) { if (!popupName || !this.isPopupRuntimeAlive()) return; if (!this.dynamicPopupLoads) this.dynamicPopupLoads = {}; this.dynamicPopupLoads[popupName] = true; const intercept = this.ensureDynamicPopupIntercept(); if (!intercept) { cc.error("GameScene is missing Canvas/intercept"); return; } intercept.active = true; } finishDynamicPopupLoad(popupName: string) { if (!popupName || !this.isPopupRuntimeAlive()) return; if (!this.dynamicPopupLoads) this.dynamicPopupLoads = {}; if (!this.dynamicPopupRetries) this.dynamicPopupRetries = {}; delete this.dynamicPopupLoads[popupName]; delete this.dynamicPopupRetries[popupName]; if (this.dynamicPopupRetryName === popupName) { this.clearDynamicPopupRetry(); this.showNextDynamicPopupRetry(); } if (Object.keys(this.dynamicPopupLoads).length === 0) { this.dynamicPopupRetries = {}; const intercept = this.ensureDynamicPopupIntercept(); if (intercept) intercept.active = false; } } showDynamicPopupLoadError(popupName: string, displayName: string, retry: () => void) { if (!this.isPopupRuntimeAlive()) return; this.beginDynamicPopupLoad(popupName); this.dynamicPopupRetries[popupName] = { displayName, retry }; if (this.dynamicPopupRetryNode && cc.isValid(this.dynamicPopupRetryNode) && this.dynamicPopupRetryName !== popupName) { return; } this.clearDynamicPopupRetry(); this.showDynamicPopupRetry(popupName); } private showNextDynamicPopupRetry() { if (!this.isPopupRuntimeAlive() || this.dynamicPopupRetryNode || !this.dynamicPopupRetries) return; const popupName = Object.keys(this.dynamicPopupRetries) .find((name) => !!this.dynamicPopupLoads[name]); if (popupName) this.showDynamicPopupRetry(popupName); } private showDynamicPopupRetry(popupName: string) { if (!this.isPopupRuntimeAlive() || !this.dynamicPopupRetries) return; const retryInfo = this.dynamicPopupRetries[popupName]; if (!retryInfo || !this.node || !cc.isValid(this.node)) return; const displayName = retryInfo.displayName; const overlay = new cc.Node("DynamicPopupLoadRetry"); overlay.setContentSize(this.node.getContentSize()); overlay.setPosition(0, 0); overlay.zIndex = cc.macro.MAX_ZINDEX; overlay.addComponent(cc.BlockInputEvents); this.node.addChild(overlay); overlay.setSiblingIndex(this.node.childrenCount - 1); const background = overlay.addComponent(cc.Graphics); background.fillColor = cc.color(0, 0, 0, 225); background.rect(-overlay.width / 2, -overlay.height / 2, overlay.width, overlay.height); background.fill(); const messageNode = new cc.Node("message"); messageNode.setContentSize(760, 120); messageNode.parent = overlay; const label = messageNode.addComponent(cc.Label); label.string = `${displayName || "界面资源"}加载失败\n点击屏幕重试`; label.fontSize = 32; label.lineHeight = 48; label.horizontalAlign = cc.Label.HorizontalAlign.CENTER; this.dynamicPopupRetryNode = overlay; this.dynamicPopupRetryName = popupName; overlay.once(cc.Node.EventType.TOUCH_END, () => { if (!this.isPopupRuntimeAlive()) return; if (this.dynamicPopupRetryNode !== overlay) return; if (!this.dynamicPopupLoads || !this.dynamicPopupLoads[popupName]) return; label.string = `${displayName || "界面资源"}正在重新加载\n请稍等`; try { retryInfo.retry(); } catch (error) { cc.error(`${displayName || "界面资源"} retry failed:`, error); this.showDynamicPopupLoadError(popupName, displayName, retryInfo.retry); } }); } private clearDynamicPopupRetry() { if (this.dynamicPopupRetryNode && cc.isValid(this.dynamicPopupRetryNode)) { this.dynamicPopupRetryNode.removeFromParent(); this.dynamicPopupRetryNode.destroy(); } this.dynamicPopupRetryNode = null; this.dynamicPopupRetryName = ""; } loadDynamicPopup( popupName: string, bundleName: string, prefabPath: string, parent: cc.Node, callback: (error: Error, root?: cc.Node) => void, showLoading: boolean = true, ) { if (!this.isPopupRuntimeAlive()) return; if (!this.dynamicPopupStates) this.dynamicPopupStates = {}; if (showLoading) this.beginDynamicPopupLoad(popupName); let state = this.dynamicPopupStates[popupName]; if (!state) { state = this.dynamicPopupStates[popupName] = { bundleName, prefabPath, root: null, bundle: null, loading: false, requestId: 0, callbacks: [], }; } if (state.root && cc.isValid(state.root)) { callback(null, state.root); return; } state.root = null; if (!parent || !cc.isValid(parent)) { callback(new Error(`${popupName} parent node is missing`)); return; } state.callbacks.push(callback); if (state.loading) return; state.loading = true; const requestId = ++state.requestId; const fail = (error: Error, bundle?: cc.AssetManager.Bundle) => { if (!this.isDynamicPopupRequestCurrent(popupName, state, requestId)) { if (bundle) this.releaseStalePopupBundle(bundleName, bundle); return; } if (bundle && state.bundle === bundle) state.bundle = null; state.loading = false; if (bundle) this.releaseDynamicPopupBundleIfUnused(bundleName, bundle); this.finishDynamicPopupAssetLoad(state, error); }; const loadPrefab = (bundle: cc.AssetManager.Bundle) => { if (!this.isDynamicPopupRequestCurrent(popupName, state, requestId)) { this.releaseStalePopupBundle(bundleName, bundle); return; } state.bundle = bundle; bundle.load(prefabPath, cc.Prefab, (error: Error, prefab: cc.Prefab) => { if (error) { fail(error, bundle); return; } if (!this.isDynamicPopupRequestCurrent(popupName, state, requestId)) { this.releaseStalePopupBundle(bundleName, bundle); return; } let root: cc.Node = null; try { root = cc.instantiate(prefab); root.active = false; parent.addChild(root); root.setPosition(0, 0); root.zIndex = cc.macro.MAX_ZINDEX - 2; console.log(`[PopupPrefab] instantiated inactive: ${bundleName}/${prefabPath}`); } catch (instantiateError) { if (root && cc.isValid(root)) { root.active = false; root.removeFromParent(); root.destroy(); } fail(instantiateError as Error, bundle); return; } state.root = root; this.finishDynamicPopupAssetLoad(state, null, root); }); }; const loadedBundle = cc.assetManager.getBundle(bundleName); if (loadedBundle) { console.log(`[PopupBundle] already loaded: ${bundleName}`); loadPrefab(loadedBundle); return; } cc.assetManager.loadBundle(bundleName, (error: Error, bundle: cc.AssetManager.Bundle) => { if (error) { fail(error); return; } console.log(`[PopupBundle] loadBundle complete: ${bundleName}`); loadPrefab(bundle); }); } getDynamicPopup(popupName: string): cc.Node { if (!cc.isValid(this, true) || !this.dynamicPopupStates) return null; const state = this.dynamicPopupStates[popupName]; return state && state.root && cc.isValid(state.root) ? state.root : null; } releaseDynamicPopup(popupName: string, force: boolean = false, retainBundle: boolean = false) { if (!this.dynamicPopupStates) return; if (this.isDestroying !== true && !this.isPopupRuntimeAlive()) return; const state = this.dynamicPopupStates[popupName]; if (state && !force && this.shouldKeepGameplayPopupInstance(popupName) && state.root && cc.isValid(state.root) && !state.loading) { state.root.active = false; this.finishDynamicPopupLoad(popupName); return; } if (state) { delete this.dynamicPopupStates[popupName]; state.requestId++; state.callbacks.length = 0; if (state.root && cc.isValid(state.root)) { state.root.active = false; state.root.removeFromParent(); state.root.destroy(); } state.root = null; const bundle = state.bundle || cc.assetManager.getBundle(state.bundleName); state.bundle = null; // 介绍只缓存到当前关,不能混入五个常用弹窗的切关保留策略。 if (bundle && (!retainBundle || state.bundleName === "gameplay_intro")) { this.releaseDynamicPopupBundle(state.bundleName, bundle); } } this.finishDynamicPopupLoad(popupName); } private shouldKeepGameplayPopupInstance(popupName: string): boolean { const cacheablePopups = ["pause", "lose", "propWindow", "newMode"]; return cacheablePopups.indexOf(popupName) >= 0 && !!cc.fx.GameTool && !!cc.fx.GameTool.shouldPreloadGameplayPopupInstances && cc.fx.GameTool.shouldPreloadGameplayPopupInstances(); } private isDynamicPopupRequestCurrent(popupName: string, state: DynamicPopupState, requestId: number) { return this.isPopupRuntimeAlive() && !!this.dynamicPopupStates && this.dynamicPopupStates[popupName] === state && state.requestId === requestId; } private finishDynamicPopupAssetLoad(state: DynamicPopupState, error: Error, root?: cc.Node) { if (!this.isPopupRuntimeAlive()) { state.loading = false; state.callbacks.length = 0; return; } state.loading = false; const callbacks = state.callbacks.splice(0); callbacks.forEach((callback) => { if (!this.isPopupRuntimeAlive()) return; try { callback(error, root); } catch (callbackError) { cc.error("Dynamic popup callback failed:", callbackError); } }); } private releaseDynamicPopupBundle(bundleName: string, bundle: cc.AssetManager.Bundle) { if (bundleName === "action_bundle") return; if (cc.assetManager.getBundle(bundleName) !== bundle) return; bundle.releaseAll(); if (cc.assetManager.getBundle(bundleName) === bundle) { cc.assetManager.removeBundle(bundle); } } private releaseDynamicPopupBundleIfUnused(bundleName: string, bundle: cc.AssetManager.Bundle) { const states = this.dynamicPopupStates; if (!states) { SceneManager.releaseOrphanedPopupBundle(bundleName, bundle); return; } const isInUse = Object.keys(states).some((popupName) => { const state = states[popupName]; return state.bundle === bundle || (state.loading && state.bundleName === bundleName); }); if (!isInUse) this.releaseDynamicPopupBundle(bundleName, bundle); } bindLosePanel(loseRoot: cc.Node) { const lose = loseRoot && loseRoot.getChildByName("lose"); const time = lose && lose.getChildByName("Time"); const buyBtn = time && time.getChildByName("buyBtn"); const revive = time && time.getChildByName("Revive"); const timeCoin = buyBtn && buyBtn.getChildByName("coinNun"); const panel: any = loseRoot && loseRoot.getComponent("LosePanel"); const map = MapConroler && MapConroler._instance; if (!lose || !revive || !timeCoin || !panel || !panel.bind || !map) { cc.error("SceneManager.bindLosePanel failed: Lose prefab node structure changed"); this.unbindLosePanel(); return false; } panel.bind({ shareFriend: () => this.shareFriend(), shareFriend2: () => this.shareFriend2(), shareClose: () => this.shareClose(), homeBtn: (event: cc.Event, data: string) => map.homeBtn(event, data), reviewLevel: (event: cc.Event, data: string) => map.reviewLevel(event, data), closeWinStreak: () => map.closeWinStreak(), openHealth: () => map.openHealth(), returnHome: (event: cc.Event, data: string) => map.returnHome(event, data), againLevel: () => map.againLevel(), }); map.revive = revive; this.timeCoin = timeCoin; NumberToImage.numberToImageNodes(1000, 25, 15, "button_", this.timeCoin, false); return true; } unbindLosePanel() { if (MapConroler && MapConroler._instance) { MapConroler._instance.revive = null; } this.timeCoin = null; } releaseLosePanel(force: boolean = false) { this.unbindLosePanel(); this.releaseDynamicPopup("lose", force); } private bindPropWindowPanel(propRoot: cc.Node) { const prop = propRoot && propRoot.getChildByName("prop"); const buyBtn = prop && prop.getChildByName("buy_Btn"); const panel: any = propRoot && propRoot.getComponent("PropWindowPanel"); const map = MapConroler && MapConroler._instance; if (!prop || !buyBtn || !buyBtn.getComponent("btnControl") || !panel || !panel.bind || !map) { cc.error("SceneManager.bindPropWindowPanel failed: propWindow prefab node structure changed"); this.unbindPropWindowPanel(); return false; } panel.bind({ clickBtn: () => this.clickBtn(), closePropBuy: (event: cc.Event) => this.closePropBuy(event), }); map.coinPop = propRoot; return true; } private unbindPropWindowPanel() { if (MapConroler && MapConroler._instance) { MapConroler._instance.coinPop = null; } } private releasePropWindowPanel(force: boolean = false) { this.unbindPropWindowPanel(); this.releaseDynamicPopup("propWindow", force); } private bindPausePanel(pauseRoot: cc.Node) { const pauseContent = pauseRoot && pauseRoot.getChildByName("pause"); const panel: any = pauseRoot && pauseRoot.getComponent("PausePanel"); const setting = pauseRoot && pauseRoot.getComponent("setUi"); const map = MapConroler && MapConroler._instance; if (!pauseContent || !panel || !panel.bind || !setting || !map) { cc.error("SceneManager.bindPausePanel failed: Pause prefab node structure changed"); return false; } panel.bind({ closePause: () => this.closePause(), shareFriend: () => this.shareFriend(), openMessage: () => this.openMessage(), returnHome: (event: cc.Event, data: string) => map.returnHome(event, data), }); return true; } // 在 Creator 的 Inspector 中绑定场景现有 tipPanel;打开后的预制体挂到这里。 @property(cc.Node) tipPanel: cc.Node = null; // 保存已创建的面板控制器,重复打开复用实例;玩法图片仍由面板按需加载/释放。 private gameplayIntro: GameplayIntro = null; private levelGameplay: any[] = []; isPauseOpen(): boolean { // 加载期间及面板显示期间都视为暂停,避免重复打开或其它流程提前恢复游戏。 if (this.dynamicPopupLoads && this.dynamicPopupLoads.gameplayIntro) return true; if (this.gameplayIntro && cc.isValid(this.gameplayIntro.node) && this.gameplayIntro.node.active) return true; const pauseRoot = this.getDynamicPopup("pause"); return !!(this.dynamicPopupLoads && this.dynamicPopupLoads.pause) || (!!pauseRoot && pauseRoot.active); } releasePausePanel(force: boolean = false) { const pauseControl = this.pause && this.pause.getComponent("btnControl"); if (pauseControl) pauseControl.setTouch(true); this.releaseDynamicPopup("pause", force); } bindWinPanel(winRoot: cc.Node) { const tween = winRoot && winRoot.getChildByName("tween"); const diguan = tween && tween.getChildByName("diguan"); const tiaodik = tween && tween.getChildByName("tiaodik"); this.level = diguan && diguan.getChildByName("level"); this.winCoin = tiaodik && tiaodik.getChildByName("coin"); this.nextMode = tiaodik && tiaodik.getChildByName("nextMode"); if (!this.level || !this.winCoin || !this.nextMode) { cc.error("SceneManager.bindWinPanel failed: Win prefab node structure changed"); this.unbindWinPanel(); return false; } this.refreshWinPanelInfo(); return true; } unbindWinPanel() { this.level = null; this.winCoin = null; this.nextMode = null; this.clearFlyCoins(); } private refreshWinPanelInfo() { if (!this.level || !cc.isValid(this.level)) return; const levelName = this.winLevelName || this.resolveWinLevelName(); // NumberToImage.numberToImageNodes3(levelName, 43, 15, "level_", this.level, true); NumberToImage.numberToImageNodes(levelName, 43, 8, "button_", this.level, true); this.level.children.forEach((child) => child.color = new cc.Color(195, 154, 91)); } private resolveWinLevelName(): number { if (cc.fx.GameConfig.GM_INFO.otherLevel > 0) { return Number(cc.fx.GameConfig.GM_INFO.otherLevel) || 0; } if (cc.fx.GameTool.maxLevel() && cc.fx.GameConfig.GM_INFO.GameplayType == 1) { return Number(cc.fx.GameConfig.GM_INFO.randomLevel) || 0; } return (Number(cc.fx.GameConfig.GM_INFO.level) || 0) + 1; } /** * 为 GameScene 中所有可交互按钮统一添加轻微震动。 * 使用根节点捕获事件,以覆盖运行时动态创建的按钮。 */ private onGameButtonTouchEnd(event: cc.Event.EventTouch) { let target = event && event.target as cc.Node; while (target) { const button = target.getComponent(cc.Button); if (button) { // _pressed 为 false 时说明手指已移出按钮,不属于有效点击。 if (button.interactable && button.enabledInHierarchy && (button as any)._pressed !== false) { try { cc.fx.GameTool.setVibrate("light", 1); } catch (error) { try { console.warn("GameScene 按钮轻振动调用失败:", error); } catch (e) { } } } return; } if (target === this.node) { return; } target = target.parent; } } // LIFE-CYCLE CALLBACKS: onLoad() { SceneManager.activeInstance = this; this.isDestroying = false; this.dynamicPopupLoads = {}; this.dynamicPopupRetries = {}; this.dynamicPopupStates = {}; this.popupBundleWarmupStarted = false; this.refreshGameplayInfo(); this.ensureLoadingOnTop(); if (!this.ensureGameShop()) cc.error("GameScene 缺少顶部商店入口 Game/shop"); const intercept = this.ensureDynamicPopupIntercept(); if (intercept) intercept.active = false; this.winLevelName = this.resolveWinLevelName(); this.node.on(cc.Node.EventType.TOUCH_END, this.onGameButtonTouchEnd, this, true); this.share_time = true; cc.game.setFrameRate(63); this.changeBg(); // 预加载 shop 预制体 SceneManager.clearInvalidShopPrefabCaches(); if (!SceneManager.cachedShopPrefab || !SceneManager.cachedRewardPrefab || !SceneManager.cachedMonthlyCardPrefab) { cc.assetManager.loadBundle('shop', (err: Error, bundle: cc.AssetManager.Bundle) => { if (err) { cc.error(err.message || err); return; } if (!SceneManager.cachedShopPrefab) { bundle.load('prefab/shop', cc.Prefab, (err: Error, prefab: cc.Prefab) => { if (err) { cc.error(err.message || err); return; } SceneManager.cachedShopPrefab = prefab; }); } if (!SceneManager.cachedRewardPrefab) { bundle.load('prefab/RewardWindow', cc.Prefab, (err: Error, prefab: cc.Prefab) => { if (err) { cc.error(err.message || err); return; } SceneManager.cachedRewardPrefab = prefab; }); } if (!SceneManager.cachedMonthlyCardPrefab) { bundle.load('prefab/monthlyCard', cc.Prefab, (err: Error, prefab: cc.Prefab) => { if (err) { cc.error(err.message || err); return; } SceneManager.cachedMonthlyCardPrefab = prefab; }); } }); } if (!cc.fx.GameTool.isLowMemoryIOSDevice()) { setTimeout(() => { cc.director.preloadScene("HomeScene", (err) => { if (err) { // console.error('预加载 HomeScene 场景失败:', err); return; } // console.log('成功预加载 HomeScene 场景'); }); }, 5000); } else { console.log("[Memory] low-memory iOS: skip HomeScene preload in SceneManager"); } } onDestroy() { this.isDestroying = true; if (SceneManager.activeInstance === this) SceneManager.activeInstance = null; this.unbindLosePanel(); this.unbindPropWindowPanel(); const retainPopupBundles = !!cc.fx.GameTool && !!cc.fx.GameTool.shouldRetainGameplayPopupBundlesForGameSceneReload && cc.fx.GameTool.shouldRetainGameplayPopupBundlesForGameSceneReload(); Object.keys(this.dynamicPopupStates || {}).forEach((popupName) => this.releaseDynamicPopup(popupName, true, retainPopupBundles)); const introBundle = cc.assetManager.getBundle("gameplay_intro"); if (introBundle) this.releaseDynamicPopupBundleIfUnused("gameplay_intro", introBundle); if (!retainPopupBundles) { ["pause", "NewMode", "propWindow", "lose"].forEach((bundleName) => { const bundle = cc.assetManager.getBundle(bundleName); if (bundle) this.releaseDynamicPopupBundleIfUnused(bundleName, bundle); }); } else { console.log("[PopupBundle] retained pause/NewMode/propWindow/lose for next GameScene"); } this.dynamicPopupStates = {}; this.dynamicPopupLoads = {}; this.dynamicPopupRetries = {}; this.clearDynamicPopupRetry(); const intercept = this.node && this.node.getChildByName("intercept"); if (intercept) intercept.active = false; if (this.node && cc.isValid(this.node, true)) { this.node.off(cc.Node.EventType.TOUCH_END, this.onGameButtonTouchEnd, this, true); } this.clearFlyCoins(); this.disposeJungleTreasure(false); } clearFlyCoins() { if (this.flyCoinLayer && cc.isValid(this.flyCoinLayer)) { cc.Tween.stopAllByTarget(this.flyCoinLayer); for (let i = 0; i < this.flyCoinLayer.childrenCount; i++) { const child = this.flyCoinLayer.children[i]; if (child && cc.isValid(child)) { cc.Tween.stopAllByTarget(child); } } this.flyCoinLayer.removeAllChildren(); } } private updateGameCoinDisplay(displayCoin: number = cc.fx.GameConfig.GM_INFO.coin): void { if (this.isDestroying) return; const map = MapConroler && MapConroler._instance; if (map && cc.isValid(map, true) && map.node && cc.isValid(map.node, true)) { map.updateCoin(displayCoin); } } getWinCoinFlyTarget() { if (!this.node || !cc.isValid(this.node)) { return null; } const shop = this.ensureGameShop(); return shop && shop.getChildByName("coins"); } //更新关卡获得金币 updateWinCoinIconState() { if (!this.winCoin || !cc.isValid(this.winCoin) || !this.winCoin.parent) { return; } const layerTwoOpen = cc.fx.GameConfig.LEGACY_LAYER_TWO_VARIANT_B_ENABLED; const coinIcon = this.winCoin.parent.getChildByName("coinIcon"); const meijinbi = this.winCoin.parent.getChildByName("meijinbi"); if (coinIcon) { coinIcon.active = layerTwoOpen; } if (meijinbi) { meijinbi.active = !layerTwoOpen; } } updateWinCoin() { if (!this.winCoin || !cc.isValid(this.winCoin)) { return; } console.log("更新关卡获得金币"); this.updateWinCoinIconState(); let winCoin = null; winCoin = 40; winCoin = winCoin * cc.fx.GameConfig.GM_INFO.doubleCoin; if (winCoin > 0) { NumberToImage.numberToImageNodes(winCoin, 50, 8, "half_black_", this.winCoin, false); } else { this.winCoin.active = false; } } //更新下一关获得金币 playWinCoinFly(winRewardCoin: number) { if (this.isDestroying || !this.node || !cc.isValid(this.node)) { return; } if (!this.winCoin || !cc.isValid(this.winCoin) || (!this.flyCoinPrefab && !this.flyCoinSpriteFrame)) { console.warn("playWinCoinFly skip: winCoin or fly coin asset missing"); this.updateGameCoinDisplay(); return; } const targetNode = this.getWinCoinFlyTarget(); if (!targetNode || !cc.isValid(targetNode)) { console.warn("playWinCoinFly skip: target Game/shop/coins missing"); this.updateGameCoinDisplay(); return; } const coinParent = this.winCoin.parent && this.winCoin.parent.getChildByName("beijing"); if (!coinParent || !cc.isValid(coinParent)) { console.warn("playWinCoinFly skip: Win/tween/tiaodik/beijing missing"); this.updateGameCoinDisplay(); return; } this.clearFlyCoins(); if (!this.flyCoinLayer || !cc.isValid(this.flyCoinLayer)) { this.flyCoinLayer = coinParent.getChildByName("flyCoinLayer"); } if (!this.flyCoinLayer || !cc.isValid(this.flyCoinLayer)) { this.flyCoinLayer = new cc.Node("flyCoinLayer"); } const layer = this.flyCoinLayer; layer.parent = coinParent; layer.setPosition(0, 0); layer.active = true; layer.zIndex = 9999; layer.removeAllChildren(); const startWorld = this.winCoin.convertToWorldSpaceAR(cc.v2(0, 0)); const targetWorld = targetNode.convertToWorldSpaceAR(cc.v2(0, 0)); // 散开偏移沿用 Canvas 坐标,再转换到 beijing 下,避免父节点变换影响路径。 const startPos = this.node.convertToNodeSpaceAR(startWorld); const targetPos = layer.convertToNodeSpaceAR(targetWorld); const count = Math.max(1, Math.floor(Number(this.flyCoinCount) || 1)); const speed = 2; const duration = Math.max(0.1, Number(this.flyCoinDuration) || 0.55) / speed; const interval = Math.max(0, Number(this.flyCoinInterval) || 0) / speed; const finalCoin = Math.max(0, Math.floor(Number(cc.fx.GameConfig.GM_INFO.coin) || 0)); const rewardCoin = Math.max(0, Math.floor(Number(winRewardCoin) || 0)); const startCoin = Math.max(0, finalCoin - rewardCoin); let arrivedCount = 0; this.updateGameCoinDisplay(startCoin); console.log("playWinCoinFly start", count, startPos, targetPos); for (let i = 0; i < count; i++) { if (this.isDestroying || !cc.isValid(layer)) { return; } const coin = this.createFlyCoinNode(); if (!coin || !cc.isValid(layer)) { continue; } layer.addChild(coin); coin.active = true; coin.opacity = 255; coin.zIndex = 1; const startX = startPos.x + (Math.random() - 0.5) * 120 + 50; const startY = startPos.y + (Math.random() - 0.5) * 70 + 150; coin.setPosition(layer.convertToNodeSpaceAR(this.node.convertToWorldSpaceAR(cc.v2(startX, startY)))); coin.scale = 0; const midPos = layer.convertToNodeSpaceAR(this.node.convertToWorldSpaceAR(cc.v2( startX + (Math.random() - 0.5) * 180, startY + 80 + Math.random() * 80 ))); cc.tween(coin) .delay(i * interval) .to(0.12 / speed, { scale: 1 }, { easing: "backOut" }) .to(duration * 0.35, { x: midPos.x, y: midPos.y }, { easing: "sineOut" }) .to(duration * 0.65, { x: targetPos.x, y: targetPos.y, scale: 0.35 }, { easing: "sineIn" }) .call(() => { arrivedCount++; const displayCoin = arrivedCount >= count ? finalCoin : startCoin + Math.floor(rewardCoin * arrivedCount / count); this.updateGameCoinDisplay(displayCoin); if (coin && cc.isValid(coin)) { coin.destroy(); } }) .start(); } } createFlyCoinNode() { if (this.flyCoinPrefab) { return cc.instantiate(this.flyCoinPrefab); } if (!this.flyCoinSpriteFrame) { return null; } const node = new cc.Node("flyCoin"); const sprite = node.addComponent(cc.Sprite); sprite.spriteFrame = this.flyCoinSpriteFrame; return node; } updateNextMode() { if (!this.nextMode || !cc.isValid(this.nextMode) || !this.nextMode.parent) { return; } let nextMode = -1; this.nextMode.active = false; this.nextMode.parent.getChildByName("nextLabel").active = false; const currentLevel = cc.fx.GameConfig.GM_INFO.level + 1; const newLevelConfig = cc.fx.GameConfig.NEW_LEVEL || []; for (let i = 0; i < newLevelConfig.length; i++) { const newLevel = Number(newLevelConfig[i].level); if (newLevel > currentLevel) { const diff = newLevel - currentLevel; if (nextMode < 0 || diff < nextMode) { nextMode = diff; } } if (newLevel == currentLevel) { nextMode = 0; this.nextMode.active = false; this.nextMode.parent.getChildByName("nextLabel").active = false; } } if (nextMode > 0 && nextMode <= 10) { this.nextMode.active = true; this.nextMode.parent.getChildByName("nextLabel").active = true; NumberToImage.numberToImageNodes(nextMode, 35, 8, "time_", this.nextMode, false); } else { this.nextMode.active = false; this.nextMode.parent.getChildByName("nextLabel").active = false; } } changeBg() { let number = Math.floor(Math.random() * 8) + 1; const path = 'bg/bg' + number; cc.resources.load(path, cc.SpriteFrame, (err, spriteFrame: cc.SpriteFrame) => { if (err) { console.error('动态加载背景图失败:', err); return; } this.node.getChildByName("Game").getChildByName("bg").getComponent(cc.Sprite).spriteFrame = spriteFrame; }) //time金币数量 if (this.timeCoin && cc.isValid(this.timeCoin)) { NumberToImage.numberToImageNodes(1000, 25, 15, "button_", this.timeCoin, false); } this.refreshWinPanelInfo(); } loadParticleEffects() { const path = 'Particle'; cc.resources.loadDir(path, cc.ParticleAsset, (err, assets: cc.ParticleAsset[]) => { if (err) { console.error('动态加载粒子特效失败:', err); return; } this.particleEffects = assets; this.setParticleSort(); this.load3 = true; //console.log('粒子特效加载成功,共加载了', this.particleEffects.length, '个粒子特效'); }); } setWallPrefabSort() { const order = ['down', 'downLeft', 'downRight', 'left', 'right', 'up', 'upLeft', 'upRight']; this.Wall_Prefab.sort((a, b) => { const indexA = order.indexOf(a.name); const indexB = order.indexOf(b.name); if (indexA === -1) return 1; if (indexB === -1) return -1; return indexA - indexB; }); } setParticleSort() { const order = ['top', 'bot', 'rig', 'lef']; this.particleEffects.sort((a, b) => { // console.log(a.name.substr(0,3),b.name.substr(0,3)); const indexA = order.indexOf(a.name.substr(0, 3)); const indexB = order.indexOf(b.name.substr(0, 3)); if (indexA === -1) return 1; if (indexB === -1) return -1; return indexA - indexB; }); } setSort() { this.Block_Array.sort((a, b) => { // 从名称中提取数字部分 const numberA = parseInt(a.name.match(/\d+/)?.[0] || '0', 10); const numberB = parseInt(b.name.match(/\d+/)?.[0] || '0', 10); return numberA - numberB; }); } /** 关卡数据在进入场景前已加载;按完整关卡保存列表,消除道具后仍可查阅。 */ private refreshGameplayInfo() { const button = cc.find("Game/GameNode/Top/tip/GameplayInfoButton", this.node); const apply = (config: any[]) => { this.levelGameplay = getLevelGameplay(config, cc.fx.GameConfig); if (button && cc.isValid(button)) button.active = this.levelGameplay.length > 0; }; if (cc.fx.GameConfig.NEW_LEVEL) { apply(cc.fx.GameConfig.NEW_LEVEL); } else { // 全局介绍配置是异步读取的,首次进场景时可能仍在加载。 if (button) button.active = false; cc.resources.load("Json/NEW_LEVEL", cc.JsonAsset, (error, asset: cc.JsonAsset) => { if (!this.isPopupRuntimeAlive()) return; if (error) { cc.error("Gameplay introductions failed to load", error); return; } apply(asset.json.NEW_LEVEL); }); } } /** * 问号 Button 的点击入口。场景按钮需绑定这个方法,Tip Panel 属性需绑定容器节点。 * 顺序:检查能否打开 → 暂停游戏 → 加载/复用预制体 → 交给 GameplayIntro 显示内容。 */ openGameplayIntro() { if (!this.isPopupRuntimeAlive() || this.isPauseOpen()) return; if (!this.levelGameplay.length) return; const map = MapConroler._instance; if (!map || map.gameOver || map.shopPause) return; if (!this.tipPanel || !cc.isValid(this.tipPanel)) { cc.error("GameplayIntro: SceneManager.tipPanel is not assigned"); return; } // 关闭或加载失败时尝试恢复;仍有冰冻、其它暂停面板、商城或已结束时不能强行恢复。 const resume = () => { if (!this.isPopupRuntimeAlive()) return; if (!map.iceTrue() && !this.isPauseOpen() && !map.shopPause && !map.gameOver) { map.pause = false; if (map.gameStart) map.startBoom(); } }; // 暂停游戏,并停止场上炸弹计时,避免阅读玩法期间仍消耗倒计时。 map.pause = true; map.stopBoom(); this.tipPanel.active = true; this.tipPanel.zIndex = cc.macro.MAX_ZINDEX - 2; // 参数依次为:弹窗缓存名、资源包名、包内预制体路径、挂载父节点、完成回调。 // 通用加载器首次会 instantiate 并挂载到 tipPanel,以后返回已经创建的根节点。 this.loadDynamicPopup("gameplayIntro", "gameplay_intro", "prefab/GameplayIntro", this.tipPanel, (error, root) => { // 无论成功失败都结束加载状态,防止暂停检测一直误认为还在加载。 this.finishDynamicPopupLoad("gameplayIntro"); if (error || !root) { cc.error("GameplayIntro prefab failed to load", error); resume(); return; } const panel = root.getComponent("GameplayIntro") as GameplayIntro; if (!panel) { cc.error("GameplayIntro prefab is missing its controller"); resume(); return; } // 同一实例只绑定一次关闭回调,避免重开多次后一次关闭触发多次恢复。 if (this.gameplayIntro !== panel) { this.gameplayIntro = panel; root.on("intro-closed", resume); } panel.open(this.levelGameplay); }); } startGame() { let jg = cc.fx.GameTool.getAuthorizationAvatar(); if (!jg) { cc.fx.GameTool.getUserAvatar(() => { }) } cc.director.loadScene("HomeScene", (err) => { if (err) { console.error('加载 HomeScene 场景失败:', err); } else { // console.log('成功切换到 HomeScene 场景'); cc.director.loadScene("HomeScene"); } }); } returnHome() { if (!this.isPopupRuntimeAlive()) return; const pauseRoot = this.getDynamicPopup("pause"); const pauseContent = pauseRoot && pauseRoot.getChildByName("pause"); const continueBtn = pauseContent && pauseContent.getChildByName("btn"); const continueControl = continueBtn && continueBtn.getComponent("btnControl"); if (continueControl && continueControl._touch && MapConroler && MapConroler._instance) { continueControl.setTouch(false); this.closePause(); if (MapConroler._instance.gameStart == true) { // MiniGameSdk.API.showToast("体力值减少"); if (MapConroler._instance.count_Time) { let overTime = Date.now(); let count_Time = overTime - MapConroler._instance.count_Time; let add_Time = MapConroler._instance.add_Time; let endLevel = false; if (cc.fx.GameTool.maxLevel() && cc.fx.GameConfig.GM_INFO.GameplayType == 1) { endLevel = true; } let data = { time: count_Time, add_Time: add_Time, is_frenzy: MapConroler._instance.is_frenzy, result: "give_up", infinity: endLevel, total_steps_count: MapConroler._instance.total_steps_count, } cc.fx.GameTool.shushu_Track("finish_stage", data); let data2 = { is_frenzy: MapConroler._instance.is_frenzy, count: cc.fx.GameConfig.GM_INFO.winStreak } cc.fx.GameTool.shushu_Track("hammer_frenzy", data2); } } cc.fx.AudioManager._instance.playEffect("zhuan1", null); this.node.getChildByName("zhuanchang").active = true; this.node.getChildByName("zhuanchang").getComponent(sp.Skeleton).setAnimation(1, "up", false); cc.director.preloadScene("HomeScene", (err, asset) => { if (err) { console.error('动态加载 Prefab 失败:', err); return; } }); setTimeout(() => { cc.fx.GameConfig.GM_INFO.otherLevel = 0; cc.fx.GameConfig.GM_INFO.otherUid = ""; let jg = cc.fx.GameTool.getAuthorizationAvatar(); if (!jg) { cc.fx.GameTool.getUserAvatar(() => { }) } cc.director.loadScene("HomeScene"); }, 1200); } } destroyNodesInFrames(nodes: cc.Node[], callback: () => void) { const BATCH_SIZE = 10; // 每帧销毁的节点数量 let index = 0; const destroyBatch = () => { let count = 0; while (index < nodes.length && count < BATCH_SIZE) { const node = nodes[index]; if (node) { node.active = false; } index++; count++; } if (index < nodes.length) { this.scheduleOnce(destroyBatch, 6); } else { callback(); } }; destroyBatch(); } // 改进后的切换场景方法 switchToEmptyScene() { const allNodes = cc.director.getScene().children; this.destroyNodesInFrames(allNodes, () => { let jg = cc.fx.GameTool.getAuthorizationAvatar(); if (!jg) { cc.fx.GameTool.getUserAvatar(() => { }) } cc.director.loadScene("HomeScene"); }); } private returningHome: boolean = false; returnHomeScene(event?: cc.Event, customEventData?: string) { if (!this.isPopupRuntimeAlive() || this.returningHome) return; this.returningHome = true; cc.fx.GameConfig.locatePositionOnHome = customEventData === "ruzhi"; let started = false; const goHome = () => { if (started || !this.isPopupRuntimeAlive()) return; started = true; const accepted = cc.director.loadScene("HomeScene", (error) => { if (error && this.isPopupRuntimeAlive()) { this.returningHome = false; cc.error("returnHomeScene failed:", error); } }); if (!accepted) this.returningHome = false; }; try { cc.fx.GameTool.shushu_Track("where_am_i", {}); } catch (error) { cc.warn("where_am_i tracking failed:", error); } try { if (customEventData === "ruzhi" && !cc.fx.GameTool.getAuthorizationAvatar()) { // 授权结果不影响返回首页,也不等待头像昵称上传。 cc.fx.GameTool.getUserAvatar(goHome); } else { goHome(); } } catch (error) { cc.warn("returnHomeScene avatar request failed:", error); goHome(); } } openPause() { if (!this.isPopupRuntimeAlive()) return; cc.fx.AudioManager._instance.playEffect("anniu_little", null); const pauseControl = this.pause && this.pause.getComponent("btnControl"); if (!pauseControl || !pauseControl._touch) return; pauseControl.setTouch(false); if (MapConroler && MapConroler._instance) { MapConroler._instance.pause = true; MapConroler._instance.stopBoom(); } this.loadPausePanel(); } private loadPausePanel() { if (!this.isPopupRuntimeAlive()) return; this.loadDynamicPopup("pause", "pause", "prefab/Pause", this.node, (error, pauseRoot) => { if (!this.isPopupRuntimeAlive()) return; if (error || !pauseRoot) { this.showPauseLoadError(error || new Error("Pause prefab is missing")); return; } try { if (!this.bindPausePanel(pauseRoot)) throw new Error("Pause runtime binding failed"); const pauseContent = pauseRoot.getChildByName("pause"); pauseRoot.active = true; pauseContent.scale = 0.3; cc.tween(pauseContent) .to(0.2, { scale: 1.05 }, { easing: 'backOut' }) .to(0.15, { scale: 1.0 }, { easing: 'sineOut' }) .start(); this.finishDynamicPopupLoad("pause"); } catch (bindError) { this.releasePausePanel(true); this.showPauseLoadError(bindError as Error); } }); } private showPauseLoadError(error: Error) { if (!this.isPopupRuntimeAlive()) return; cc.error("Pause cannot open:", error && (error.message || error)); this.showDynamicPopupLoadError("pause", "暂停界面资源", () => this.loadPausePanel()); } closeShop() { if (this.shopNode) { this.shopNode.active = false; if (MapConroler && MapConroler._instance) { MapConroler._instance.shopPause = false; var pause = MapConroler._instance.iceTrue(); if (pause == false && !this.isPauseOpen()) { if (MapConroler && MapConroler._instance) MapConroler._instance.pause = false; if (MapConroler._instance.gameStart == true) MapConroler._instance.startBoom(); } } } } /** GameScene 商城入口打开 Jungle Treasure。 */ openJungleTreasure(event?: cc.Event, customEventData?: string) { let level = Number(cc.fx.GameConfig.GM_INFO.level) || 0; if (!cc.fx.GameConfig.GM_INFO.canIos || cc.fx.GameConfig.GM_INFO.jungleTreasureAvailable !== true || level < cc.fx.GameConfig.getJungleTreasureUnlockLevel() || this.jungleTreasureDataLoading || this.jungleTreasureBundleLoading || this.jungleTreasureNode) { return; } this.jungleTreasureOpenSource = customEventData === "jump_window" ? "jump_window" : "shop_page"; this.openLoad(); this.jungleTreasureDataLoading = true; let requestId = ++this.jungleTreasureRequestId; this.clearJungleTreasureRequestTimeout(); this.jungleTreasureRequestTimeout = setTimeout(() => { if (requestId !== this.jungleTreasureRequestId || !this.jungleTreasureDataLoading) { return; } this.jungleTreasureRequestId++; this.finishJungleTreasureOpenFailure("获取活动数据超时,请稍后重试"); }, 8000); Utils.getJungleTreasure((response) => { if (requestId !== this.jungleTreasureRequestId || this.isDestroying) { return; } this.clearJungleTreasureRequestTimeout(); this.jungleTreasureDataLoading = false; let data = response && response.code === 1 ? response.data : null; if (data && data.available === false) { cc.fx.GameConfig.GM_INFO.jungleTreasureAvailable = false; this.closeLoad(); return; } let config = data && data.config; let tierStates = data && data.tierStates; if (!config || !Array.isArray(config.tiers) || !Array.isArray(tierStates) || tierStates.length < config.tiers.length) { this.finishJungleTreasureOpenFailure( (response && (response.msg || response.message)) || "获取活动数据失败", ); return; } this.jungleTreasureServerData = data; cc.fx.GameConfig.GM_INFO.jungleTreasureAvailable = true; this.cacheJungleTreasureState(data); this.loadJungleTreasureBundle(); }); } closeJungleTreasure() { this.closeLoad(); this.disposeJungleTreasure(true); } /** Jungle 领奖后刷新 GameScene 的金币和三种局内道具显示。 */ refreshJungleRewardDisplay(): void { if (MapConroler && MapConroler._instance && MapConroler._instance.node && cc.isValid(MapConroler._instance.node)) { MapConroler._instance.updateCoin(); MapConroler._instance.setPropNum(); } } private loadJungleTreasureBundle(): void { if (SceneManager.cachedJungleTreasurePrefab) { this.showJungleTreasure(); return; } this.jungleTreasureBundleLoading = true; let existingBundle = cc.assetManager.getBundle("jungle_treasure"); if (existingBundle) { this.loadJungleTreasurePrefab(existingBundle); return; } cc.assetManager.loadBundle("jungle_treasure", (bundleError: Error, bundle: cc.AssetManager.Bundle) => { if (this.isDestroying) { return; } if (bundleError || !bundle) { this.jungleTreasureBundleLoading = false; this.finishJungleTreasureOpenFailure("加载活动资源失败,请稍后重试"); cc.error("GameScene 加载 jungle_treasure 分包失败:", bundleError); return; } this.loadJungleTreasurePrefab(bundle); }); } private loadJungleTreasurePrefab(bundle: cc.AssetManager.Bundle): void { bundle.load("prefab/jungle", cc.Prefab, (prefabError: Error, prefab: cc.Prefab) => { if (this.isDestroying) { return; } this.jungleTreasureBundleLoading = false; if (prefabError || !prefab) { this.finishJungleTreasureOpenFailure("加载活动界面失败,请稍后重试"); cc.error("GameScene 加载 Jungle prefab 失败:", prefabError); return; } SceneManager.cachedJungleTreasurePrefab = prefab; this.showJungleTreasure(); }); } private showJungleTreasure(): void { if (!SceneManager.cachedJungleTreasurePrefab || !this.jungleTreasureServerData || this.isDestroying) { this.finishJungleTreasureOpenFailure("活动界面初始化失败"); return; } try { let jungleNode = cc.instantiate(SceneManager.cachedJungleTreasurePrefab); this.jungleTreasureNode = jungleNode; this.node.addChild(jungleNode); jungleNode.zIndex = 1001; jungleNode.on("jungle-close", this.closeJungleTreasure, this); jungleNode.on("jungle-state-changed", this.onJungleTreasureStateChanged, this); this.ensureLoadingOnTop(); let manager: any = jungleNode.getComponent("jungle_Manager"); if (!manager || typeof manager.setServerData !== "function" || typeof manager.openActivity !== "function") { throw new Error("Jungle prefab 缺少 jungle_Manager 初始化方法"); } let initialized = manager.setServerData( this.jungleTreasureServerData.config, this.jungleTreasureServerData.tierStates, this.jungleTreasureOpenSource, ); if (!initialized) { throw new Error("Jungle 服务器配置初始化失败"); } manager.openActivity(); this.closeLoad(); } catch (error) { cc.error("GameScene Jungle 界面初始化异常:", error); this.disposeJungleTreasure(true); this.finishJungleTreasureOpenFailure("活动界面初始化失败"); } } private onJungleTreasureStateChanged(data: any): void { if (!data || !Array.isArray(data.tierStates)) { return; } if (!this.jungleTreasureServerData) { this.jungleTreasureServerData = {}; } this.jungleTreasureServerData.tierStates = data.tierStates.slice(); if (data.config) this.jungleTreasureServerData.config = data.config; if (data.expiresAt !== undefined) this.jungleTreasureServerData.expiresAt = data.expiresAt; if (data.serverNow !== undefined) this.jungleTreasureServerData.serverNow = data.serverNow; if (data.revision !== undefined) this.jungleTreasureServerData.revision = data.revision; if (data.updatedAt !== undefined) this.jungleTreasureServerData.updatedAt = data.updatedAt; this.cacheJungleTreasureState(data); } private cacheJungleTreasureState(data: any): void { if (!data || !Array.isArray(data.tierStates)) { return; } let gameInfo: any = cc.fx.GameConfig.GM_INFO; let state = gameInfo.jungleTreasureState && typeof gameInfo.jungleTreasureState === "object" ? gameInfo.jungleTreasureState : {}; state.tierStates = data.tierStates.slice(); if (data.expiresAt !== undefined) state.expiresAt = data.expiresAt; if (data.activityType !== undefined) state.activityType = data.activityType; if (data.periodKey !== undefined) state.periodKey = data.periodKey; if (data.startsAt !== undefined) state.startsAt = data.startsAt; if (data.revision !== undefined) state.revision = data.revision; if (data.updatedAt !== undefined) state.updatedAt = data.updatedAt; gameInfo.jungleTreasureState = state; if (data.serverNow !== undefined) { gameInfo.jungleTreasureServerNow = data.serverNow; } } private finishJungleTreasureOpenFailure(message: string): void { this.jungleTreasureDataLoading = false; this.jungleTreasureBundleLoading = false; this.clearJungleTreasureRequestTimeout(); this.closeLoad(); MiniGameSdk.API.showToast(message); } private clearJungleTreasureRequestTimeout(): void { if (this.jungleTreasureRequestTimeout) { clearTimeout(this.jungleTreasureRequestTimeout); this.jungleTreasureRequestTimeout = null; } } private disposeJungleTreasure(releaseOnNextFrame: boolean): void { this.jungleTreasureRequestId++; this.jungleTreasureDataLoading = false; this.jungleTreasureBundleLoading = false; this.clearJungleTreasureRequestTimeout(); let jungleNode = this.jungleTreasureNode; this.jungleTreasureNode = null; this.jungleTreasureServerData = null; SceneManager.cachedJungleTreasurePrefab = null; if (jungleNode && cc.isValid(jungleNode)) { jungleNode.off("jungle-close", this.closeJungleTreasure, this); jungleNode.off("jungle-state-changed", this.onJungleTreasureStateChanged, this); jungleNode.destroy(); } let releaseBundle = () => { let bundle = cc.assetManager.getBundle("jungle_treasure"); if (bundle) { bundle.releaseAll(); cc.assetManager.removeBundle(bundle); } }; if (releaseOnNextFrame && !this.isDestroying) { this.scheduleOnce(releaseBundle, 0); } else { releaseBundle(); } } closePause() { cc.fx.AudioManager._instance.playEffect("anniu_little", null); this.releasePausePanel(); if (MapConroler && MapConroler._instance) { var pause = MapConroler._instance.iceTrue(); if (pause == false && !this.isPauseOpen() && !MapConroler._instance.shopPause) { if (MapConroler && MapConroler._instance) MapConroler._instance.pause = false; if (MapConroler._instance.gameStart == true) MapConroler._instance.startBoom(); } } } openPropBuy(name: string) { if (!this.isPopupRuntimeAlive()) return; cc.fx.GameTool.tryReactivateStarterPack("level_purchase"); if (MapConroler && MapConroler._instance) { MapConroler._instance.pause = true; MapConroler._instance.stopBoom(); } this.btnName = name; cc.fx.AudioManager._instance.playEffect("tanchuang", null); const gameRoot = this.node && this.node.getChildByName("Game"); this.loadDynamicPopup("propWindow", "propWindow", "prefab/PropWindow", gameRoot, (error, propRoot) => { if (!this.isPopupRuntimeAlive()) return; if (error || !propRoot) { this.showPropWindowLoadError(error || new Error("propWindow prefab is missing"), name); return; } try { if (!this.bindPropWindowPanel(propRoot)) throw new Error("propWindow runtime binding failed"); const prop = propRoot.getChildByName("prop"); const target = prop && prop.getChildByName(name); if (!target) throw new Error(`propWindow item is missing: ${name}`); ["freeze", "hammer", "magic"].forEach((itemName) => { prop.getChildByName(itemName).active = itemName === name; }); prop.getChildByName("buy_Btn").getComponent("btnControl").setTouch(true); propRoot.active = true; prop.scale = 0.3; cc.tween(prop) .to(0.2, { scale: 1.05 }, { easing: 'backOut' }) .to(0.15, { scale: 1.0 }, { easing: 'sineOut' }) .start(); this.finishDynamicPopupLoad("propWindow"); } catch (bindError) { this.releasePropWindowPanel(true); this.showPropWindowLoadError(bindError as Error, name); } }); } private showPropWindowLoadError(error: Error, name: string) { if (!this.isPopupRuntimeAlive()) return; cc.error("propWindow cannot open:", error && (error.message || error)); this.showDynamicPopupLoadError( "propWindow", "道具购买资源", () => this.openPropBuy(name) ); } clickBtn() { if (!this.isPopupRuntimeAlive()) return; cc.fx.AudioManager._instance.playEffect("anniu_Big", null); const propRoot = this.getDynamicPopup("propWindow"); const prop = propRoot && propRoot.getChildByName("prop"); const buyBtn = prop && prop.getChildByName("buy_Btn"); const buyControl = buyBtn && buyBtn.getComponent("btnControl"); const map = MapConroler && MapConroler._instance; if (buyControl && buyControl._touch && map && cc.isValid(map, true)) { buyControl.setTouch(false); if (this.btnName == "freeze") map.buyFreeze(); else if (this.btnName == "hammer") map.buyHammer(); else if (this.btnName == "magic") map.buyMagic(); } } resetBtn() { if (!this.isPopupRuntimeAlive()) return; const propRoot = this.getDynamicPopup("propWindow"); const prop = propRoot && propRoot.getChildByName("prop"); const buyBtn = prop && prop.getChildByName("buy_Btn"); const buyControl = buyBtn && buyBtn.getComponent("btnControl"); if (buyControl) buyControl.setTouch(true); } /** Reuse the authored gift prefab when reactivation happens inside a level. */ openStarterPack() { if (!this.isPopupRuntimeAlive() || this.starterPackOpening || starterPackRemaining() <= 0) return; const existing = this.getDynamicPopup("starterPack"); if (existing && existing.active) return; const map = MapConroler._instance; if (!map || map.gameWin) return; const wasPaused = map.pause; const wasFrozen = map.iceTrue(); const wasShopPaused = map.shopPause; map.pause = true; map.shopPause = true; map.stopBoom(); this.starterPackOpening = true; const restore = () => { if (!this.isPopupRuntimeAlive() || !cc.isValid(map.node)) return; map.shopPause = wasShopPaused || !!(this.shopNode && this.shopNode.active); const propWindow = this.getDynamicPopup("propWindow"); if ((!wasPaused || wasFrozen) && !map.shopPause && !map.gameOver && !map.gameWin && !(propWindow && propWindow.active) && !map.iceTrue() && !this.isPauseOpen()) { map.pause = false; if (map.gameStart) map.startBoom(); } }; this.loadDynamicPopup("starterPack", "action_bundle", "prefab/newbieGift", this.node, (error, root) => { this.starterPackOpening = false; this.finishDynamicPopupLoad("starterPack"); if (error || !root || starterPackRemaining() <= 0 || map.gameWin) { restore(); return; } root.zIndex = cc.macro.MAX_ZINDEX - 1; root.active = true; root.once("starter-pack-closed", restore); root.getComponent("NewbieGift").init(!existing); cc.fx.GameTool.shushu_Track("interface_exposure", { interface_id: "NewbieGift", reason: "auto", is_first_exposure: !existing, entry_point: "reactivate_level", }); }); } //打开商店 openShop(event, customEventData?: string) { if (!this.isPopupRuntimeAlive()) return; console.log("进入sceneManager openShop"); if (!cc.fx.GameConfig.GM_INFO.canIos) { return; } cc.fx.GameTool.tryReactivateStarterPack("level_purchase"); SceneManager.clearInvalidShopPrefabCaches(); if (!SceneManager.cachedShopPrefab) { cc.error('Shop prefab is not loaded yet.'); return; } Utils.getShopDouble(res => { if (!this.isPopupRuntimeAlive()) return; if (res.code === 1) { let eventName = ""; let btnName = "" if (customEventData !== undefined) { btnName = customEventData; eventName = "click" } else { btnName = "shop_level"; eventName = "auto"; } let dataInfo = { interface_id: "shop", // 界面名称 reason: eventName, // 事件名称 is_first_exposure: false, // 是否首次曝光 entry_point: btnName, // 按钮名称 } cc.fx.GameTool.shushu_Track("interface_exposure", dataInfo); cc.fx.GameConfig.GM_INFO.shopDouble = res.data.shopDouble; if (MapConroler && MapConroler._instance) { MapConroler._instance.shopPause = true; MapConroler._instance.pause = true; MapConroler._instance.stopBoom(); } if (!this.shopNode) { // 第一次使用,创建节点 this.shopNode = cc.instantiate(SceneManager.cachedShopPrefab); if (this.node && this.shopNode) this.node.addChild(this.shopNode); this.shopNode.getComponent("shop").init(res.data.shopDouble); } else { // 非第一次使用,直接激活节点 this.shopNode.active = true; this.shopNode.getComponent("shop").init(res.data.shopDouble); } // console.log("shopNode parent:", this.shopNode.parent); } }); } //打开月卡 openMonthlyCard(customEventData?: string) { SceneManager.clearInvalidShopPrefabCaches(); if (!SceneManager.cachedMonthlyCardPrefab) { cc.error('MonthlyCard prefab is not loaded yet.'); return; } let eventName = ""; let btnName = "" if (customEventData !== undefined) { btnName = customEventData; eventName = "click" } else { btnName = "yicon_level"; eventName = "auto"; } let dataInfo = { interface_id: "monthlyCard", // 界面名称 reason: eventName, // 事件名称 is_first_exposure: false, // 是否首次曝光 entry_point: btnName, // 按钮名称 } cc.fx.GameTool.shushu_Track("interface_exposure", dataInfo); if (!this.monthlyCardNode) { // 第一次使用,创建节点 this.monthlyCardNode = cc.instantiate(SceneManager.cachedMonthlyCardPrefab); if (this.node && this.monthlyCardNode) this.node.addChild(this.monthlyCardNode); this.monthlyCardNode.getComponent("monthlyCard").init(); this.monthlyCardNode.getComponent("monthlyCard").juwai = false; } else { // 非第一次使用,直接激活节点 this.monthlyCardNode.active = true; this.monthlyCardNode.getComponent("monthlyCard").init(); this.monthlyCardNode.getComponent("monthlyCard").juwai = false; } } updateCoin() { MapConroler._instance.updateCoin(); } closePropBuy(type) { if (!this.isPopupRuntimeAlive()) return; if (type == true) { } else { if (MapConroler && MapConroler._instance) { var pause = MapConroler._instance.iceTrue(); if (pause == false && !this.isPauseOpen() && !MapConroler._instance.shopPause) { if (MapConroler && MapConroler._instance) MapConroler._instance.pause = false; if (MapConroler._instance.gameStart == true) MapConroler._instance.startBoom(); } } } if (MapConroler && MapConroler._instance) { let freezeBtn = MapConroler._instance.node.parent.getChildByName("Bottom").getChildByName("timeBtn"); let hammerBtn = MapConroler._instance.node.parent.getChildByName("Bottom").getChildByName("destroyBtn"); let magicBtn = MapConroler._instance.node.parent.getChildByName("Bottom").getChildByName("magicBtn"); if (this.btnName == "freeze") freezeBtn.getComponent("btnControl").setTouch(true); else if (this.btnName == "hammer") hammerBtn.getComponent("btnControl").setTouch(true); else if (this.btnName == "magic") magicBtn.getComponent("btnControl").setTouch(true); } this.releasePropWindowPanel(); } openRewardWindow(data, callBack) { // console.log("_____________________打开奖励弹窗", data); SceneManager.clearInvalidShopPrefabCaches(); if (!SceneManager.cachedRewardPrefab) { cc.error('Reward prefab is not loaded yet.'); return; } if (!this.RewardNode) { // 第一次使用,创建节点 this.RewardNode = cc.instantiate(SceneManager.cachedRewardPrefab); if (this.node && this.RewardNode) this.node.addChild(this.RewardNode); this.RewardNode.zIndex = 99; this.RewardNode.getComponent("Reward").init(data, callBack); } else { this.RewardNode.destroy(); this.RewardNode = null; this.RewardNode = cc.instantiate(SceneManager.cachedRewardPrefab); if (this.node && this.RewardNode) this.node.addChild(this.RewardNode); this.RewardNode.zIndex = 99; this.RewardNode.getComponent("Reward").init(data, callBack); } this.RewardNode.zIndex = 1001; } shareFriend() { // const loseRoot = this.getDynamicPopup("lose"); // if (loseRoot && loseRoot.active) { // let shareTips = loseRoot.getChildByName("share_tips"); // // let isCheckboxChecked = cc.sys.localStorage.getItem("share_checkbox_state"); // let isCheckboxChecked = cc.fx.StorageMessage.getStorage("share_checkbox_state"); // if (isCheckboxChecked == 0 || isCheckboxChecked == "" || isCheckboxChecked == null || isCheckboxChecked == undefined) { // shareTips.active = true; // } else { // this.shareFriend2(); // } // } // else { // this.shareFriend2(); // } } shareFriend2() { // const loseRoot = this.getDynamicPopup("lose"); // if (loseRoot && loseRoot.active) { // let shareTips = loseRoot.getChildByName("share_tips"); // if (shareTips.active) { // shareTips.active = false; // let checkbox = shareTips.getChildByName("checkbox"); // let checkboxSelect = 0; // if (checkbox.getComponent(cc.Toggle).isChecked) { // checkboxSelect = 1; // } // cc.fx.StorageMessage.setStorage("share_checkbox_state", checkboxSelect); // } // } // else { // } // if (this.share_time) { // this.share_time = false; // console.log("设置分享链接......."); // let timeStamp = Date.now(); // let otherInfo = { // timeStamp: timeStamp, // otherLevel: (cc.fx.GameConfig.GM_INFO.level + 1), // } // cc.fx.StorageMessage.setStorage("otherLevel", otherInfo); // MiniGameSdk.API.shareGame(); // setTimeout(() => { // this.share_time = true; // }, 5000); // } } shareClose() { // const loseRoot = this.getDynamicPopup("lose"); // const shareTips = loseRoot && loseRoot.getChildByName("share_tips"); // if (!shareTips) return; // if (shareTips.active) { // shareTips.active = false; // let checkbox = shareTips.getChildByName("checkbox"); // let checkboxSelect = 0; // if (checkbox.getComponent(cc.Toggle).isChecked) { // checkboxSelect = 1; // } // cc.fx.StorageMessage.setStorage("share_checkbox_state", checkboxSelect); // } } openLoad() { const loading = this.ensureLoadingOnTop(); LoadingCatAnimation.play(loading); } closeLoad() { LoadingCatAnimation.stop(this.node.getChildByName("Loading")); } openMessage() { // let reddemCodeNode = cc.instantiate(this.messageNode); // let messageScrpit = reddemCodeNode.getComponent("Message"); // let nodeType = "commentNode"; // console.log("打开留言界面", nodeType); // let pos = 1; // messageScrpit.showNode(nodeType, pos); // this.node.addChild(reddemCodeNode); } openMessage2() { // 检查 messageNode 是否已经分配了预制体 // if (!this.messageNode) { // cc.error("messageNode 预制体未分配,请在编辑器中设置"); // return; // } // let reddemCodeNode = cc.instantiate(this.messageNode); // let messageScrpit = reddemCodeNode.getComponent("Message"); // if (!messageScrpit) { // cc.error("Message 组件未找到,请确保预制体中包含 Message 组件"); // return; // } // let nodeType = "likeComment"; // console.log("打开点赞界面", nodeType); // let pos = 1; // messageScrpit.showNode(nodeType, pos); // this.node.addChild(reddemCodeNode); } update(dt) { } public setPic(url, node) { cc.assetManager.loadRemote(url, { ext: '.png' }, (err, texture: cc.Texture2D) => { if (texture && node) { // node.getChildByName("pic").active = true; var sprite = node.getComponent(cc.Sprite); sprite.spriteFrame = new cc.SpriteFrame(texture); } else { } }) } }