Cocos2d-x for Android:より高速なファイル読み取り

私の最近のプロジェクトの1つは、ゲームをiOSからAndroidに移植することです。 ゲームは、かなり人気のあるクロスプラットフォームゲームエンジンであるCocos2d-xを使用して記述されています。

Cocos2d-x for Androidの詳細
Cocos2d-xは、ほぼ2年前からAndroidに存在しており、かなり立派な時代です。 オープンソース、MITライセンス(変更は不要)。

OpenGL ES 1.xの最新の安定版リリースは0.13.0で、今年3月にリリースされました。

OpenGL ES 2.0-2.0.2の最初のリリースは、8月下旬に登場しました。


ビッグゲームのダウンロード速度を何回も簡単に上げることができ、ユーザーアクションに対する反応の全体的なスムーズさを改善する方法をお伝えしたいと思います。



穏やかな仕事


iOSのゲームはバージョン0.13を使用しているため、Androidの場合は同じもの(公式リポジトリの最新の修正を含む)を使用しました。 エンジンに大きな失望や重大なバグを発見することなく、すべてが全般的にうまくいきました。貧弱なグラフィックス用のおもちゃのバージョン(古いiPhone 480x320)はうまく機能しました。 「HD」グラフィックモード(960x640)を追加します。



iOSでは、cocos2dの最初のブランチで、現在のモードの接尾辞が付いているファイルが存在する場合、それ以外の場合、そのような接尾辞のないファイルを使用して、他のモードのサポートが自動的に実行されます。 接尾辞は通常、iPhone HDおよびiPad SDモードの場合は ' -hd '、iPad HDの場合は ' -ipadhd 'です。 そのため、「image.png」ファイルをダウンロードしようとすると、「image-hd.png」ファイルが実際にロードされる場合があります。 Androidバージョンでは、これはそうではありませんでした-独立して追加されました。 グラフィックが良くなりました。



問題


おもちゃの読み込みが長くなり始め、ユーザーの操作(メニューへの遷移など)に応答するのがはるかに悪くなりました。



最初に、サフィックスを持つファイルの存在をチェックするたびに、std :: unordered_map <std :: string requestedFile、std :: string receivedFile>という形式の単純なキャッシュで、ファイルの存在を1回だけチェックするのに十分であると判断しました-部分的に助けましたしかし、状況は非常に困難なままでした。 Win8の同じゲームのポートにこのような問題がなかったことは驚くべきことでした(そしてWin8のバージョンは、iPad HDモードを含むすべてをサポートしました-画面2048x1536のテクスチャを使用)。



彼はさらに理解し始めました。

CCFileUtils :: getFileData-Cocos2d-x内のファイルの読み取りを担当するメソッド(github-および以降もリンク) -APKからダウンロードするために、 CCFileUtils :: getFileDataFromZipを呼び出します。

ソースコード
unsigned char* CCFileUtils::getFileDataFromZip(const char* pszZipFilePath, const char* pszFileName, unsigned long * pSize) { unsigned char * pBuffer = NULL; unzFile pFile = NULL; *pSize = 0; do { CC_BREAK_IF(!pszZipFilePath || !pszFileName); CC_BREAK_IF(strlen(pszZipFilePath) == 0); pFile = unzOpen(pszZipFilePath); CC_BREAK_IF(!pFile); int nRet = unzLocateFile(pFile, pszFileName, 1); CC_BREAK_IF(UNZ_OK != nRet); char szFilePathA[260]; unz_file_info FileInfo; nRet = unzGetCurrentFileInfo(pFile, &FileInfo, szFilePathA, sizeof(szFilePathA), NULL, 0, NULL, 0); CC_BREAK_IF(UNZ_OK != nRet); nRet = unzOpenCurrentFile(pFile); CC_BREAK_IF(UNZ_OK != nRet); pBuffer = new unsigned char[FileInfo.uncompressed_size]; int nSize = 0; nSize = unzReadCurrentFile(pFile, pBuffer, FileInfo.uncompressed_size); CCAssert(nSize == 0 || nSize == (int)FileInfo.uncompressed_size, "the file size is wrong"); *pSize = FileInfo.uncompressed_size; unzCloseCurrentFile(pFile); } while (0); if (pFile) { unzClose(pFile); } return pBuffer; }
      
      





