发布的微信勾选 md5 cache 后,用 AudioClip 播放音频会失败,找不到音频文件

这个问题啥时候解决?

提供下版本号。

我昨天下载的最新的2.0.1 版本 ,发现勾选md5cache 之后 发布到微信里面 音频播放不出来 ,去掉md5cache 发布到微信,就可以。我看播放路径,res/raw-assets/1d/1dd75ba5-d1e5-4306-9524-053faabe62f2.mp3 但是微信里面的真正路径是.//res/raw-assets/1d/1dd75ba5-d1e5-4306-9524-053faabe62f2.aac79.mp3

我这边测试是没问题的。

下面这个链接是发布md5cache的资源版本
.//res/raw-assets/1d/1dd75ba5-d1e5-4306-9524-053faabe62f2.aac79.mp3

你看播放的路径会通过地址转换,转换成md5版链接
res/raw-assets/1d/1dd75ba5-d1e5-4306-9524-053faabe62f2.mp3

不清楚你是在什么情况下无法播放的。

是不是需要自己动手转换地址,我用的是cc.AudioClip应该引擎自动转换的吧

audioSource直接就是AudioClip
代码如下:
play : function(audioSource, loop, callback, isBgMusic) {
if (isBgMusic && !this.mMusicSwitch) return;
if (!isBgMusic && !this.mEffectSwitch) return;

        var volume = isBgMusic ? this.bgMusicVolume : this.effectMusicVolume;

        if (CC_JSB) {
            var context = cc.audioEngine.play(audioSource, loop, volume);
            if (callback){
                cc.audioEngine.setFinishCallback(context, function(){
                    callback.call(this);
                }.bind(this));
            }
            this.mAudioMap[audioSource] = context;
            cc.wwx.OutPut.log('play audio effect : ' + audioSource);

            return audioSource;
        } else {
            var context = wx.createInnerAudioContext();
            context.autoplay = true;
            context.loop = loop;
            context.obeyMuteSwitch = true;
            context.volume = volume;
            if (callback) {
                context.onEnded(function() {
                    callback.call(this);
                }.bind(this));
            } else {
                context.offEnded();
            }

            var audioPath = wxDownloader.REMOTE_SERVER_ROOT + audioSource;
            if (audioSource && audioPath != wxDownloader.REMOTE_SERVER_ROOT && audioPath.endsWith('.mp3')) {
                context.src = audioPath;
                context.play();

                this.mAudioMap[audioSource] = context;
            }

            cc.wwx.OutPut.log('play audio effect : ' + context.src);

            return audioSource;
        }
    },
`文字缩进4格`

使用loadRes去加载音频的话,是不需要转换地址,如果其他原因,上传个demo工程,看起来快。

这个问题应该是使用微信接口 就不行

    var context = wx.createInnerAudioContext();
    context.autoplay = true;
    context.loop = loop;
   context.obeyMuteSwitch = true;
    context.volume = volume;
     if (callback) {
    context.onEnded(function() {
    callback.call(this);
    }.bind(this));
     } else {
     context.offEnded();
    }
    
    var audioPath = wxDownloader.REMOTE_SERVER_ROOT + audioSource;
   if (audioSource && audioPath != wxDownloader.REMOTE_SERVER_ROOT && audioPath.endsWith('.mp3')) {
     context.src = audioPath;
     context.play();
    
    this.mAudioMap[audioSource] = context;
        }

我也遇到了相同的问题,使用了微信的接口就会不行,请问你有什么好的解决方案吗?

暂时没有

例如:
第一步:cc.url.raw(resources/audio/click.mp3);
这个是cdn的地址:${cdn}${Const.GameEnName}
let cdn = ‘’; // SDKTools.CdnUrl;
cdn && (url = ${cdn}${Const.GameEnName}/${url});

得到的地址为:res/raw-assets/57/5784bfc1-05eb-47df-bc2a-4d13a3003d5f.mp3

第二步:url = cc.loader.md5Pipe.transformURL(url);

得到url地址为:res/raw-assets/57/5784bfc1-05eb-47df-bc2a-4d13a3003d5f.24503.mp3
如果勾选了md5 cache,一定要用transformURL

Audio.zip (6.2 KB)
在微信小游戏的声音播放上面,归总的audio工具类,可以自由切换使用微信的声音播放api还是cocos自带的api。
解决了勾选了md5 cache以后,使用微信自带的createInnerAudioContext api,播放声音需要指定url,碰到了url转换不正确的问题,通过社区强大的力量找到了解决方案。特提供给碰到类似问题的广大朋友。

执行WxAudio.ts的protected getPath(name: string)方法,运行结果为:

这样就能找到微信小游戏项目目录res/raw-assets/57下面的声音文件咯:

当然如果使用的是cocos自带的声音播放引擎,不会出现类似的问题。

LoaderMgr.ts的封装:
import Helper from ‘…/common/Helper’;

export default class LoaderCenter {
/**已加载资源引用计数, key:资源路径 value:被加载次数 */
private resLoadNumMap: Map<string, number> = null;

private constructor() {
    this.resLoadNumMap = new Map;
}

/**
 * 加载声音资源
 * @param url 资源路径
 */
loadSpriteFrameWithUrl(url: string): Promise<cc.SpriteFrame> {
    return new Promise(resolve => {
        this.loadResWithUrl(url, cc.SpriteFrame, {
            complete: (err: any, res: cc.SpriteFrame) => resolve(res),
            context: this
        });
    });
}

/**
 * 加载声音资源
 * @param url 资源路径
 */
loadAudioWithUrl(url: string): Promise<cc.AudioClip> {
    return new Promise(resolve => {
        this.loadResWithUrl(url, cc.AudioClip, {
            complete: (err: any, res: cc.AudioClip) => {
                resolve(res);
            },
            context: this
        });
    });
}

/**
 * 加载预制体资源
 * @param url 资源路径
 */
loadPrefabWithUrl(url: string): Promise<cc.Prefab> {
    return new Promise(resolve => {
        this.loadResWithUrl(url, cc.Prefab, {
            complete: (err: any, res: cc.Prefab) => {
                resolve(res);
            },
            context: this
        });
    });
}

/**
 * 加载资源
 * @param url 资源路径
 * @param resType 资源类型
 * @param opts 
 *  complete 完成回调,回传参数列表 (error, resource)
 *  progress 进度回调,回传参数列表 (completedCount, totalCount, item)
 */
loadResWithUrl(url: string, resType: typeof cc.Asset, opts?: { complete: Function, progress?: Function, context?: any }) {
    opts = opts || Object.create(null);
    let res = cc.loader.getRes(url, resType);
    if (cc.isValid(res))
        return opts.complete && opts.complete.call(opts.context, null, res);

    cc.loader.loadRes(
        url,
        resType,
        (completedCount: number, totalCount: number, item: any) => {
            // 进度回调
            opts.progress && opts.progress.call(opts.context, completedCount, totalCount, item);
        },
        (err, res) => {
            // 完成回调
            if (err) return cc.error('ResManager loadRes Error', err);

            opts.complete && opts.complete.call(opts.context, err, res);

            // 资源引用计数
            let deps = cc.loader.getDependsRecursively(url);
            for (let resPath of deps) {
                let num = this.resLoadNumMap.get(resPath);
                if (num == null) {
                    this.resLoadNumMap.set(resPath, 1);
                } else {
                    this.resLoadNumMap.set(resPath, num + 1);
                }
            }
        }
    );
}

/**
 * 释放资源
 * @param url 
 */
releaseRes(url: string) {
    let deps = cc.loader.getDependsRecursively(url);
    for (let resPath of deps) {
        let num = this.resLoadNumMap.get(resPath);
        if (!Helper.isNullOrUndefined(num)) {
            num--;
            this.resLoadNumMap.set(resPath, Math.max(num, 0));
            if (num <= 0) {
                cc.loader.releaseRes(url);
                this.resLoadNumMap.delete(resPath);
            }
        }
    }
}

/**
 * 从resources目录加载asset
 * @param url 
 * @param assetType 
 */
loadRes(url: string, assetType?: typeof cc.Asset): Promise<any> {
    return new Promise(resolve => {
        let res = cc.loader.getRes(url, assetType);
        if (cc.isValid(res)) return resolve(res);

        cc.loader.loadRes(url, assetType, (err, res) => {
            if (err) {
                cc.warn("loadAsset error", url);
                resolve(err);

            } else if (cc.isValid(res)) {
                resolve(res);

            } else
                resolve(err);
        });
    });
}
}

/**
 * 从resources目录加载prefab(省略资源后缀),加载成功后生成prefab实例
 * @param url 
 */
loadPrefabObj(url: string): Promise<cc.Node> {
    return new Promise(resolve => {
        let res: cc.Prefab = cc.loader.getRes(url, cc.Prefab);
        if (cc.isValid(res))
            return resolve(cc.instantiate(res) as cc.Node);

        cc.loader.loadRes(url, cc.Prefab, (err, res: cc.Prefab) => {
            if (err) {
                resolve(null);

            } else if (cc.isValid(res)) {
                resolve(cc.instantiate(res) as cc.Node);

            } else
                resolve(null);
        });
    });
}


private static _instance = null;
static get I(): LoaderCenter {
    return this._instance || (this._instance = new LoaderCenter);
}

}

export const LoaderMgr = LoaderCenter.I;