マーマレードのフレームワーク(パート3)

本日は、このシリーズ記事のパート1で始まったマーマレードフレームワークの開発の説明を続け、グラフィックリソースを使用した作業を改善するとともに、サウンドおよびイメージグループを使用した作業を追加し、アプリケーションのローカライズを提供します。



まず、グラフィックリソースをSprite.cppに読み込む方法を詳しく見てみましょう。



Sprite.cpp
void Sprite::addImage(const char*res, int state) { img = Iw2DCreateImage(res); }
      
      







明らかに、同じ画像が繰り返し使用される場合、スプライトを作成するのと同じ回数だけメモリにロードします。 このように作業することにより、モバイルプラットフォームで使用可能なRAMの量が不足します。 一度ロードされたリソースを複数回使用できるリソースマネージャーが必要です。



ResourceManager.h
 #ifndef _RESOURCEMANAGER_H_ #define _RESOURCEMANAGER_H_ #include <map> #include <string> #include "s3e.h" #include "IwResManager.h" #include "IwSound.h" #include "ResourceHolder.h" using namespace std; class ResourceManager { private: map<string, ResourceHolder*> res; public: ResourceManager(): res() {} void init(); void release(); ResourceHolder* load(const char* name, int loc); typedef map<string, ResourceHolder*>::iterator RIter; typedef pair<string, ResourceHolder*> RPair; }; extern ResourceManager rm; #endif // _RESOURCEMANAGER_H_
      
      







ResourceManager.cpp
 #include "ResourceManager.h" #include "Locale.h" ResourceManager rm; void ResourceManager::init() { IwResManagerInit(); } void ResourceManager::release() { for (RIter p = res.begin(); p != res.end(); ++p) { delete p->second; } res.clear(); IwResManagerTerminate(); } ResourceHolder* ResourceManager::load(const char* name, int loc) { ResourceHolder* r = NULL; string nm(name); RIter p = res.find(nm); if (p == res.end()) { r = new ResourceHolder(name, loc); res.insert(RPair(nm, r)); } else { r = p->second; } return r; }
      
      







動的に割り当てられたすべてのメモリをクリアする必要があることに注意してください。そうしないと、アプリケーションの終了時にエラーが発生します。 ロードされたグラフィックリソースへのポインターは、R​​esourceHolderに格納されます。



ResourceHolder.h
 #ifndef _RESOURCEHOLDER_H_ #define _RESOURCEHOLDER_H_ #include <string> #include "s3e.h" #include "Iw2D.h" #include "IwResManager.h" using namespace std; class ResourceHolder { private: string name; int loc; CIw2DImage* data; public: ResourceHolder(const char* name, int loc); ~ResourceHolder() {unload();} void load(); void unload(); CIw2DImage* getData(); }; #endif // _RESOURCEHOLDER_H_ </spoiler> <spoiler title="ResourceHolder.cpp"> <source lang="cpp"> #include "ResourceHolder.h" #include "Locale.h" ResourceHolder::ResourceHolder(const char* name, int loc): name(name) , loc(loc) , data(NULL) { } void ResourceHolder::load() { if (data == NULL) { CIwResGroup* resGroup; const char* groupName = Locale::getGroupName(loc); if (groupName != NULL) { resGroup = IwGetResManager()->GetGroupNamed(groupName); IwGetResManager()->SetCurrentGroup(resGroup); data = Iw2DCreateImageResource(name.c_str()); } else { data = Iw2DCreateImage(name.c_str()); } } } void ResourceHolder::unload() { if (data != NULL) { delete data; data = NULL; } } CIw2DImage* ResourceHolder::getData() { load(); return data; }
      
      







ResourceHolder :: loadメソッドでは、名前を受け取ったことに気づくことができます。まず(locで取得した値に応じて)いくつかのグループを読み込んでその中のリソースを見つけ、失敗した場合は名前を使用してファイルを読み込みます。 実際、Marmaladeでは、画像(およびその他のリソース)をいわゆるグループに配置して、それらを全体としてアップロードすることができます。