つまり、APKのファイルごとに毎回、個別の開閉アーカイブがあります。 最も簡単なアクションは、少なくとも一度開いて、Cocos2d-xの2.0.xバージョンで修正されているかどうかを確認することです-異なるグラフィックスのファイルロジックが変更されているため、修正されているだけでなく、悪化していることがわかりますAndroidバージョン)-APKファイルからの読み取り2回試みることが可能です。



さて、私はさらに見て-unzLocateFileへの呼び出し。 この機能は何をしますか?

  err = unzGoToFirstFile(file); while (err == UNZ_OK) { char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; err = unzGetCurrentFileInfo64(file,NULL, szCurrentFileName,sizeof(szCurrentFileName)-1,NULL,0,NULL,0); if (err == UNZ_OK) { if (unzStringFileNameCompare(szCurrentFileName, szFileName,iCaseSensitivity)==0) return UNZ_OK; err = unzGoToNextFile(file); } }
      
      





つまり、毎回-アーカイブ内のすべてのファイルの線形検索。 毎回。 そこでは、コードがより深くなり、メモリを節約できるため、ときどき-アーカイブおよび読み取りとともにファイル内を移動します。 おもちゃのあるAPKで-リソースを含む1300を超えるファイル。

また、gles20の場合、ファイルが読み取られていない場合、cocos2d-xのバージョンは一般的に最悪のケースであり、2番目のファイルを再度検索する必要があります。



解決策


良い-少なくともファイルのリストを何らかの方法でキャッシュすることにしました。unzLocateFileがより速く動作するように、アーカイブ内にその位置を保持することが望ましいです。 私はさらに掘り始めます-そして驚いたことに、すべてがすでにそこにあることがわかりました。使用を開始するのに十分です:

 /* unz_file_info contain information about a file in the zipfile */ typedef struct unz_file_pos_s { uLong pos_in_zip_directory; /* offset in zip file directory */ uLong num_of_file; /* # of file */ } unz_file_pos; int ZEXPORT unzGetFilePos(unzFile file, unz_file_pos* file_pos); int ZEXPORT unzGoToFilePos(unzFile file, unz_file_pos* file_pos);
      
      







したがって、一般に、位置が1回のファイルのリストを生成することは基本的であり、さらに-必要なものを静かに直接読み取ります。



アイデアをテストすると、読み込みが5倍高速化され(さらに、グラフィックスが簡略化された古いバージョンと比較してグラフィックスが重いバージョン)、ゲーム全体が改善されました(グラフィックスが多数あるため、すべてをメモリに保存することは不可能であり、常に何かを読み込む必要があります) 。



後で、unzGoToFirstFile / unzGoToNextFileの後にunzGetCurrentFileInfoを個別に呼び出す必要がないことが判明しましたが、それらはまだ同じ情報を読み取ります。



最後に-既存の元のunzLocateFileファイルを検索するのとほぼ同時にZIPからファイルのリスト全体を読み取るヘルパークラスが作成されました。



Android用の最新バージョンのCocos2d-xのプルリクエスト -間違いなく機能します。 0.13のバージョンは、マイナーな編集によって取得されます。

健康に使用してください。



結論


なぜこれが可能ですか? ソースコードのすべてが実際に準備できているので、ソリューションの検索には文字通り数時間かかりました。 ただし、この問題は存在し、主に「APKのファイル数を減らす」など、さまざまな方法で解決されました。

次に、3か月前にAsset Managerを使用するという申し出を見つけました-そこに何がどのようにあり、生産性が向上する(または既に低下している)かどうかを調べる必要があります。



同じことがさまざまな企業で繰り返し解決されていると思いますが、誰も修正を返してくれません。



Cocos2d-xのMiniZipの使用部分-実際には、その実行方法を示しているだけで、ランダムファイルを常に読み取ることは意図されていませんでした。



そのため、2年にもかかわらず、パブリックバージョンでは、何かを改善することが初歩的な場所がまだ多くあります。 パフォーマンス-はい、エンジンをプロファイルするように設計されたCCProfiling.h / .cppの同じクラスで、古い平均と新しい値を加算してさらに半分に分割すること平均を計算します。 したがって、たとえば、10 4 1と1 4 10の同じ値の結果は異なります(それぞれ4と6.25)。 cocos2d-iphoneのソースコードにもまったく同じ問題があるので、少し後で一般的な修正を行います(他の人の興味がなければ)。



時間があり、0.13から現在の2.xに移行することで、公式バージョンに他の修正を追加できることを願っています。



All Articles