上一节 解决了读文件的问题,游戏也跑起来了,可是音效和背景音乐,死活播放不出来。回想一下,的确没有考虑到游戏的音效问题。让我们再折腾一下,播放zip里的音效问题。cocos-x 安卓的背景音乐处理交给了Cocos2dxMusic.java,音效处理交给了Cocos2dxSound.java。别的我们不看,就关注声音资源加载的地方。
先分析一下Cocos2dxMusic.java 的声音资源加载方法
private MediaPlayer createMediaplayer(final String pPath) ;
这个方法就是啦。大概就是判断fullpath路径是否 '/'开头,如果是就加载sd卡声音资源,否则加载apk里的资源。
/** * create mediaplayer for music * * @param pPath * the pPath relative to assets * @return */ private MediaPlayer createMediaplayer(final String pPath) { MediaPlayer mediaPlayer = new MediaPlayer(); try { //加载sd卡里的声音资源 if (pPath.startsWith("/")) { final fileinputStream fis = new fileinputStream(pPath); mediaPlayer.setDataSource(fis.getFD()); fis.close(); } else { //加载apk里的声音资源 final AssetfileDescriptor assetfileDescritor = this.mContext.getAssets().openFd(pPath); mediaPlayer.setDataSource(assetfileDescritor.getfileDescriptor(),assetfileDescritor.getStartOffset(),assetfileDescritor.getLength()); } mediaPlayer.prepare(); mediaPlayer.setVolume(this.mleftVolume,this.mRightVolume); } catch (final Exception e) { mediaPlayer = null; Log.e(Cocos2dxMusic.TAG,"error: " + e.getMessage(),e); } return mediaPlayer; }
在分析一下Cocos2dxSound.java的声音资源加载方法
->public int playEffect(final String pPath,final boolean pLoop)
->public int preloadEffect(final String pPath)
->public int createSoundIDFromAsset(final String pPath)
也是是判断fullpath路径是否 '/'开头,如果是就加载sd卡声音资源,否则加载apk里的资源。
public int createSoundIDFromAsset(final String pPath) { int soundID = Cocos2dxSound.INVALID_SOUND_ID; try { if (pPath.startsWith("/")) { //加载sd卡声音资源 soundID = this.mSoundPool.load(pPath,0); } else { //加载apk里的声音资源 soundID = this.mSoundPool.load(this.mContext.getAssets().openFd(pPath),0); } } catch (final Exception e) { soundID = Cocos2dxSound.INVALID_SOUND_ID; Log.e(Cocos2dxSound.TAG,e); } // mSoundPool.load returns 0 if something goes wrong,for example a file does not exist if (soundID == 0) { soundID = Cocos2dxSound.INVALID_SOUND_ID; } return soundID; }
回想起来,我们已经有fullpath ,我们要像读取向下面的资源
fullpath = /storage/emulated/0/DonutABC/unitRes/game_22.zip#/res/pub_element/L1U1/audio/pub_unit1_blue_audio.wav
有两种方法:
方法1:直接读取zip里的声音流 让播放器们播放
inputStream 这样的东西java 程序猿们太熟悉不过了,何况是ZipinputStream。我们先将对应的文件用ZipinputStream 读出,然后直接让MediaPlayer 或SoundPool 加载播放流不就可以了吗?可是童话都是骗人的。突然想起邓超演的《美人鱼》,我实在是想不通。好好的白富美在身边不要,偏偏去要一条上半身是人,下半身是鱼的人鱼。没有接口下半辈子能幸福吗!!!实在是想不通。在这个问题上,也是遇到了这样的人鱼。MediaPlayer 和 SoundPool 都没有直接播放流的方法 ,MediaPlayer的setDataSource方法。SoundPool的load方法 都没有播放流的接口。下半辈子不幸福,感觉不会再爱了。好吧换第二个方法
方法2:将zip对应的声音文件临时解压到sd卡里, 然后返回fullpath给Cocos2dxMusic.java或Cocos2dxSound.java 的加载方法。
新欢没有接口,我们找旧爱。旧爱的接口就是要个声音文件的fullpath吗,太容易满足了。让我们动手改改
Cocos2dxMusic.java 的 createMediaplayer
/** * create mediaplayer for music * * @param pPath * the pPath relative to assets * @return */ private MediaPlayer createMediaplayer(final String pPath) { MediaPlayer mediaPlayer = new MediaPlayer(); try { if (pPath.startsWith("/")) {//改了这里 String ppPath = PathUtils.getZipfilePath(pPath); final fileinputStream fis = new fileinputStream(ppPath); mediaPlayer.setDataSource(fis.getFD()); fis.close(); } else { final AssetfileDescriptor assetfileDescritor = this.mContext.getAssets().openFd(pPath); mediaPlayer.setDataSource(assetfileDescritor.getfileDescriptor(),e); } return mediaPlayer; }
Cocos2dxSound.java 的createSoundIDFromAsset
public int createSoundIDFromAsset(final String pPath) { int soundID = Cocos2dxSound.INVALID_SOUND_ID; try { if (pPath.startsWith("/")) {//改了下面的 String ppPath = PathUtils.getZipfilePath(pPath); soundID = this.mSoundPool.load(ppPath,0); } else { soundID = this.mSoundPool.load(this.mContext.getAssets().openFd(pPath),for example a file does not exist if (soundID == 0) { soundID = Cocos2dxSound.INVALID_SOUND_ID; } return soundID; }
解压的方法getZipfilePath
/*** * 获得路径 也许是zip里面的的路径 * @param pPath * @return * @throws ZipException * @throws IOException */ public static String getZipfilePath(String pPath) throws ZipException,IOException { String zipfilepath = ""; String filename = ""; int index = pPath.indexOf("#"); String ppPath = pPath; //是否在zip里 if(index != -1){ zipfilepath = pPath.substring(0,index); filename = pPath.substring(index+2); Log.d("RecordManager","zipfilepath:"+ zipfilepath + "--filename:" + filename); String filesavename = filename.replaceAll("/","_"); ppPath = PathUtils.getTempPath() +filesavename+".temp"; file filetemp = new file(ppPath); //是否有临时解压文件 避免重复解压 if(!filetemp.exists()){ filetemp.createNewfile(); Zipfile file = new Zipfile(zipfilepath); fileheader fileheader = file.getfileheader(filename); net.lingala.zip4j.io.ZipinputStream zipinputStream = file.getinputStream(fileheader); fileOutputStream fo = new fileOutputStream(ppPath); byte[] b = new byte[4096]; int readline = -1; while ((readline = zipinputStream.read(b)) != -1) { fo.write(b,readline); } fo.close(); zipinputStream.close(); } } return ppPath; }
注:我用了开源的zip *** 作库 zip4j ,我git上有提交 。
好啦。我们这就跑起来。欧啦!声音出来了。这个方案凑合着用,如有更高明的方法,请回复我。
解压方法 c++版
static std::string getZipfilePath(const std::string& fullPath,const std::string& saveDir);static unsigned char* getfileDataFromZip(const char* pszZipfilePath,const char* pszfilename,unsigned long * pSize);
#include "support/zip_support/ZipUtils.h"#include "platform/CCCommon.h"#include "support/zip_support/unzip.h"#include "platform/CCfileUtils.h"
/** * fullpath 音效的绝对路径 * saveDir 解压到的地方 * return 解压后的资源返回的路径 **/std::string JNItools::getZipfilePath(const std::string& fullPath,const std::string& saveDir){ unsigned char * pBuffer = NulL; unsigned long pSize = 0; std::string savepath = fullPath; std::string pszfilenameTemp = ""; std::string pszZipfilePath = "";
size_t pos = fullPath.find_last_of("#"); if (pos != std::string::npos) { // file_path = /storage/emulated/0/DonutABC/unitRes/game_22.zip pszZipfilePath = fullPath.substr(0,pos); // file = res/pub_element/L1U1/audio/pub_unit1_blue_audio.wav pszfilenameTemp = fullPath.substr(pos+2);// cclOG("pszZipfilePath:%s,pszfilenameTemp:%s",pszZipfilePath.c_str(),pszfilenameTemp.c_str()); //替换‘/' std::string filename = pszfilenameTemp; std::string old_value = "/"; std::string new_value = "_"; for(string::size_type pos(0); pos!=string::npos; pos+=new_value.length()){ if((pos=filename.find(old_value,pos))!=string::npos) filename.replace(pos,old_value.length(),new_value); else break; }// cclOG("filename:%s",filename.c_str()); savepath = saveDir + filename + ".temp"; const char* output = savepath.c_str(); cclOG("filename:%s",output); //文件如果存在就不解压了 int i = access(savepath.c_str(),0); cclOG("filename:%d",i); if( i == -1){ pBuffer = JNItools::getfileDataFromZip(pszZipfilePath.c_str(),pszfilenameTemp.c_str(),&pSize); file *savefile = fopen(output,"wb"); fwrite(pBuffer,1,(size_t)pSize,savefile); fflush(savefile); fclose(savefile); delete pBuffer; } } return savepath; }/** * 解压zip 获得资源数据 **/unsigned char* JNItools::getfileDataFromZip(const char* pszZipfilePath,unsigned long * pSize){ unsigned char * pBuffer = NulL; unzfile pfile = NulL; *pSize = 0; do {// cclOG("1"); CC_BREAK_IF(!pszZipfilePath || !pszfilename);// cclOG("11"); CC_BREAK_IF(strlen(pszZipfilePath) == 0);// cclOG("1111"); pfile = unzOpen(pszZipfilePath); CC_BREAK_IF(!pfile);// cclOG("2"); int nRet = unzLocatefile(pfile,pszfilename,1); CC_BREAK_IF(UNZ_OK != nRet);// cclOG("3"); char szfilePathA[260]; unz_file_info fileInfo; nRet = unzGetCurrentfileInfo(pfile,&fileInfo,szfilePathA,sizeof(szfilePathA),NulL,0); CC_BREAK_IF(UNZ_OK != nRet);// cclOG("4"); nRet = unzOpenCurrentfile(pfile); CC_BREAK_IF(UNZ_OK != nRet);// cclOG("5"); pBuffer = new unsigned char[fileInfo.uncompressed_size]; int CC_UNUSED nSize = unzReadCurrentfile(pfile,pBuffer,fileInfo.uncompressed_size); CCAssert(nSize == 0 || nSize == (int)fileInfo.uncompressed_size,"the file size is wrong");// cclOG("6"); *pSize = fileInfo.uncompressed_size; unzCloseCurrentfile(pfile); } while (0); if (pfile) { unzClose(pfile); } return pBuffer;}
调方法
public static native String getZipfilePath(String pPath,String saveDir);
/** * jni 调用 */ Jstring Java_org_cocos2dx_lib_PathUtils_getZipfilePath(jnienv* env,jobject thiz,Jstring path,Jstring savedir) {// cclOG("filename:%s","Java_org_cocos2dx_lib_PathUtils_getZipfilePath"); std::string char_path = JniHelper::Jstring2string(path); std::string char_savedir = JniHelper::Jstring2string(savedir); std::string str_path = JNItools::getZipfilePath(char_path,char_savedir);// cclOG("----getPath:%s",str_path.c_str()); env->DeleteLocalRef(path); env->DeleteLocalRef(savedir); return (env)->NewStringUTF(str_path.c_str()); }
public int createSoundIDFromAsset(final String pPath) { int soundID = Cocos2dxSound.INVALID_SOUND_ID; try { if (pPath.startsWith("/")) { String ppPath = PathUtils.getZipfilePath(pPath,PathUtils.getTempPath()); soundID = this.mSoundPool.load(ppPath,for example a file does not exist if (soundID == 0) { soundID = Cocos2dxSound.INVALID_SOUND_ID; } return soundID; }
Cocos2dxMusic.java 修改
/** * create mediaplayer for music * * @param pPath * the pPath relative to assets * @return */ private MediaPlayer createMediaplayer(final String pPath) { MediaPlayer mediaPlayer = new MediaPlayer(); try { if (pPath.startsWith("/")) { String ppPath = PathUtils.getZipfilePath(pPath,PathUtils.getTempPath()); final fileinputStream fis = new fileinputStream(ppPath); mediaPlayer.setDataSource(fis.getFD()); fis.close(); } else { final AssetfileDescriptor assetfileDescritor = this.mContext.getAssets().openFd(pPath); mediaPlayer.setDataSource(assetfileDescritor.getfileDescriptor(),e); } return mediaPlayer; }
整体来看效率没什么提高,就是不用zip4j 解压了。
还是老样子。下载完整的改动
{{{{{{{{{github传送门}}}}}}}}
本文出处
http://www.jb51.cc/article/p-wywfhxbf-bbs.html
总结以上是内存溢出为你收集整理的cocos2d-x android 直接加载下载到sd的zip里的资源文件(二)全部内容,希望文章能够帮你解决cocos2d-x android 直接加载下载到sd的zip里的资源文件(二)所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)