From a00361d157e160807e221bd7aeeec40a7e6e6820 Mon Sep 17 00:00:00 2001 From: "COMPUTER\\EDY" <249929363@qq.com> Date: Thu, 9 Jul 2026 10:10:31 +0800 Subject: [PATCH] =?UTF-8?q?=E9=9F=B3=E9=A2=91=E8=B5=84=E6=BA=90=E5=8A=A0?= =?UTF-8?q?=E5=85=A5cdn=E6=96=B9=E6=A1=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/Scene/HomeScene.fire | 119 ++---- assets/Script/Block.ts | 18 +- assets/Script/GameManager.ts | 27 +- assets/Script/JiaZai.ts | 4 +- assets/Script/Map.ts | 41 ++ assets/Script/module/Config/GameConfig.ts | 2 +- assets/Script/module/Music/AudioManager.ts | 440 +++++++++++++++++++-- assets/UI/UI/comment_btn.png.meta | 4 +- assets/music.meta | 2 +- settings/wechatgame.json | 2 +- 10 files changed, 514 insertions(+), 145 deletions(-) diff --git a/assets/Scene/HomeScene.fire b/assets/Scene/HomeScene.fire index 9435e30..0b0d75d 100644 --- a/assets/Scene/HomeScene.fire +++ b/assets/Scene/HomeScene.fire @@ -139,96 +139,35 @@ "__id__": 2 }, "_enabled": true, - "audioGameBgm0": { - "__uuid__": "75b4f368-70b5-452d-9afd-ca7a6f1e2e60" - }, - "xiaochu": { - "__uuid__": "8d67c864-c7f4-4305-8b3b-2d66eb69e349" - }, - "hit": { - "__uuid__": "7575a6bd-a21d-491e-aad2-e40a189683e9" - }, - "down": { - "__uuid__": "ec293610-1767-46e8-b6b6-4a9a79ef2233" - }, - "fangxiang": null, - "build": null, - "win": { - "__uuid__": "9f2906df-601d-4dfa-a561-31745b62c9dd" - }, - "lose": null, - "anniu_Big": { - "__uuid__": "7362cac7-12b5-4704-af6d-5b110c5e9fcf" - }, - "anniu_little": { - "__uuid__": "a5518b0f-7ed3-46f0-aa78-93f02f4579cc" - }, - "tanchuang": { - "__uuid__": "1301aea4-4351-40d8-b4c4-e428613b3681" - }, - "zhuan1": { - "__uuid__": "e4d2f16e-7663-4ee5-a479-06ae0eae91d5" - }, - "zhuan2": { - "__uuid__": "998a24bd-ffa0-4f96-bc39-a9789f42bd9b" - }, - "adhesive": { - "__uuid__": "e0f2c22c-d8dd-4dad-9fa2-7b73d15a5518" - }, - "freezeBlock": { - "__uuid__": "9a056601-12c6-45dd-abde-a847d6f15247" - }, - "freezeDoor": { - "__uuid__": "20367986-5fa0-4558-9c73-861dc7bc1293" - }, - "hammer": { - "__uuid__": "a25b43cf-deba-4061-a5da-e87aa343269d" - }, - "lockBlock1": { - "__uuid__": "227cb730-4de8-41e2-93d1-f9e8cfdffd11" - }, - "lockBlock2": { - "__uuid__": "2cfcd2ad-bb5d-47c0-be0a-3a8135d89c1f" - }, - "lockDoor": { - "__uuid__": "25ca59d5-b522-472d-83e4-de16fd1cdd5c" - }, - "magic1": { - "__uuid__": "b8ec297a-cc2d-4a82-ac2c-345db075ce84" - }, - "magic2": { - "__uuid__": "e09b4699-ba82-4ac8-9894-d8b88640d041" - }, - "simpleColor": { - "__uuid__": "6f213771-d0ef-45ba-b87d-e878f291886b" - }, - "stacking": { - "__uuid__": "2c418573-4beb-49f8-bafe-6a9ae7906195" - }, - "starBlock": { - "__uuid__": "643d1ba5-fd6f-45ab-928e-c271afddbcef" - }, - "timePause1": { - "__uuid__": "f5c8c01d-aa75-4518-a7c6-82599ad31e58" - }, - "timePause2": { - "__uuid__": "37c7f2a9-e0d2-4f3e-9ed6-1ad2ef1862c0" - }, - "hit1": { - "__uuid__": "e16fd77e-9bbb-4bd1-b149-e1dd5a409c8f" - }, - "hit2": { - "__uuid__": "bab2f84c-f988-4355-8fc7-2c55a09cc089" - }, - "hit3": { - "__uuid__": "e06aa433-8f1d-4787-8e6d-7fe6a29ca6f5" - }, - "hit4": { - "__uuid__": "ed4cf450-ae51-46ce-997a-50dd75d9a519" - }, - "hit5": { - "__uuid__": "b27d353c-e4fe-46de-b7c9-75d2b96363d3" - }, + "main_bgm": null, + "xiaochu": null, + "hit": null, + "down": null, + "win": null, + "anniu_Big": null, + "anniu_little": null, + "tanchuang": null, + "zhuan1": null, + "zhuan2": null, + "adhesive": null, + "freezeBlock": null, + "freezeDoor": null, + "hammer": null, + "lockBlock1": null, + "lockBlock2": null, + "lockDoor": null, + "magic1": null, + "magic2": null, + "simpleColor": null, + "stacking": null, + "starBlock": null, + "timePause1": null, + "timePause2": null, + "hit1": null, + "hit2": null, + "hit3": null, + "hit4": null, + "hit5": null, "_id": "24lN1LYRdNia3ZOiLsINIp" }, { diff --git a/assets/Script/Block.ts b/assets/Script/Block.ts index b6c57ce..0570374 100644 --- a/assets/Script/Block.ts +++ b/assets/Script/Block.ts @@ -1418,6 +1418,7 @@ export default class Block extends cc.Component { this.node.removeFromParent(); } + const deadCheckVersion = MapConroler._instance.nextDeadCheckVersion(); setTimeout(() => { if (MapConroler._instance) { if (MapConroler._instance.blockNum != 0 && (MapConroler._instance.openWall.length > 0 || @@ -1426,10 +1427,7 @@ export default class Block extends cc.Component { || MapConroler._instance.change_colorBlock == true || MapConroler._instance.jump_state != null || MapConroler._instance.buttonWall.length > 0) && !MapConroler._instance.gameOver && !MapConroler._instance.gameWin) { - let gameover = MapConroler._instance.predict_End(true); - if (gameover == false) { - MapConroler._instance.failReason(); - } + MapConroler._instance.requestPredictEndCheck(deadCheckVersion); } } }, 1200); @@ -2058,6 +2056,7 @@ export default class Block extends cc.Component { this.node.removeFromParent(); } MapConroler._instance.checkColor(colorTemp, true, this.node); + const deadCheckVersion = MapConroler._instance.nextDeadCheckVersion(); setTimeout(() => { if (MapConroler._instance) { if (MapConroler._instance.blockNum != 0 && (MapConroler._instance.openWall.length > 0 || @@ -2065,10 +2064,7 @@ export default class Block extends cc.Component { MapConroler._instance.blockLock == true || MapConroler._instance.change_colorBlock == true || MapConroler._instance.jump_state != null || MapConroler._instance.buttonWall.length > 0) && !MapConroler._instance.gameOver && !MapConroler._instance.gameWin) { - let gameover = MapConroler._instance.predict_End(true); - if (gameover == false) { - MapConroler._instance.failReason(); - } + MapConroler._instance.requestPredictEndCheck(deadCheckVersion); } } }, 900); @@ -2270,6 +2266,7 @@ export default class Block extends cc.Component { } MapConroler._instance.checkColor(colorTemp, true, this.node); + const deadCheckVersion = MapConroler._instance.nextDeadCheckVersion(); setTimeout(() => { if (MapConroler._instance) { if (MapConroler._instance.blockNum != 0 && (MapConroler._instance.openWall.length > 0 || @@ -2277,10 +2274,7 @@ export default class Block extends cc.Component { MapConroler._instance.blockLock == true || MapConroler._instance.change_colorBlock == true || MapConroler._instance.jump_state != null || MapConroler._instance.buttonWall.length > 0) && !MapConroler._instance.gameOver && !MapConroler._instance.gameWin) { - let gameover = MapConroler._instance.predict_End(true); - if (gameover == false) { - MapConroler._instance.failReason(); - } + MapConroler._instance.requestPredictEndCheck(deadCheckVersion); } } }, 1200); diff --git a/assets/Script/GameManager.ts b/assets/Script/GameManager.ts index 71e7edf..68950a9 100644 --- a/assets/Script/GameManager.ts +++ b/assets/Script/GameManager.ts @@ -158,12 +158,14 @@ export default class GameManager extends cc.Component { } onHide() { - cc.audioEngine.stopMusic(); + // 只暂停游戏逻辑,不要 stopMusic —— 微信小游戏 InnerAudioContext 的 resumeMusic 经常失效 + // 用 pauseAll,回前台时用 resumeAll 续播,保证退回 HomeScene 时音乐不被中断/丢失 + cc.audioEngine.pauseAll(); cc.game.pause(); } onShow() { - cc.audioEngine.resumeMusic(); + cc.audioEngine.resumeAll(); cc.game.resume(); } //#region 给加载好的墙壁资源排序 @@ -204,18 +206,19 @@ export default class GameManager extends cc.Component { if (err) { console.error('加载 shop 失败:', err); } else { + cc.director.loadScene("HomeScene"); // 加载成功后进入 HomeScene - cc.assetManager.loadBundle('music', (err, bundle) => { - if (err) { - console.error('加载 music bundle 失败:', err); - // 加载失败时仍尝试进入 HomeScene - } else { - } - setTimeout(() => { - cc.director.loadScene("HomeScene"); - }, 300); + // cc.assetManager.loadBundle('music', (err, bundle) => { + // if (err) { + // console.error('加载 music bundle 失败:', err); + // // 加载失败时仍尝试进入 HomeScene + // } else { + // } + // setTimeout(() => { - }); + // }, 300); + + // }); } }); diff --git a/assets/Script/JiaZai.ts b/assets/Script/JiaZai.ts index 2301803..a2943df 100644 --- a/assets/Script/JiaZai.ts +++ b/assets/Script/JiaZai.ts @@ -250,7 +250,7 @@ export default class JiaZai extends cc.Component { if (cc.fx.GameConfig.GM_INFO.openid) { if (cc.fx.GameConfig.GM_INFO.openid == "orm8I7N10NSQdAhN4LP04n8UP56I") { this.node.getChildByName("Load").getChildByName("New EditBox").active = true; - cc.fx.GameTool.changeCoin(9999); + // cc.fx.GameTool.changeCoin(9999); } } } @@ -411,7 +411,7 @@ export default class JiaZai extends cc.Component { } //console.log("音乐开关", cc.fx.GameConfig.GM_INFO.musicOpen); - AudioManager._instance.playMusicGame(); + // AudioManager._instance.playMusicGame(); // 从房间出来判断是否弹出通行证 this.popUpPassCheck(); diff --git a/assets/Script/Map.ts b/assets/Script/Map.ts index b0ea444..5332796 100644 --- a/assets/Script/Map.ts +++ b/assets/Script/Map.ts @@ -120,6 +120,8 @@ export default class MapConroler extends cc.Component { new_mode: number = 0; new_item: number = 0; autoNextAfterNewMode: boolean = false; + deadCheckVersion: number = 0; + deadCheckConfirmDelay: number = 450; mapWidth: number = 0; mapHeight: number = 0; wallNum: number = 0; @@ -5899,6 +5901,45 @@ export default class MapConroler extends cc.Component { } //提前判断游戏结束 + nextDeadCheckVersion() { + this.deadCheckVersion += 1; + return this.deadCheckVersion; + } + + canRunPredictEndCheck() { + return this.blockNum != 0 && (this.openWall.length > 0 || + this.revolving_state != 0 || this.longAndShortWall.length > 0 || + this.blockLock == true || this.change_colorBlock == true || + this.jump_state != null || this.buttonWall.length > 0) + && !this.gameOver && !this.gameWin; + } + + requestPredictEndCheck(version: number) { + if (version != this.deadCheckVersion || !this.canRunPredictEndCheck()) { + return; + } + + let gameover = this.predict_End(true); + if (gameover != false) { + return; + } + + if (this.openWall.length > 0) { + setTimeout(() => { + if (version != this.deadCheckVersion || !this.canRunPredictEndCheck()) { + return; + } + let confirmGameover = this.predict_End(true); + if (confirmGameover == false) { + this.failReason(); + } + }, this.deadCheckConfirmDelay); + return; + } + + this.failReason(); + } + predict_End(type) { //return true; // console.log("提前判断游戏结束"); diff --git a/assets/Script/module/Config/GameConfig.ts b/assets/Script/module/Config/GameConfig.ts index 29e2688..9b06b53 100644 --- a/assets/Script/module/Config/GameConfig.ts +++ b/assets/Script/module/Config/GameConfig.ts @@ -304,7 +304,7 @@ export class GameConfig { vibrateOpen: true, //震动 coinnum: 0, //每局的金币数 paid_user: false, //是否是付费用户 - version: "1.9.94", //版本号 + version: "1.9.95", //版本号 shushu_DistinctId: "", //数数访客ID shushu_AccountId: "", //数数账号ID uid: "", //用户和后端唯一id diff --git a/assets/Script/module/Music/AudioManager.ts b/assets/Script/module/Music/AudioManager.ts index 2db73cf..08b3336 100644 --- a/assets/Script/module/Music/AudioManager.ts +++ b/assets/Script/module/Music/AudioManager.ts @@ -4,10 +4,41 @@ const { ccclass, property } = cc._decorator; @ccclass export default class AudioManager extends cc.Component { static _instance: any; - //背景音乐 - @property(cc.AudioClip) - audioGameBgm0: cc.AudioClip = null; + // 远程音频 CDN 基础地址 + // public CDN_AUDIO_BASE: string = "https://pay.nika4games.com/remote/music/"; + public CDN_AUDIO_BASE: string = "https://cdn.pay.nika4games.com/remote/music/"; + + // 本地缓存的 AudioClip(按需懒加载) + private audioCache: { [key: string]: cc.AudioClip } = {}; + // 远程 URL 映射表 + private audioUrlMap: { [key: string]: string } = {}; + // 本地持久化缓存目录(微信小游戏) + private localCacheDir: string = ''; + + /** + * 获取本地缓存目录 + * 微信小游戏使用 USER_DATA_PATH 下的 audio_cache 子目录 + */ + getLocalCacheDir(): string { + if (this.localCacheDir) return this.localCacheDir; + // @ts-ignore + if (typeof wx !== 'undefined' && wx.env && wx.env.USER_DATA_PATH) { + // @ts-ignore + this.localCacheDir = wx.env.USER_DATA_PATH + '/audio_cache/'; + } + return this.localCacheDir; + } + + /** + * 获取本地缓存文件路径 + */ + getLocalCachePath(name: string): string { + return this.getLocalCacheDir() + name + '.mp3'; + } + // 背景音乐 + @property(cc.AudioClip) + main_bgm: cc.AudioClip = null; @property(cc.AudioClip) xiaochu: cc.AudioClip = null; @property(cc.AudioClip) @@ -15,14 +46,8 @@ export default class AudioManager extends cc.Component { @property(cc.AudioClip) down: cc.AudioClip = null; @property(cc.AudioClip) - fangxiang: cc.AudioClip = null; - @property(cc.AudioClip) - build: cc.AudioClip = null; - @property(cc.AudioClip) win: cc.AudioClip = null; @property(cc.AudioClip) - lose: cc.AudioClip = null; - @property(cc.AudioClip) anniu_Big: cc.AudioClip = null; @property(cc.AudioClip) anniu_little: cc.AudioClip = null; @@ -32,8 +57,6 @@ export default class AudioManager extends cc.Component { zhuan1: cc.AudioClip = null; @property(cc.AudioClip) zhuan2: cc.AudioClip = null; - - @property(cc.AudioClip) adhesive: cc.AudioClip = null; @property(cc.AudioClip) @@ -112,6 +135,257 @@ export default class AudioManager extends cc.Component { this.mMusicSwitch = 1; this.mEffectSwitch = 1; + + // 初始化远程音频 URL 映射 + this.initAudioUrlMap(); + } + + /** + * 初始化远程音频 URL 映射表 + * 将所有音频文件路径映射到 CDN 完整 URL + */ + initAudioUrlMap() { + const base = this.CDN_AUDIO_BASE; + // 音效列表 + const names = [ + 'main_bgm', 'xiaochu', 'hit', 'down', 'win', + 'anniu_Big', 'anniu_little', 'tanchuang', 'zhuan1', 'zhuan2', + 'adhesive', 'freezeBlock', 'freezeDoor', 'hammer', + 'lockBlock1', 'lockBlock2', 'lockDoor', + 'magic1', 'magic2', 'simpleColor', 'stacking', + 'starBlock', 'timePause1', 'timePause2', + 'hit1', 'hit2', 'hit3', 'hit4', 'hit5' + ]; + names.forEach((name) => { + this.audioUrlMap[name] = base + name + '.mp3'; + }); + } + + /** + * 根据名称获取远程音频 URL + */ + getRemoteUrl(name: string): string { + if (name == null || name == "null") { + return ""; + } + return this.audioUrlMap[name] || (this.CDN_AUDIO_BASE + name + '.mp3'); + } + + /** + * 加载远程音频 + * 优先从缓存获取,没有则从 CDN 下载 + */ + loadRemoteAudio(name: string, callback?: (clip: cc.AudioClip) => void) { + // 1. 已有缓存直接返回 + if (this.audioCache[name]) { + callback && callback(this.audioCache[name]); + return; + } + + // 2. 本地有 @property 注入的 AudioClip,优先使用 + if (this[name] instanceof cc.AudioClip) { + this.audioCache[name] = this[name]; + callback && callback(this[name]); + return; + } + + // 3. 从远程 CDN 加载 + const url = this.getRemoteUrl(name); + // console.log('远程加载音频:', name, url); + if (url == null || url == "") { + callback && callback(null); + return; + } + // 微信小游戏:先查本地缓存,没有再下载 + if (cc.sys.platform === cc.sys.WECHAT_GAME) { + // @ts-ignore + if (typeof wx !== 'undefined' && wx.downloadFile) { + this.loadRemoteAudioWithCache(name, url, callback); + return; + } + } + + // 4. 其他平台直接 loadRemote + cc.assetManager.loadRemote(url, (err, audioClip: cc.AudioClip) => { + if (err) { + console.error('远程音频加载失败:', name, err); + callback && callback(null); + return; + } + this.audioCache[name] = audioClip; + callback && callback(audioClip); + }); + } + + /** + * 微信小游戏:优先从本地缓存加载音频,没有则下载并保存到本地 + * 缓存路径: wx.env.USER_DATA_PATH/audio_cache/.mp3 + * 微信本地缓存总上限 50MB,超出 saveFile 会失败但不影响本次播放 + */ + loadRemoteAudioWithCache(name: string, url: string, callback?: (clip: cc.AudioClip) => void) { + const localPath = this.getLocalCachePath(name); + if (!localPath) { + // 拿不到本地路径,回退到直接下载 + this.downloadAndCacheAudio(name, url, '', callback); + return; + } + // @ts-ignore + const fs = wx.getFileSystemManager(); + + // 确保缓存目录存在(递归创建) + try { + fs.mkdirSync(this.getLocalCacheDir(), true); + } catch (e) { + // 目录已存在或其他错误,忽略 + } + + // 1. 检查本地缓存文件是否存在 + fs.access({ + path: localPath, + success: () => { + // 本地缓存命中,直接从本地路径加载 + // console.log('____________音频本地缓存命中:', name, localPath); + cc.assetManager.loadRemote(localPath, (err, audioClip: cc.AudioClip) => { + if (err) { + //console.log('____________本地缓存加载失败,重新下载:', name, err); + // 加载失败,删除坏文件并重新下载 + try { fs.unlinkSync(localPath); } catch (e) { } + this.downloadAndCacheAudio(name, url, localPath, callback); + return; + } + this.audioCache[name] = audioClip; + //console.log('____________本地缓存加载成功:', name, this.audioCache); + callback && callback(audioClip); + }); + }, + fail: () => { + // 本地缓存未命中,从 CDN 下载 + //console.log('____________本地缓存未命中,下载音频:', name, url); + this.downloadAndCacheAudio(name, url, localPath, callback); + } + }); + } + + /** + * 下载音频,保存到本地后再从本地路径加载(避免 loadRemote 与 saveFile 竞争) + * 关键:wx.saveFile 是移动 temp 文件,而 cc.assetManager.loadRemote 是异步读, + * 如果先 loadRemote(tempPath) 再 saveFile,引擎真正读 temp 时文件已被移走 → 500。 + * 正确顺序:saveFile 落盘 → loadRemote(本地路径) + * @param localPath 本地保存路径,为空时只下载不缓存 + */ + downloadAndCacheAudio(name: string, url: string, localPath: string, callback?: (clip: cc.AudioClip) => void) { + // @ts-ignore + const fs = wx.getFileSystemManager(); + + // @ts-ignore + wx.downloadFile({ + url: url, + success: (res: any) => { + if (res.statusCode !== 200) { + //console.error('____________下载音频失败:', name, res.statusCode); + callback && callback(null); + return; + } + const tempFilePath = res.tempFilePath; + + if (!localPath) { + // 不需要缓存,直接从 temp 加载 + cc.assetManager.loadRemote(tempFilePath, (err, audioClip: cc.AudioClip) => { + if (err) { + //console.error('____________远程音频加载失败:', name, err); + callback && callback(null); + return; + } + this.audioCache[name] = audioClip; + callback && callback(audioClip); + }); + return; + } + + // 先 saveFile 落盘到本地缓存,再从稳定的本地路径 loadRemote + fs.saveFile({ + tempFilePath: tempFilePath, + filePath: localPath, + success: (saveRes: any) => { + const savedPath = saveRes.savedFilePath || localPath; + //console.log('____________音频已缓存到本地:', name, savedPath); + cc.assetManager.loadRemote(savedPath, (err, audioClip: cc.AudioClip) => { + if (err) { + console.error('____________本地缓存加载失败:', name, err); + try { fs.unlinkSync(savedPath); } catch (e) { } + callback && callback(null); + return; + } + //console.log('____________远程音频加载成功:', name); + this.audioCache[name] = audioClip; + callback && callback(audioClip); + }); + }, + fail: (saveErr: any) => { + // saveFile 失败(50MB 超出 / temp 已被清理等),回退到 temp 直接加载 + //console.log('____________saveFile 失败,回退到 temp 直接加载:', name, saveErr); + cc.assetManager.loadRemote(tempFilePath, (err, audioClip: cc.AudioClip) => { + if (err) { + //console.error('____________远程音频加载失败:', name, err); + callback && callback(null); + return; + } + this.audioCache[name] = audioClip; + callback && callback(audioClip); + }); + } + }); + }, + fail: (err: any) => { + //console.error('____________downloadFile 失败:', name, err); + callback && callback(null); + } + }); + } + + /** + * 清除所有音频本地缓存(调试/换包用) + */ + clearLocalAudioCache(onComplete?: () => void) { + if (!(cc.sys.platform === cc.sys.WECHAT_GAME)) { + onComplete && onComplete(); + return; + } + // @ts-ignore + const fs = wx.getFileSystemManager(); + const dir = this.getLocalCacheDir(); + if (!dir) { + onComplete && onComplete(); + return; + } + fs.readdir({ + dirPath: dir, + success: (res: any) => { + const files: string[] = res.files || []; + let removed = 0; + if (files.length === 0) { + onComplete && onComplete(); + return; + } + files.forEach((file) => { + const filePath = dir + file; + fs.unlink({ + filePath: filePath, + success: () => { + removed++; + if (removed === files.length) onComplete && onComplete(); + }, + fail: () => { + removed++; + if (removed === files.length) onComplete && onComplete(); + } + }); + }); + }, + fail: () => { + onComplete && onComplete(); + } + }); } play(audioSource, loop, callback, isBgMusic) { // if (isBgMusic && !this.mMusicSwitch) return; @@ -165,13 +439,65 @@ export default class AudioManager extends cc.Component { preload() { if (!(cc.sys.platform === cc.sys.WECHAT_GAME || cc.sys.platform === cc.sys.BYTEDANCE_GAME)) { return; } - var musics = [ - this.audioGameBgm0, - ]; - musics.forEach(function (path) { - }) - // console.log("音乐开关", cc.fx.GameConfig.GM_INFO.musicOpen); - // this.playMusicGame(); + // 预加载所有音频 + const allSounds = Object.keys(this.audioUrlMap); + console.log('AudioManager 开始预加载所有音频,共', allSounds.length, '个'); + this.preloadAudios( + allSounds, + (loaded, total) => { + // console.log('音频预加载进度:', loaded, '/', total); + }, + () => { + console.log('所有音频预加载完成'); + }, + // 单条加载完成回调:main_bgm 特殊处理 + (name, clip) => { + if (name === 'main_bgm' && clip) { + // main_bgm 加载完成后,如果音乐开关是打开的,自动播放 + if (cc.fx.GameConfig.GM_INFO && cc.fx.GameConfig.GM_INFO.musicOpen) { + console.log('main_bgm 加载完成,音乐开关已开启,自动播放'); + this.playMusic('main_bgm', null, true); + } else { + console.log('main_bgm 加载完成,但音乐开关未开启'); + } + } + } + ); + } + + /** + * 预加载指定的音频列表 + * @param names 音频名称数组 + * @param onProgress 进度回调 (loaded, total) + * @param onComplete 完成回调 + */ + preloadAudios( + names: string[], + onProgress?: (loaded: number, total: number) => void, + onComplete?: () => void, + onItemLoaded?: (name: string, clip: cc.AudioClip) => void + ) { + if (!names || names.length === 0) { + onComplete && onComplete(); + return; + } + let loaded = 0; + const total = names.length; + const onOneLoaded = (name: string, clip: cc.AudioClip) => { + loaded++; + onItemLoaded && onItemLoaded(name, clip); + onProgress && onProgress(loaded, total); + if (loaded === total) onComplete && onComplete(); + }; + names.forEach((name) => { + if (this[name] instanceof cc.AudioClip || this.audioCache[name]) { + onOneLoaded(name, this[name] || this.audioCache[name]); + return; + } + this.loadRemoteAudio(name, (clip) => { + onOneLoaded(name, clip); + }); + }); } getAudioMusicSwitch() { @@ -217,30 +543,96 @@ export default class AudioManager extends cc.Component { if (!cc.fx.GameConfig.GM_INFO.effectOpen) { return; } - if (this[name]) + // 1. 本地有 AudioClip 直接播放 + if (this[name] instanceof cc.AudioClip) { + // console.log('____________本地有音频成功:', name); return this.play(this[name], false, callback, this.mEffectSwitch); + } + // 2. 缓存中有已加载的 AudioClip + if (this.audioCache[name]) { + // console.log('____________缓存音频加载成功:', name, this.audioCache); + return this.play(this.audioCache[name], false, callback, this.mEffectSwitch); + } + // console.log('____________最终走远程加载:', name); + // 3. 远程加载 + this.loadRemoteAudio(name, (clip) => { + if (clip) { + // console.log('____________远程音频加载成功:', name); + this.play(clip, false, callback, this.mEffectSwitch); + } else { + console.log('音效播放失败:', name); + } + }); } playMusic(key, callback, loop) { - // console.log("音频开关________________________________________"); + console.log("音频开关________________________________________", cc.fx.GameConfig.GM_INFO.musicOpen); if (!cc.fx.GameConfig.GM_INFO.musicOpen) { return; } loop = typeof loop == 'undefined' || loop ? true : false; - this.stopMusic(); - this.mMusicKey = this.play(key, loop, callback, true); + // 如果请求播放的就是当前已在播放的音乐,跳过 stop+restart,避免打断 + const targetName = (key instanceof cc.AudioClip) ? key.name : key; + if (this.mMusicKey != null && this.mMusicKey === targetName + && cc.audioEngine.isMusicPlaying && cc.audioEngine.isMusicPlaying()) { + console.log("当前已在播放的音乐,跳过 stop+restart,避免打断"); + return; + } + + // this.stopMusic(); + + // key 可能是 AudioClip 实例,也可能是字符串名称 + if (key instanceof cc.AudioClip) { + this.mMusicKey = this.play(key, loop, callback, true); + console.log("开始播放音乐", key.name); + return; + } + + // 字符串名称:尝试从缓存或远程加载 + const name = key; + console.log("开始播放音乐的名称", name); + const playFromCache = () => { + console.log("从缓存播放音乐", name); + if (this.audioCache[name]) { + this.mMusicKey = this.play(this.audioCache[name], loop, callback, true); + } else { + console.log('背景音乐播放失败:', name); + } + }; + if (this.audioCache[name]) { + console.log("从缓存播放音乐", name); + playFromCache(); + } else { + this.loadRemoteAudio(name, (clip) => { + console.log("从远程加载音乐", name, clip); + if (clip) { + this.mMusicKey = this.play(clip, loop, callback, true); + } + }); + } } /** * 游戏背景音乐 + * 仅当音乐开关开启、且当前没有在播放 main_bgm 时才真正播放 + * 避免从 GameScene 退回 HomeScene 时被打断重启 */ playMusicGame() { - this.stopMusic(); - this.playMusic(this.audioGameBgm0, {}, true); + + if (!cc.fx.GameConfig.GM_INFO.musicOpen) { + return; + } + // 已经在播背景音乐(musicId 不为 -1)就不再打断 + if (this.mMusicKey != null && this.mMusicKey !== undefined && cc.audioEngine.isMusicPlaying && cc.audioEngine.isMusicPlaying()) { + return; + } + console.log("开始播放音乐"); + this.playMusic('main_bgm', null, true); } /** * 停止背景音乐播放 */ stopMusic() { + console.log("无论什么原因——————————————暂停了背景音乐播放", this.mMusicKey); // cc.wwx.OutPut.log('stopMusic audio effect wx: ' + this.mMusicKey); cc.audioEngine.stopAll(); } diff --git a/assets/UI/UI/comment_btn.png.meta b/assets/UI/UI/comment_btn.png.meta index e092573..e9eb94f 100644 --- a/assets/UI/UI/comment_btn.png.meta +++ b/assets/UI/UI/comment_btn.png.meta @@ -21,11 +21,11 @@ "trimThreshold": 1, "rotated": false, "offsetX": 0, - "offsetY": 0, + "offsetY": 0.5, "trimX": 0, "trimY": 0, "width": 175, - "height": 190, + "height": 189, "rawWidth": 175, "rawHeight": 190, "borderTop": 0, diff --git a/assets/music.meta b/assets/music.meta index 109c1cc..dc6cb45 100644 --- a/assets/music.meta +++ b/assets/music.meta @@ -2,7 +2,7 @@ "ver": "1.1.3", "uuid": "6ebfe2d9-539a-4853-8b16-fca5c89cf2ce", "importer": "folder", - "isBundle": true, + "isBundle": false, "bundleName": "music", "priority": 9, "compressionType": { diff --git a/settings/wechatgame.json b/settings/wechatgame.json index 503f91d..9e6177f 100644 --- a/settings/wechatgame.json +++ b/settings/wechatgame.json @@ -2,7 +2,7 @@ "appid": "wxdd145ced49158a1e", "orientation": "portrait", "separate_engine": true, - "REMOTE_SERVER_ROOT": "", + "REMOTE_SERVER_ROOT": "https://cdn.pay.nika4games.com/", "subContext": "SubContextView", "startSceneAssetBundle": true }