この事実を利用して、アプリケーションのローカライズを確実にします。 locパラメーターの値に応じて、言語設定に関連する画像のグループを読み込みます。グループ内のリソース自体の名前は一致します(ファイル自体は異なるディレクトリに配置されます)。 グループを定義するには、グループと拡張グループの名前でテキストファイルを作成する必要があります。 以下は、画像のグループに対するこのようなファイルの定義の例です。



locale_ru.group
 CIwResGroup { name "locale_ru" "./locale_ru/play.png" "./locale_ru/setup.png" "./locale_ru/musicoff.png" "./locale_ru/musicon.png" "./locale_ru/soundoff.png" "./locale_ru/soundon.png" }
      
      







グループといえば、2つの重要な推奨事項を作成する必要があります。





次のコードを使用して、クロスプラットフォームのデバイスの現在のロケールを確認できます。



Locale.h
 #ifndef _LOCALE_H_ #define _LOCALE_H_ enum ELocale { elNothing = 0x0, elImage = 0x1, elSound = 0x2, elEnImage = 0x5, elRuImage = 0x9, elEnSound = 0x6, elRuSound = 0xA }; class Locale { public: static int getCurrentImageLocale(); static int getCurrentSoundLocale(); static int getCommonImageLocale() {return elImage;} static int getCommonSoundLocale() {return elSound;} static const char* getGroupName(int locale); }; #endif // _LOCALE_H_
      
      







Locale.cpp
 #include "Locale.h" #include "s3e.h" const char* Locale::getGroupName(int locale) { switch (locale) { case elImage: return "images"; case elEnSound: case elRuSound: case elSound: return "sounds"; case elEnImage: return "locale_en"; case elRuImage: return "locale_ru"; default: return NULL; } } int Locale::getCurrentImageLocale() { int32 lang = s3eDeviceGetInt(S3E_DEVICE_LANGUAGE); switch (lang) { case S3E_DEVICE_LANGUAGE_RUSSIAN: return elRuImage; default: return elEnImage; } } int Locale::getCurrentSoundLocale() { int32 lang = s3eDeviceGetInt(S3E_DEVICE_LANGUAGE); switch (lang) { case S3E_DEVICE_LANGUAGE_RUSSIAN: return elRuSound; default: return elEnSound; } }
      
      







私たちのプロジェクトにサウンドを扱うためのサポートを追加することは残っています。 2つのサブシステムを使用します。





これらのサブシステムでの作業の原則は、この記事で詳しく説明されているため、詳細については説明しません。 プロジェクトに加える必要がある変更のみを説明します。



アプリケーション設定ファイルに、同時に再生されるサウンドの数を制御するパラメーターを追加します。



app.icf
 [SOUND] MaxChannels=16
      
      







プロジェクトファイルに次の説明を追加します。



mf.mkb
 #!/usr/bin/env mkb options { module_path="$MARMALADE_ROOT/examples" } subprojects { iw2d iwresmanager SoundEngine } ... files { ... [Data] (data) locale_en.group locale_ru.group sounds.group } assets { (data) background.png sprite.png music.mp3 (data-ram/data-gles1, data) locale_en.group.bin locale_ru.group.bin sounds.group.bin }
      
      







サウンドリソースグループの説明は次のとおりです。



sounds.group
 CIwResGroup { name "sounds" "./sounds/menubutton.wav" "./sounds/sound.wav" CIwSoundSpec { name "menubutton" data "menubutton" vol 0.9 loop false } CIwSoundSpec { name "sound" data "sound" vol 0.9 loop false } CIwSoundGroup { name "sound_effects" maxPolyphony 8 killOldest true addSpec "menubutton" addSpec "sound" } }
      
      







ここでは2つの効果音が定義されており、設定を制御できます(例:音量)。 リソースマネージャーで、オーディオサブシステムの初期化と完了を追加します。



