В предыдущей статье, мы разработали дизайн фреймворка для разработки простых 2D-игр, с использованием инструментальной среды Marmalade. Начнем разработку нашего фреймворка с построения mkb-файла, являющегося платформо-независимым описанием проекта.
mf.mkb:
#!/usr/bin/env mkb
options
{
}
subprojects
{
iw2d
}
includepath
{
./source/Main
./source/Common
./source/Scene
}
files
{
[Main]
(source/Main)
Main.cpp
Main.h
Desktop.cpp
Desktop.h
[Common]
(source/Common)
IObject.h
IScreenObject.h
ISprite.h
ISpriteOwner.h
AbstractScreenObject.h
AbstractScreenObject.cpp
AbstractSpriteOwner.h
AbstractSpriteOwner.cpp
[Scene]
(source/Scene)
Scene.cpp
Scene.h
Background.cpp
Background.h
Sprite.cpp
Sprite.h
[Data]
(data)
}
assets
{
(data)
background.png
sprite.png
(data-ram/data-gles1, data)
}
options
{
}
subprojects
{
iw2d
}
includepath
{
./source/Main
./source/Common
./source/Scene
}
files
{
[Main]
(source/Main)
Main.cpp
Main.h
Desktop.cpp
Desktop.h
[Common]
(source/Common)
IObject.h
IScreenObject.h
ISprite.h
ISpriteOwner.h
AbstractScreenObject.h
AbstractScreenObject.cpp
AbstractSpriteOwner.h
AbstractSpriteOwner.cpp
[Scene]
(source/Scene)
Scene.cpp
Scene.h
Background.cpp
Background.h
Sprite.cpp
Sprite.h
[Data]
(data)
}
assets
{
(data)
background.png
sprite.png
(data-ram/data-gles1, data)
}
Файл получился довольно большой, но только за счет того, что я заранее описал в нем все файлы, которые понадобятся нам сегодня. На пустые разделы пока можно не обращать внимания (они понадобятся нам в последующем). В разделе subprojects описываются подпроекты, которые мы используем (в настоящее время это только подсистема iw2d Marmalade, которая позволит нам работать с 2D-графикой). В includepath, как это очевидно из названия, перечисляем имена каталогов, содержащих h-файлы. В разделе files описываются исходные файлы (имя в квадратных скобках определяет имя папки в проекте MSVC, а путь в круглых скобках показывает, где эта папка размещается на диске). В разделе assets описываются ресурсы, используемые приложением.
Далее, заранее создадим заготовки h- и cpp-файлов, расположив их в соответствующих папках проекта:
main.h:
#ifndef _MAIN_H_
#define _MAIN_H_
#endif // _MAIN_H_
#define _MAIN_H_
#endif // _MAIN_H_
main.cpp:
#include "Main.h"
И запустим MKB-файл на исполнение. Если все сделано правильно, откроется Microsoft Visual Studio, в которой мы увидим наш проект:
Начнем наполнять его кодом. Главный цикл нашего приложения будет расположен в Main.cpp:
#include "Main.h"
#include "s3e.h"
#include "Iw2D.h"
#include "IwGx.h"
#include "Desktop.h"
#include "Scene.h"
#include "Background.h"
#include "Sprite.h"
void init() {
// Initialise Mamrlade graphics system and Iw2D module
IwGxInit();
Iw2DInit();
// Set the default background clear colour
IwGxSetColClear(0x0, 0x0, 0x0, 0);
desktop.init();
}
void release() {
desktop.release();
Iw2DTerminate();
IwGxTerminate();
}
int main() {
init(); {
Scene scene;
new Background(&scene, "background.png", 1);
new Sprite(&scene, "sprite.png", 122, 100, 2);
desktop.setScene(&scene);
int32 duration = 1000 / 25;
// Main Game Loop
while (!s3eDeviceCheckQuitRequest()) {
// Update keyboard system
s3eKeyboardUpdate();
if ((s3eKeyboardGetState(s3eKeyAbsBSK) & S3E_KEY_STATE_DOWN)
#include "s3e.h"
#include "Iw2D.h"
#include "IwGx.h"
#include "Desktop.h"
#include "Scene.h"
#include "Background.h"
#include "Sprite.h"
void init() {
// Initialise Mamrlade graphics system and Iw2D module
IwGxInit();
Iw2DInit();
// Set the default background clear colour
IwGxSetColClear(0x0, 0x0, 0x0, 0);
desktop.init();
}
void release() {
desktop.release();
Iw2DTerminate();
IwGxTerminate();
}
int main() {
init(); {
Scene scene;
new Background(&scene, "background.png", 1);
new Sprite(&scene, "sprite.png", 122, 100, 2);
desktop.setScene(&scene);
int32 duration = 1000 / 25;
// Main Game Loop
while (!s3eDeviceCheckQuitRequest()) {
// Update keyboard system
s3eKeyboardUpdate();
if ((s3eKeyboardGetState(s3eKeyAbsBSK) & S3E_KEY_STATE_DOWN)
== S3E_KEY_STATE_DOWN) break;
// Update
desktop.update(s3eTimerGetMs());
// Clear the screen
IwGxClear(IW_GX_COLOUR_BUFFER_F | IW_GX_DEPTH_BUFFER_F);
// Refresh
desktop.refresh();
// Show the surface
Iw2DSurfaceShow();
// Yield to the opearting system
s3eDeviceYield(duration);
}
}
release();
return 0;
}
// Update
desktop.update(s3eTimerGetMs());
// Clear the screen
IwGxClear(IW_GX_COLOUR_BUFFER_F | IW_GX_DEPTH_BUFFER_F);
// Refresh
desktop.refresh();
// Show the surface
Iw2DSurfaceShow();
// Yield to the opearting system
s3eDeviceYield(duration);
}
}
release();
return 0;
}
Это достаточно шаблонный код для проектов Marmalade. В init инициализируются все подсистемы, с которыми мы будем работать, далее создается сцена и ее наполнение, которые мы будем отображать и передается в desktop в качестве главной сцены (это делается в операторном блоке, для того чтобы обеспечить удаление объекта scene до вызова функции release).
В главном цикле приложения мы проверяем условие выхода по нажатию кнопки "s3eKeyAbsBSK" (работу с клавиатурой мы рассмотрим в последующих статьях), после чего обновляем desktop, передавая ему текущее значение timestamp, очищаем экран, вызываем перерисовку desktop, отображаем изменения на экране вызовом Iw2DSurfaceShow, после чего передаем управление операционной системе, вызовом s3eDeviceYield. По завершении главного цикла, очищаем ресурсы в функции release.
desktop.h:
#ifndef _DESKTOP_H_
#define _DESKTOP_H_
#include "Scene.h"
class Desktop {
private:
int width, height;
Scene* currentScene;
public:
void init();
void release() {}
void update(uint64 timestamp);
void refresh();
int getWidth() const {return width;}
int getHeight() const {return height;}
Scene* getScene() {return currentScene;}
void setScene(Scene* scene);
};
extern Desktop desktop;
#endif // _DESKTOP_H_
#define _DESKTOP_H_
#include "Scene.h"
class Desktop {
private:
int width, height;
Scene* currentScene;
public:
void init();
void release() {}
void update(uint64 timestamp);
void refresh();
int getWidth() const {return width;}
int getHeight() const {return height;}
Scene* getScene() {return currentScene;}
void setScene(Scene* scene);
};
extern Desktop desktop;
#endif // _DESKTOP_H_
desktop.cpp:
#include "Desktop.h"
#include "Iw2D.h"
Desktop desktop;
void Desktop::init() {
width = Iw2DGetSurfaceWidth();
height = Iw2DGetSurfaceHeight();
setScene(NULL);
}
void Desktop::setScene(Scene* scene) {
if (scene != NULL) {
scene->init();
}
currentScene = scene;
}
void Desktop::update(uint64 timestamp) {
if (currentScene != NULL) {
currentScene->update(timestamp);
}
}
void Desktop::refresh() {
if (currentScene != NULL) {
currentScene->refresh();
}
}
#include "Iw2D.h"
Desktop desktop;
void Desktop::init() {
width = Iw2DGetSurfaceWidth();
height = Iw2DGetSurfaceHeight();
setScene(NULL);
}
void Desktop::setScene(Scene* scene) {
if (scene != NULL) {
scene->init();
}
currentScene = scene;
}
void Desktop::update(uint64 timestamp) {
if (currentScene != NULL) {
currentScene->update(timestamp);
}
}
void Desktop::refresh() {
if (currentScene != NULL) {
currentScene->refresh();
}
}
Подсистема desktop будет отслеживать изменение системных параметров и управлять обновлением и отрисовкой текущей сцены. Пока его реализация довольно примитивна (он получает размеры экрана устройства, используя вызовы Iw2DGetSurfaceWidth и Iw2DGetSurfaceHeight, возвращает их по требованию и вызывает методы update и refresh для currentScene. В следующих статьях, мы будем расширять функциональность desktop.
Далее, создадим необходимые интерфейсы, в соответствии с разработанной нами ранее архитектурой:
IObject.h:
#ifndef _IOBJECT_H_
#define _IOBJECT_H_
#include "s3e.h"
class IObject {
public:
virtual ~IObject() {}
virtual bool isBuzy() = 0;
virtual int getState() = 0;
virtual bool sendMessage(int msg, uint64 timestamp = 0,
#define _IOBJECT_H_
#include "s3e.h"
class IObject {
public:
virtual ~IObject() {}
virtual bool isBuzy() = 0;
virtual int getState() = 0;
virtual bool sendMessage(int msg, uint64 timestamp = 0,
void* data = NULL) = 0;
virtual bool sendMessage(int msg, int x, int y) = 0;
virtual void update(uint64 timestamp) = 0;
virtual void refresh() = 0;
};
#endif // _IOBJECT_H_
virtual bool sendMessage(int msg, int x, int y) = 0;
virtual void update(uint64 timestamp) = 0;
virtual void refresh() = 0;
};
#endif // _IOBJECT_H_
IScreenObject.h:
#ifndef _ISCREENOBJECT_H_
#define _ISCREENOBJECT_H_
#include "s3e.h"
#include "IObject.h"
class IScreenObject: public IObject {
public:
virtual int getXPos() = 0;
virtual int getYPos() = 0;
virtual int getWidth() = 0;
virtual int getHeight() = 0;
};
#endif // _ISCREENOBJECT_H_
#define _ISCREENOBJECT_H_
#include "s3e.h"
#include "IObject.h"
class IScreenObject: public IObject {
public:
virtual int getXPos() = 0;
virtual int getYPos() = 0;
virtual int getWidth() = 0;
virtual int getHeight() = 0;
};
#endif // _ISCREENOBJECT_H_
ISprite.h:
#ifndef _ISPRITE_H_
#define _ISPRITE_H_
#include "Locale.h"
#include "Iw2D.h"
#include "IwGx.h"
class ISprite {
public:
virtual void addImage(const char* res, int state = 0) = 0;
virtual CIw2DImage* getImage(int state = 0) = 0;
};
#endif // _ISPRITE_H_
#define _ISPRITE_H_
#include "Locale.h"
#include "Iw2D.h"
#include "IwGx.h"
class ISprite {
public:
virtual void addImage(const char* res, int state = 0) = 0;
virtual CIw2DImage* getImage(int state = 0) = 0;
};
#endif // _ISPRITE_H_
ISpriteOwner.h:
#ifndef _ISPRITEOWNER_H_
#define _ISPRITEOWNER_H_
#include "IObject.h"
#include "AbstractScreenObject.h"
class ISpriteOwner: public IObject {
public:
virtual void addSprite(AbstractScreenObject* sprite, int zOrder) = 0;
virtual bool setZOrder(AbstractScreenObject* sprite, int z) = 0;
virtual int getDesktopWidth() = 0;
virtual int getDesktopHeight() = 0;
virtual int getXSize(int xSize) = 0;
virtual int getYSize(int ySize) = 0;
virtual int getXPos(int x) = 0;
virtual int getYPos(int y) = 0;
};
#endif // _ISPRITEOWNER_H_
#define _ISPRITEOWNER_H_
#include "IObject.h"
#include "AbstractScreenObject.h"
class ISpriteOwner: public IObject {
public:
virtual void addSprite(AbstractScreenObject* sprite, int zOrder) = 0;
virtual bool setZOrder(AbstractScreenObject* sprite, int z) = 0;
virtual int getDesktopWidth() = 0;
virtual int getDesktopHeight() = 0;
virtual int getXSize(int xSize) = 0;
virtual int getYSize(int ySize) = 0;
virtual int getXPos(int x) = 0;
virtual int getYPos(int y) = 0;
};
#endif // _ISPRITEOWNER_H_
Во вспомогательном классе AbstractScreenObject будем вести счетчик ссылок и хранить параметры расположения спрайта:
AbstractScreenObject.h:
#ifndef _ABSTRACTSCREENOBJECT_H_
#define _ABSTRACTSCREENOBJECT_H_
#include <string>
#include "Iw2D.h"
#include "IScreenObject.h"
using namespace std;
class AbstractScreenObject: public IScreenObject {
private:
static int idCounter;
int id;
int usageCounter;
protected:
virtual bool init();
CIw2DAlphaMode alpha;
int xPos, yPos, angle;
int xDelta, yDelta;
bool isVisible;
bool isInitialized;
public:
AbstractScreenObject(int x, int y);
virtual ~AbstractScreenObject() {}
int getId() const {return id;}
void incrementUsage();
bool decrementUsage();
virtual int getXPos() {return xPos + xDelta;}
virtual int getYPos() {return yPos + yDelta;}
virtual int getWidth() {return 0;} // TODO:
virtual int getHeight() {return 0;} // TODO:
virtual bool isBackground() {return false;}
virtual bool isBuzy() {return false;}
int getAngle() const {return angle;}
void move(int x = 0, int y = 0);
void setXY(int x = 0, int y = 0);
void clearXY();
void setAngle(int a) {angle = a;}
void setAlpha(CIw2DAlphaMode a) {alpha = a;}
bool setState(int state) {return false;}
};
#endif // _ABSTRACTSCREENOBJECT_H_
#define _ABSTRACTSCREENOBJECT_H_
#include <string>
#include "Iw2D.h"
#include "IScreenObject.h"
using namespace std;
class AbstractScreenObject: public IScreenObject {
private:
static int idCounter;
int id;
int usageCounter;
protected:
virtual bool init();
CIw2DAlphaMode alpha;
int xPos, yPos, angle;
int xDelta, yDelta;
bool isVisible;
bool isInitialized;
public:
AbstractScreenObject(int x, int y);
virtual ~AbstractScreenObject() {}
int getId() const {return id;}
void incrementUsage();
bool decrementUsage();
virtual int getXPos() {return xPos + xDelta;}
virtual int getYPos() {return yPos + yDelta;}
virtual int getWidth() {return 0;} // TODO:
virtual int getHeight() {return 0;} // TODO:
virtual bool isBackground() {return false;}
virtual bool isBuzy() {return false;}
int getAngle() const {return angle;}
void move(int x = 0, int y = 0);
void setXY(int x = 0, int y = 0);
void clearXY();
void setAngle(int a) {angle = a;}
void setAlpha(CIw2DAlphaMode a) {alpha = a;}
bool setState(int state) {return false;}
};
#endif // _ABSTRACTSCREENOBJECT_H_
AbstractScreenObject.cpp:
#include "AbstractScreenObject.h"
#include "Desktop.h"
int AbstractScreenObject::idCounter = 0;
AbstractScreenObject::AbstractScreenObject(int x, int y):
#include "Desktop.h"
int AbstractScreenObject::idCounter = 0;
AbstractScreenObject::AbstractScreenObject(int x, int y):
xPos(x), alpha(IW_2D_ALPHA_NONE),yPos(y), angle(0),
xDelta(0), yDelta(0), isVisible(true), isInitialized(false), usageCounter(0) {
id = ++idCounter;
}
bool AbstractScreenObject::init() {
bool r = !isInitialized;
isInitialized = true;
return r;
}
void AbstractScreenObject::incrementUsage() {
usageCounter++;
}
bool AbstractScreenObject::decrementUsage() {
usageCounter--;
return (usageCounter == 0);
}
void AbstractScreenObject::move(int x, int y) {
xDelta += x;
yDelta += y;
}
void AbstractScreenObject::setXY(int x, int y) {
xPos = x;
yPos = y;
}
void AbstractScreenObject::clearXY() {
xDelta = 0;
yDelta = 0;
}
id = ++idCounter;
}
bool AbstractScreenObject::init() {
bool r = !isInitialized;
isInitialized = true;
return r;
}
void AbstractScreenObject::incrementUsage() {
usageCounter++;
}
bool AbstractScreenObject::decrementUsage() {
usageCounter--;
return (usageCounter == 0);
}
void AbstractScreenObject::move(int x, int y) {
xDelta += x;
yDelta += y;
}
void AbstractScreenObject::setXY(int x, int y) {
xPos = x;
yPos = y;
}
void AbstractScreenObject::clearXY() {
xDelta = 0;
yDelta = 0;
}
Теперь, переходим к интересной части. Реализация Sprite:
Sprite.h:
#ifndef _SPRITE_H_
#define _SPRITE_H_
#include "AbstractScreenObject.h"
#include "ISprite.h"
#include "ISpriteOwner.h"
#include "Locale.h"
class Sprite: public AbstractScreenObject
, public ISprite {
protected:
ISpriteOwner* owner;
CIw2DImage* img;
public:
Sprite(ISpriteOwner* owner, int x, int y , int zOrder = 0);
Sprite(ISpriteOwner* owner, const char* res, int x, int y, int zOrder = 0);
virtual ~Sprite();
virtual bool sendMessage(int msg, uint64 timestamp = 0, void* data = NULL);
virtual bool sendMessage(int msg, int x, int y) {return false;}
virtual void update(uint64 timestamp) {}
virtual void refresh();
virtual void addImage(const char*res, int state = 0);
virtual CIw2DImage* getImage(int id = 0);
virtual int getState() {return 0;}
virtual int getWidth();
virtual int getHeight();
};
#endif // _SPRITE_H_
#define _SPRITE_H_
#include "AbstractScreenObject.h"
#include "ISprite.h"
#include "ISpriteOwner.h"
#include "Locale.h"
class Sprite: public AbstractScreenObject
, public ISprite {
protected:
ISpriteOwner* owner;
CIw2DImage* img;
public:
Sprite(ISpriteOwner* owner, int x, int y , int zOrder = 0);
Sprite(ISpriteOwner* owner, const char* res, int x, int y, int zOrder = 0);
virtual ~Sprite();
virtual bool sendMessage(int msg, uint64 timestamp = 0, void* data = NULL);
virtual bool sendMessage(int msg, int x, int y) {return false;}
virtual void update(uint64 timestamp) {}
virtual void refresh();
virtual void addImage(const char*res, int state = 0);
virtual CIw2DImage* getImage(int id = 0);
virtual int getState() {return 0;}
virtual int getWidth();
virtual int getHeight();
};
#endif // _SPRITE_H_
Sprite.cpp:
#include "Sprite.h"
#include "Locale.h"
Sprite::Sprite(ISpriteOwner* owner, int x, int y, int zOrder):
#include "Locale.h"
Sprite::Sprite(ISpriteOwner* owner, int x, int y, int zOrder):
AbstractScreenObject(x, y)
, owner(owner)
, img(NULL) {
owner->addSprite((AbstractScreenObject*)this, zOrder);
}
Sprite::Sprite(ISpriteOwner* owner, const char* res, int x, int y, int zOrder):
, owner(owner)
, img(NULL) {
owner->addSprite((AbstractScreenObject*)this, zOrder);
}
Sprite::Sprite(ISpriteOwner* owner, const char* res, int x, int y, int zOrder):
AbstractScreenObject(x, y)
, owner(owner)
, img(NULL) {
addImage(res, 0);
owner->addSprite((AbstractScreenObject*)this, zOrder);
}
Sprite::~Sprite() {
if (img != NULL) {
delete img;
}
}
bool Sprite::sendMessage(int msg, uint64 timestamp, void* data) {
return owner->sendMessage(msg, timestamp, data);
}
void Sprite::addImage(const char*res, int state) {
img = Iw2DCreateImage(res);
}
CIw2DImage* Sprite::getImage(int id) {
return img;
}
int Sprite::getWidth() {
CIw2DImage* img = getImage(getState());
if (img != NULL) {
return img->GetWidth();
} else {
return 0;
}
}
int Sprite::getHeight() {
CIw2DImage* img = getImage(getState());
if (img != NULL) {
return img->GetHeight();
} else {
return 0;
}
}
void Sprite::refresh() {
init();
CIw2DImage* img = getImage(getState());
if (isVisible && (img != NULL)) {
CIwMat2D m;
m.SetRot(getAngle());
m.ScaleRot(IW_GEOM_ONE);
m.SetTrans(CIwSVec2(owner->getXSize(owner->getXPos(getXPos())),
, owner(owner)
, img(NULL) {
addImage(res, 0);
owner->addSprite((AbstractScreenObject*)this, zOrder);
}
Sprite::~Sprite() {
if (img != NULL) {
delete img;
}
}
bool Sprite::sendMessage(int msg, uint64 timestamp, void* data) {
return owner->sendMessage(msg, timestamp, data);
}
void Sprite::addImage(const char*res, int state) {
img = Iw2DCreateImage(res);
}
CIw2DImage* Sprite::getImage(int id) {
return img;
}
int Sprite::getWidth() {
CIw2DImage* img = getImage(getState());
if (img != NULL) {
return img->GetWidth();
} else {
return 0;
}
}
int Sprite::getHeight() {
CIw2DImage* img = getImage(getState());
if (img != NULL) {
return img->GetHeight();
} else {
return 0;
}
}
void Sprite::refresh() {
init();
CIw2DImage* img = getImage(getState());
if (isVisible && (img != NULL)) {
CIwMat2D m;
m.SetRot(getAngle());
m.ScaleRot(IW_GEOM_ONE);
m.SetTrans(CIwSVec2(owner->getXSize(owner->getXPos(getXPos())),
owner->getYSize(owner->getYPos(getYPos()))));
Iw2DSetTransformMatrix(m);
Iw2DSetAlphaMode(alpha);
Iw2DDrawImage(img, CIwSVec2(0, 0), CIwSVec2(owner->getXSize(getWidth()),
Iw2DSetTransformMatrix(m);
Iw2DSetAlphaMode(alpha);
Iw2DDrawImage(img, CIwSVec2(0, 0), CIwSVec2(owner->getXSize(getWidth()),
owner->getYSize(getHeight())));
}
}
}
}
Здесь стоит обратить внимание на методы addImage (загрузка рисунка из ресурса) и refresh
(отображение рисунка на экране). В последнем, с последовательностью
вызовов SetRot, ScaleRot, SetTrans лучше не экспериментировать (если вам
не хочется долгой и мучительной отладки). Передавая в эти вызовы соответсвующие параметры, можно добиться поворота и масштабирования изображения, а также переноса его относительно начала координат.
В вызовах SetTrans и Iw2DrawImage, мы используем методы getXSize и getYSize для преобразования логических координат в экранные. Чуть позже, мы их рассмотрим. Следует отметить важное различие между ScaleRot и использованием третьего (необязательного параметра) Iw2DrawImage. Если первый из них позволяет выполнить афинное преобразование, масштабирующее изображение, то во втором случае, мы можем маштабировать исходное изображение независимо по осям X и Y (чтобы привести к требуемому aspect ratio).
Background.h:
#ifndef _BACKGROUND_H_
#define _BACKGROUND_H_
#include "Sprite.h"
#include "Locale.h"
class Background: public Sprite {
public:
Background(ISpriteOwner* owner, const char* res, int zOrder);
virtual bool isBackground() {return true;}
virtual void refresh();
};
#endif // _BACKGROUND_H_
#define _BACKGROUND_H_
#include "Sprite.h"
#include "Locale.h"
class Background: public Sprite {
public:
Background(ISpriteOwner* owner, const char* res, int zOrder);
virtual bool isBackground() {return true;}
virtual void refresh();
};
#endif // _BACKGROUND_H_
Background.cpp:
#include "Background.h"
Background::Background(ISpriteOwner* owner, const char* res, int zOrder):
Background::Background(ISpriteOwner* owner, const char* res, int zOrder):
Sprite(owner, res, 0, 0, zOrder) {}
void Background::refresh() {
CIwMat2D m;
m.SetRot(0);
m.ScaleRot(IW_GEOM_ONE);
m.SetTrans(CIwSVec2(0, 0));
Iw2DSetTransformMatrix(m);
Iw2DSetAlphaMode(alpha);
Iw2DDrawImage(img, CIwSVec2(0, 0),
void Background::refresh() {
CIwMat2D m;
m.SetRot(0);
m.ScaleRot(IW_GEOM_ONE);
m.SetTrans(CIwSVec2(0, 0));
Iw2DSetTransformMatrix(m);
Iw2DSetAlphaMode(alpha);
Iw2DDrawImage(img, CIwSVec2(0, 0),
CIwSVec2(owner->getDesktopWidth(), owner->getDesktopHeight()));
}
}
Класс Background является наследником Sprite и переопределяет метод refresh. Как легко заметить, экранный размер изображения не вычисляется на основании данных, предоставленных владельцем, а берется непосредственно из размеров desktop
AbstractSpriteOwner.h:
#ifndef _ABSTRACTSPRITEOWNER_H_
#define _ABSTRACTSPRITEOWNER_H_
#include <map>
#include "IObject.h"
#include "ISpriteOwner.h"
#include "AbstractScreenObject.h"
using namespace std;
class AbstractSpriteOwner: public ISpriteOwner {
protected:
multimap<int, AbstractScreenObject*> zOrder;
public:
AbstractSpriteOwner();
virtual ~AbstractSpriteOwner();
virtual void addSprite(AbstractScreenObject* sprite, int z);
virtual bool setZOrder(AbstractScreenObject* sprite, int z);
virtual int getDesktopWidth();
virtual int getDesktopHeight();
virtual int getState() {return 0;}
virtual void update(uint64 timestamp);
virtual void refresh();
virtual bool sendMessage(int msg, uint64 timestamp = 0,
#define _ABSTRACTSPRITEOWNER_H_
#include <map>
#include "IObject.h"
#include "ISpriteOwner.h"
#include "AbstractScreenObject.h"
using namespace std;
class AbstractSpriteOwner: public ISpriteOwner {
protected:
multimap<int, AbstractScreenObject*> zOrder;
public:
AbstractSpriteOwner();
virtual ~AbstractSpriteOwner();
virtual void addSprite(AbstractScreenObject* sprite, int z);
virtual bool setZOrder(AbstractScreenObject* sprite, int z);
virtual int getDesktopWidth();
virtual int getDesktopHeight();
virtual int getState() {return 0;}
virtual void update(uint64 timestamp);
virtual void refresh();
virtual bool sendMessage(int msg, uint64 timestamp = 0,
void* data = NULL) {return false;}
virtual bool sendMessage(int msg, int x, int y);
typedef multimap<int, AbstractScreenObject*>::iterator ZIter;
typedef multimap<int, AbstractScreenObject*>::reverse_iterator RIter;
typedef pair<int, AbstractScreenObject*> ZPair;
};
#endif // _ABSTRACTSPRITEOWNER_H_
virtual bool sendMessage(int msg, int x, int y);
typedef multimap<int, AbstractScreenObject*>::iterator ZIter;
typedef multimap<int, AbstractScreenObject*>::reverse_iterator RIter;
typedef pair<int, AbstractScreenObject*> ZPair;
};
#endif // _ABSTRACTSPRITEOWNER_H_
AbstractSpriteOwner.cpp:
#include "AbstractSpriteOwner.h"
#include "Desktop.h"
#include "ISprite.h"
AbstractSpriteOwner::AbstractSpriteOwner(): zOrder() {}
AbstractSpriteOwner::~AbstractSpriteOwner() {
for (ZIter p = zOrder.begin(); p != zOrder.end(); ++p) {
if (p->second->decrementUsage()) {
delete p->second;
}
}
}
void AbstractSpriteOwner::addSprite(AbstractScreenObject* sprite, int z) {
sprite->incrementUsage();
zOrder.insert(ZPair(z, sprite));
}
bool AbstractSpriteOwner::setZOrder(AbstractScreenObject* sprite, int z) {
for (ZIter p = zOrder.begin(); p != zOrder.end(); ++p) {
if (p->second == sprite) {
zOrder.erase(p);
zOrder.insert(ZPair(z, sprite));
return true;
}
}
return false;
}
int AbstractSpriteOwner::getDesktopWidth() {
return desktop.getWidth();
}
int AbstractSpriteOwner::getDesktopHeight() {
return desktop.getHeight();
}
void AbstractSpriteOwner::update(uint64 timestamp) {
for (ZIter p = zOrder.begin(); p != zOrder.end(); ++p) {
p->second->update(timestamp);
}
}
void AbstractSpriteOwner::refresh() {
for (ZIter p = zOrder.begin(); p != zOrder.end(); ++p) {
p->second->refresh();
}
}
bool AbstractSpriteOwner::sendMessage(int msg, int x, int y) {
for (RIter p = zOrder.rbegin(); p != zOrder.rend(); ++p) {
if (p->second->isBackground()) continue;
if (p->second->sendMessage(msg, x, y)) {
return true;
}
}
return false;
}
#include "Desktop.h"
#include "ISprite.h"
AbstractSpriteOwner::AbstractSpriteOwner(): zOrder() {}
AbstractSpriteOwner::~AbstractSpriteOwner() {
for (ZIter p = zOrder.begin(); p != zOrder.end(); ++p) {
if (p->second->decrementUsage()) {
delete p->second;
}
}
}
void AbstractSpriteOwner::addSprite(AbstractScreenObject* sprite, int z) {
sprite->incrementUsage();
zOrder.insert(ZPair(z, sprite));
}
bool AbstractSpriteOwner::setZOrder(AbstractScreenObject* sprite, int z) {
for (ZIter p = zOrder.begin(); p != zOrder.end(); ++p) {
if (p->second == sprite) {
zOrder.erase(p);
zOrder.insert(ZPair(z, sprite));
return true;
}
}
return false;
}
int AbstractSpriteOwner::getDesktopWidth() {
return desktop.getWidth();
}
int AbstractSpriteOwner::getDesktopHeight() {
return desktop.getHeight();
}
void AbstractSpriteOwner::update(uint64 timestamp) {
for (ZIter p = zOrder.begin(); p != zOrder.end(); ++p) {
p->second->update(timestamp);
}
}
void AbstractSpriteOwner::refresh() {
for (ZIter p = zOrder.begin(); p != zOrder.end(); ++p) {
p->second->refresh();
}
}
bool AbstractSpriteOwner::sendMessage(int msg, int x, int y) {
for (RIter p = zOrder.rbegin(); p != zOrder.rend(); ++p) {
if (p->second->isBackground()) continue;
if (p->second->sendMessage(msg, x, y)) {
return true;
}
}
return false;
}
AbstractSpriteOwner управляет хранением спрайтов и ведет Z-последовательность их отображения на экране. Наследником AbstractSpriteOwner является Scene:
Scene.h:
#ifndef _SCENE_H_
#define _SCENE_H_
#include "s3eKeyboard.h"
#include "AbstractSpriteOwner.h"
#include "AbstractScreenObject.h"
using namespace std;
class Scene: public AbstractSpriteOwner {
private:
AbstractScreenObject* background;
bool isInitialized;
public:
Scene();
virtual bool init();
int getXSize(int xSize);
int getYSize(int ySize);
virtual int getXPos(int x) {return x;}
virtual int getYPos(int y) {return y;}
virtual void refresh();
virtual void update(uint64 timestamp);
virtual bool isBuzy() {return false;}
virtual bool sendMessage(int id, int x, int y);
};
#endif // _SCENE_H_
#define _SCENE_H_
#include "s3eKeyboard.h"
#include "AbstractSpriteOwner.h"
#include "AbstractScreenObject.h"
using namespace std;
class Scene: public AbstractSpriteOwner {
private:
AbstractScreenObject* background;
bool isInitialized;
public:
Scene();
virtual bool init();
int getXSize(int xSize);
int getYSize(int ySize);
virtual int getXPos(int x) {return x;}
virtual int getYPos(int y) {return y;}
virtual void refresh();
virtual void update(uint64 timestamp);
virtual bool isBuzy() {return false;}
virtual bool sendMessage(int id, int x, int y);
};
#endif // _SCENE_H_
Scene.cpp:
#include "Scene.h"
#include "Desktop.h"
Scene::Scene(): AbstractSpriteOwner()
, isInitialized(false)
, background(NULL) {}
bool Scene::init() {
bool r = !isInitialized;
isInitialized = true;
return r;
}
int Scene::getXSize(int xSize) {
if (background != NULL) {
return (getDesktopWidth() * xSize) / background->getWidth();
}
return xSize;
}
int Scene::getYSize(int ySize) {
if (background != NULL) {
return (getDesktopHeight() * ySize) / background->getHeight();
}
return ySize;
}
void Scene::refresh() {
init();
if (background == NULL) {
for (ZIter p = zOrder.begin(); p != zOrder.end(); ++p) {
if (p->second->isBackground()) {
background = p->second;
break;
}
}
}
AbstractSpriteOwner::refresh();
}
void Scene::update(uint64 timestamp) {
AbstractSpriteOwner::update(timestamp);
}
bool Scene::sendMessage(int id, int x, int y) {
if (AbstractSpriteOwner::sendMessage(id, x, y)) {
return true;
}
if (background != NULL) {
return background->sendMessage(id, x, y);
}
return false;
}
#include "Desktop.h"
Scene::Scene(): AbstractSpriteOwner()
, isInitialized(false)
, background(NULL) {}
bool Scene::init() {
bool r = !isInitialized;
isInitialized = true;
return r;
}
int Scene::getXSize(int xSize) {
if (background != NULL) {
return (getDesktopWidth() * xSize) / background->getWidth();
}
return xSize;
}
int Scene::getYSize(int ySize) {
if (background != NULL) {
return (getDesktopHeight() * ySize) / background->getHeight();
}
return ySize;
}
void Scene::refresh() {
init();
if (background == NULL) {
for (ZIter p = zOrder.begin(); p != zOrder.end(); ++p) {
if (p->second->isBackground()) {
background = p->second;
break;
}
}
}
AbstractSpriteOwner::refresh();
}
void Scene::update(uint64 timestamp) {
AbstractSpriteOwner::update(timestamp);
}
bool Scene::sendMessage(int id, int x, int y) {
if (AbstractSpriteOwner::sendMessage(id, x, y)) {
return true;
}
if (background != NULL) {
return background->sendMessage(id, x, y);
}
return false;
}
Уфф, вот и все :) Здесь стоит обратить внимание на реализацию методов getXSize и getYSize, вычисляющих экранные размеры на основании размеров фонового изображения и реальных размеров экрана устройства.
Запускаем наше приложение на выполнение ... и получаем ошибку:
Дело в том, что динамическая память, на мобильных платформах, является крайне ценным ресурсом и в Marmalade, тщательно учитывается. Внесем необходимые изменения в файл настроек app.icf (заодно зафиксируем альбомную ориентацию экрана):
[S3E]
DispFixRot=FixedLandscape
MemSize=70000000
MemSizeDebug=70000000
DispFixRot=FixedLandscape
MemSize=70000000
MemSizeDebug=70000000
Теперь, запустив приложение, видим вполне ожидаемую картинку:
Мы затратили довольно много усилий, чтобы отобразить эту картинку и, несомненно, того-же эффекта можно было добиться гораздо проще, но созданная нами инфраструктура обязательно пригодиться нам в последующих статьях.
Исходные тексты проекта можно скачать здесь.
Комментариев нет:
Отправить комментарий