nagvalizm.ru

Телепередачи
01.12.2014, 04:45

Чем фаршировать тортилью

Сегодня мы продолжим описание разработки Marmalade Framework, начатой в 1 части статей этого цикла, усовершенствовав работу с графическими ресурсами, а также добавив работу со звуком и группами изображений, при помощи.

24.11.2014, 23:18

Фруктовое пюре для ребенка 4 месяца

Select Pin Pin it Like byfet.ru капустный пирог Татьяны Толстой Источник: https://www.pinterest.com/pin/277534395761416438/

В мармеладе iphone 4

Омлет Просмотров: 708 Комментариев: 10
Сегодня мы продолжим описание разработки Marmalade Framework, начатой в 1 части статей этого цикла, усовершенствовав работу с графическими ресурсами, а также добавив работу со звуком и группами изображений, при помощи которых мы обеспечим локализацию приложений.

В первую очередь, внимательно посмотрим на то, как мы загружаем графические ресурсы в Sprite.cpp:
Sprite.cpp
void Sprite::addImage(const charres, int state) { img = Iw2DCreateImage(res); }

Очевидно, что если одно и то-же изображение будет использоваться многократно, мы загрузим его в память столько раз, сколько спрайтов создадим. Работая таким образом, мы быстро исчерпаем тот скудный объем оперативной памяти, который нам доступен на мобильных платформах. Нам необходим менеджер ресурсов, позволяющий использовать единожды загруженные ресурсы многократно.
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; }

Обратите внимание, что мы должны очищать всю динамически выделенную память, в противном случае, мы получим ошибку при завершении приложения. Указатель на загруженный графический ресурс будет храниться в ResourceHolder.
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 мы будем загружать группу изображений, связанных с языковыми настройками, имена же самих ресурсов, внутри групп, будут совпадать (сами файлы будут размещены в разных каталогах). Для того, чтобы определить группу, необходимо создать текстовый файл с именем группы и расширением group. Ниже пример определения такого файла для группы изображений.
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" }

Говоря о группах, следует дать две важных рекомендации:
  • Не забывайте о том, что при загрузке файла, имя следует указывать с расширением, а для загрузки изображения из группы, следует использовать одноименный ресурс без расширения
  • Не следует помещать изображения в группы без необходимости (особенно изображения большого размера), поскольку внутри группы они хранятся в неупакованном состоянии и занимают гораздо больше места, чем при использовании упакованных графических форматов (таких как PNG), что напрямую повлияет на размер дистрибутива

Текущую локаль на устройстве мы можем определять кроссплатформенно, используя следующий код.
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; } }

Осталось добавить в наш проект поддержку работы со звуком. Мы будем использовать две подсистемы:
  • S3E Audio — для проигрывания фоновой музыки (поддерживает ряд кодеков и стерео)
  • S3E Sound — для проигрывания звуковых эффектов (поддерживает возможность одновременного проигрывания нескольких звуков)

Принципы работы с этими подсистемами хорошо описаны в этой статье и я не буду подробно на них останавливаться. Опишу лишь изменения, которые необходимо внести в проект.
В файл настроек приложения мы добавляем параметр, управляющий количеством одновременно проигрываемых звуков.
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" } }

Здесь определены два звуковых эффекта и мы можем управлять их настройками (например громкостью vol). В менеджере ресурсов добавляем инициализацию и завершение звуковой подсистемы.
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 не забываем добавить вызов метода update для звуковой подсистемы:
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 под отладчиком. Если мы что-то сделали неправильно, то получим сообщение об ошибке:
image
В результате компиляции, появляется каталог data-ram/data-gles1, содержащий двоичное представление загруженных групп. Работая под отладчиком, мы можем удалять содержимое этого каталога (оно будет пересоздано), но при выполнении сборки под мобильную платформу (iOS или Android) оно должно присутствовать. В противном случае, сборка завершиться с ошибкой.
Завершая разговор о ресурсах, следует упомянуть о еще одном интересном моменте. Мы легко можем обеспечить персистентность при помощи следующей настройки:
app.icf
[S3E] DataDirIsRAM=1

В результате, каталог data, ранее содержащий файлы ресурсов, доступных только на чтение, становится доступен на запись. Эта возможность кроссплатформенна и мы можем создавать файлы, для хранения настроек приложения, как на 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, указавшего мне на этот недочет.
В следующей статье мы завершим разработку Framework-а и построим небольшое демонстрационное приложение.
При разработке Marmalade Framework использовались следующие материалы

Источник: http://habrahabr.ru/post/161959/

В мармеладе iphone 4