ResourceManager.cpp
 void ResourceManager::init() { IwResManagerInit(); #ifdef IW_BUILD_RESOURCES IwGetResManager()->AddHandler(new CIwResHandlerWAV); #endif IwGetResManager()->LoadGroup("sounds.group"); if (Locale::getCurrentImageLocale() == elEnImage) { IwGetResManager()->LoadGroup("locale_en.group"); } if (Locale::getCurrentImageLocale() == elRuImage) { IwGetResManager()->LoadGroup("locale_ru.group"); } } void ResourceManager::release() { for (RIter p = res.begin(); p != res.end(); ++p) { delete p->second; } res.clear(); IwResManagerTerminate(); IwSoundTerminate(); }
      
      







Main.cppで、サウンドサブシステムの更新メソッドへの呼び出しを追加することを忘れないでください。



Main.cpp
 #include "Main.h" #include "s3e.h" #include "Iw2D.h" #include "IwGx.h" #include "IwSound.h" #include "ResourceManager.h" #include "TouchPad.h" #include "Desktop.h" #include "Scene.h" #include "Background.h" #include "Sprite.h" void init() { // Initialise Mamrlade graphics system and Iw2D module IwGxInit(); Iw2DInit(); // Init IwSound IwSoundInit(); // Set the default background clear colour IwGxSetColClear(0x0, 0x0, 0x0, 0); // Initialise the resource manager rm.init(); touchPad.init(); desktop.init(); } void release() { desktop.release(); touchPad.release(); // Shut down the resource manager rm.release(); Iw2DTerminate(); IwGxTerminate(); } int main() { init(); { Scene scene; new Background(&scene, "background.png", 1, elNothing); new Sprite(&scene, "sprite.png", 122, 100, 2, elNothing); desktop.setScene(&scene); int32 duration = 1000 / 25; // Main Game Loop while (!desktop.isQuitMessageReceived()) { // Update keyboard system s3eKeyboardUpdate(); // Update Iw Sound Manager IwGetSoundManager()->Update(); // Update touchPad.update(); uint64 timestamp = s3eTimerGetMs(); desktop.update(timestamp); // Clear the screen IwGxClear(IW_GX_COLOUR_BUFFER_F | IW_GX_DEPTH_BUFFER_F); touchPad.clear(); // Refresh desktop.refresh(); // Show the surface Iw2DSurfaceShow(); // Yield to the opearting system s3eDeviceYield(duration); } } release(); return 0; }
      
      







LoadGroupメソッドがデバッガーの下で実行されると、リソースグループは説明からコンパイルされます。 間違ったことをすると、エラーメッセージが表示されます。



画像



コンパイルの結果、data-ram / data-gles1ディレクトリが表示され、ロードされたグループのバイナリ表現が含まれます。 デバッガーの下で作業すると、このディレクトリの内容を削除できます(再作成されます)が、モバイルプラットフォーム(iOSまたはAndroid)用にビルドする場合は、このディレクトリが存在する必要があります。 そうでない場合、アセンブリは失敗します。



リソースについての話を締めくくり、別の興味深い点に言及すべきです。 次の設定により、永続性を簡単に確保できます。



app.icf
 [S3E] DataDirIsRAM=1
      
      







その結果、以前は読み取り専用のリソースファイルが含まれていたデータディレクトリが書き込み可能になります。 この機能はクロスプラットフォームであり、iOSとAndroidの両方でアプリケーション設定を保存するファイルを作成できます。 保存されたデータマネージャーの実装は簡単なため、説明しません。 望む人はここでそれを独立して見ることができます



DataDirIsRAMフラグを設定した後、Marmaladeは、コンパイルされたグループリソースを含むdata-gles1ディレクトリをdata-ramからdataに移動することに注意してください。 グループがロードされるとディレクトリが自動的に作成されるため、これはデバッガーでの作業にはまったく影響しませんが、モバイルデバイス(AndroidまたはiPhone)の配布キットが無関係なリソースで誤って構築される可能性があるという事実につながる可能性があります。 これを防ぐには、プロジェクトのmkbファイルに簡単な変更を加える必要があります。



  ... - (data-ram/data-gles1, data) + (data-ram/data-gles1, data/data-gles1) locale_en.group.bin locale_ru.group.bin sounds.group.bin
      
      





このことを指摘してくれたMezomishに感謝します。



次の記事では、フレームワークの開発を完了し、小さなデモアプリケーションを構築します。



以下の資料は 、マーマレードフレームワークの開発に使用されました。




All Articles