Фреймворк В Мармеладе (Часть 4)

В последней статье цикл , посвященный разработке Marmalade Framework, мы добавим в проект анимированные и составные спрайты, а также построим небольшое демонстрационное приложение, иллюстрирующее принципы использования библиотеки.

Поскольку мы разрабатываем событийно-ориентированный фреймворк, любое действие (в том числе анимация) будет выполняться в результате обработки события.

Мы определим набор основных событий в модуле «Рабочий стол».

Рабочий стол.

h

  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
   

#ifndef _DESKTOP_H_ #define _DESKTOP_H_ #include <set> #include "s3eKeyboard.h" #include "Scene.h" using namespace std; enum EMessageType { emtNothing = 0x00, emtHide = 0x01, emtShadow = 0x02, emtShow = 0x03, emtSwitch = 0x04, emtInit = 0x05, emtFix = 0x08, emtStartAnimation = 0x06, emtStopAnimation = 0x07, emtActivate = 0x09, emtSystemMessage = 0x0F, emtTouchEvent = 0x10, emtTouchIdMask = 0x03, emtTouchMask = 0x78, emtMultiTouch = 0x14, emtTouchOut = 0x18, emtTouchDown = 0x30, emtTouchUp = 0x50, emtTouchOutUp = 0x58, emtTouchMove = 0x70, emtSingleTouchDown = 0x30, emtSingleTouchUp = 0x50, emtSingleTouchMove = 0x70, emtMultiTouchDown = 0x34, emtMultiTouchUp = 0x54, emtMultiTouchMove = 0x74, emtKeyEvent = 0x80, emtKeyAction = 0x82, emtKeyDown = 0x81, emtKeyPressed = 0x83, emtKeyReleased = 0x82 }; .



Как мы увидим позже, мы можем легко определять новые события, не меняя модуль Desktop. Перечисление EMessageType — это не что иное, как удобный способ собрать вместе все основные коды событий.

Цель анимированного спрайта — инкапсулировать обработку основных событий.

Интерфейс анимированного спрайта содержит всего два метода: IAnimatedSprite.h

#ifndef _IANIMATEDSPRITE_H_ #define _IANIMATEDSPRITE_H_ #include <string> #include "Desktop.h" using namespace std; class IAnimatedSprite { public: virtual bool isValidMessage(int msg) = 0; virtual void doMessage(int msg, void* data = NULL, uint64 timestamp = 0) = 0; }; #endif // _IANIMATEDSPRITE_H_

Метод isValidMessage проверяет, может ли спрайт обработать событие заданного типа (если событие не может быть обработано спрайтом, оно передается в содержащий его контейнер — составной спрайт или сцену), а обработку выполняет метод doMessage. Помимо кода события — msg, в обработчик можно передать указатель на произвольные данные — data и метку времени — timestamp, вычисляемую на каждой итерации цикла в Main.cpp. Реализация AnimatedSprite будет выглядеть так: AnimatedSprite.h

#ifndef _ANIMATEDSPRITE_H_ #define _ANIMATEDSPRITE_H_ #include <map> #include <vector> #include "Sprite.h" #include "IAnimatedSprite.h" #include "AnimateMessage.h" #include "ResourceManager.h" #define REFRESH_CNT 2 using namespace std; class AnimatedSprite: public Sprite, public IAnimatedSprite { protected: struct Message { Message(int id, uint64 timestamp, void* data = NULL): id(id), timestamp(timestamp), data(data) {} Message(const Message& m): id(m.id), timestamp(m.timestamp), data(m.data) {} int id; void* data; uint64 timestamp; }; struct CurrentMessage { CurrentMessage(AnimateMessage* message, uint64 timestamp): message(message), timestamp(timestamp), lastTimeDelta(0), isEmpty(false) {} CurrentMessage(const CurrentMessage& m): message(m.message), timestamp(m.timestamp), lastTimeDelta(m.lastTimeDelta), isEmpty(m.isEmpty) {} AnimateMessage* message; uint64 timestamp; uint64 lastTimeDelta; bool isEmpty; }; int state; map<int, ResourceHolder*> images; map<int, AnimateMessage*> rules; uint64 lastTimestamp; vector<Message> messages; vector<CurrentMessage> currentMessages; bool isAnimated; int refreshCnt; public: AnimatedSprite(ISpriteOwner* scene, int x, int y, int zOrder = 0); AnimatedSprite(ISpriteOwner* scene, const char* res, int x, int y, int zOrder = 0, int loc = elNothing); ~AnimatedSprite(); void clearMessageRules() {rules.clear();} void addMessageRule(int msg, AnimateMessage* rule); virtual void addImage(const char*res, int id = 0, int loc = 0); virtual CIw2DImage* getImage(int id = 0); virtual int getState(); virtual bool setState(int newState); virtual void update(uint64 timestamp); virtual void refresh(); virtual bool sendMessage(int msg, uint64 timestamp = 0, void* data = NULL); virtual bool isBuzy() {return false;} virtual bool isValidMessage(int msg) {return (msg <= emtSystemMessage);} virtual void doMessage(int msg, void* data = NULL, uint64 timestamp = 0); virtual void unload(); typedef map<int, ResourceHolder*>::iterator IIter; typedef pair<int, ResourceHolder*> IPair; typedef map<int, AnimateMessage*>::iterator RIter; typedef pair<int, AnimateMessage*> RPair; typedef vector<Message>::iterator MIter; typedef vector<CurrentMessage>::iterator CIter; }; #endif // _ANIMATEDSPRITE_H_

AnimatedSprite.cpp

#include "AnimatedSprite.h" #include "Desktop.h" #include "Locale.h" AnimatedSprite::AnimatedSprite(ISpriteOwner* scene, int x, int y, int zOrder): Sprite(scene, x, y, zOrder) , state(0) , images() , lastTimestamp(0) , messages() , currentMessages() , isAnimated(false) , refreshCnt(REFRESH_CNT) , rules() {} AnimatedSprite::AnimatedSprite(ISpriteOwner* scene, const char* res, int x, int y, int zOrder, int loc): Sprite(scene, x, y, zOrder) , state(0) , images() , lastTimestamp(0) , messages() , currentMessages() , isAnimated(false) , refreshCnt(REFRESH_CNT) , rules() { AnimatedSprite::addImage(res, 0, loc); } AnimatedSprite::~AnimatedSprite() { for (RIter p = rules.begin(); p != rules.end(); ++p) { delete p->second; } } void AnimatedSprite::unload() { for (IIter p = images.begin(); p != images.end(); ++p) { p->second->unload(); } } void AnimatedSprite::addMessageRule(int msg, AnimateMessage* rule) { RIter p = rules.find(msg); if (p != rules.end()) { return; } rules.insert(RPair(msg, rule)); } void AnimatedSprite::addImage(const char*res, int id, int loc) { ResourceHolder* img = rm.load(res, loc); images.insert(IPair(id, img)); } bool AnimatedSprite::setState(int newState) { IIter p = images.find(newState); if (p == images.end()) { return false; } state = newState; return true; } CIw2DImage* AnimatedSprite::getImage(int id) { IIter p = images.find(id); if (p == images.end()) { return NULL; } return p->second->getData(); } int AnimatedSprite::getState() { return state; } void AnimatedSprite::doMessage(int msg, void* data, uint64 timestamp) { init(); int s = getState(); switch (msg) { case emtStartAnimation: isAnimated = true; break; case emtStopAnimation: isAnimated = false; break; case emtSwitch: s++; if (getImage(s) == NULL) { s = 0; } setState(s); return; case emtHide: isVisible = false; return; case emtShadow: isVisible = true; alpha = IW_2D_ALPHA_HALF; return; case emtShow: isVisible = true; alpha = IW_2D_ALPHA_NONE; return; }; if (timestamp == 0) { timestamp = s3eTimerGetMs(); } RIter p = rules.find(msg); if (p != rules.end()) { for (CIter q = currentMessages.begin(); q != currentMessages.end(); ++q) { if (q->isEmpty) { q->isEmpty = false; q->message = p->second; q->timestamp = timestamp; q->lastTimeDelta = 0; return; } } currentMessages.push_back(CurrentMessage(p->second, timestamp)); } } bool AnimatedSprite::sendMessage(int msg, uint64 timestamp, void* data) { if (!isValidMessage(msg)) { return false; } if (timestamp <= lastTimestamp) { doMessage(msg, data); return true; } messages.push_back(Message(msg, timestamp, data)); return true; } void AnimatedSprite::update(uint64 timestamp) { bool isEmpty = true; for (MIter p = messages.begin(); p != messages.end(); ++p) { if (p->timestamp <= lastTimestamp) continue; if (p->timestamp <= timestamp) { doMessage(p->id, p->data, p->timestamp); continue; } isEmpty = false; } if (isEmpty) { messages.clear(); } isEmpty = true; for (CIter p = currentMessages.begin(); p != currentMessages.end(); ++p) { if (p->isEmpty) continue; uint64 timeDelta = timestamp - p->timestamp; if (!p->message->update(timeDelta, p->lastTimeDelta)) { p->isEmpty = true; continue; } p->lastTimeDelta = timeDelta; isEmpty = false; } if (isEmpty) { currentMessages.clear(); } lastTimestamp = timestamp; } void AnimatedSprite::refresh() { if (isAnimated) { if (--refreshCnt <= 0) { refreshCnt = REFRESH_CNT; doMessage(emtSwitch); } } Sprite::refresh(); }

В отличие от обычного спрайта, анимированный может загружать несколько изображений, связывая их с некоторыми числовыми значениями.

Мы назовем эти значения состояниями спрайта.

Обработка событий будет выполняться в doMessage. Еще одним важным компонентом нашей библиотеки будет составной спрайт, который позволяет манипулировать набором спрайтов (включая составные и анимированные) как единым целым.

Реализация этого класса очень примитивна: CompositeSprite.h

#ifndef _COMPOSITESPRITE_H_ #define _COMPOSITESPRITE_H_ #include "AnimatedSprite.h" #include "AbstractSpriteOwner.h" #include "Scene.h" class CompositeSprite: public AnimatedSprite , public AbstractSpriteOwner { protected: ISpriteOwner* owner; public: CompositeSprite(ISpriteOwner* scene, int x, int y, int zOrder); virtual int getXSize(int xSize); virtual int getYSize(int ySize); virtual int getXPos(int x); virtual int getYPos(int y); virtual bool setState(int newState); virtual void refresh(); virtual void update(uint64 timestamp); virtual bool isBuzy(); virtual bool sendMessage(int msg, uint64 timestamp = 0, void* data = NULL); virtual bool sendMessage(int msg, int x, int y); virtual void unload(); }; #endif // _COMPOSITESPRITE_H_

КомпозитСпрайт.cpp

#include "CompositeSprite.h" CompositeSprite::CompositeSprite(ISpriteOwner* scene, int x, int y, int zOrder): AnimatedSprite(scene, x, y, zOrder), owner(scene), AbstractSpriteOwner() {} int CompositeSprite::getXSize(int xSize) { return owner->getXSize(xSize); } int CompositeSprite::getYSize(int ySize) { return owner->getYSize(ySize); } int CompositeSprite::getXPos(int x) { return AbstractScreenObject::getXPos() + owner->getXPos(x); } int CompositeSprite::getYPos(int y) { return AbstractScreenObject::getYPos() + owner->getYPos(y); } void CompositeSprite::refresh() { if (isVisible) { init(); AbstractSpriteOwner::refresh(); } } void CompositeSprite::update(uint64 timestamp) { AnimatedSprite::update(timestamp); AbstractSpriteOwner::update(timestamp); } bool CompositeSprite::isBuzy() { return AnimatedSprite::isBuzy(); } bool CompositeSprite::sendMessage(int msg, uint64 timestamp, void* data) { return AnimatedSprite::sendMessage(msg, timestamp, data) || owner->sendMessage(msg, timestamp, data); } bool CompositeSprite::sendMessage(int msg, int x, int y) { if (!isVisible) return false; return AbstractSpriteOwner::sendMessage(msg, x, y); } bool CompositeSprite::setState(int newState) { state = newState; return true; } void CompositeSprite::unload() { AbstractSpriteOwner::unload(); }

Чтобы иметь возможность определить достаточно сложную анимацию, определим правила анимации по следующей схеме:

Фреймворк в Мармеладе (часть 4)

В AnimatedSprite добавлено несколько коллекций.

Правила будут содержать соответствие между числовыми кодами сообщений и некоторыми более крупными правилами выполнения анимации AnimateMessage. Список currentMessages будет содержать список правил, по которым в данный момент выполняется анимация (одновременно может обрабатываться несколько правил), messages будут содержать сообщения, которые необходимо обработать.

В свою очередь, AnimateMessage будет содержать список основных действий AnimateAction, для каждого из которых будет установлено время начала и окончания действия относительно момента начала анимации (эти значения будут одинаковыми для действий, выполняемых одновременно).

.

AnimateMessage.h

#ifndef _ANIMATEMESSAGE_H_ #define _ANIMATEMESSAGE_H_ #include <set> #include "s3eTypes.h" #include "AnimateAction.h" using namespace std; class AnimateMessage { private: set<AnimateAction*> actions; public: AnimateMessage(); ~AnimateMessage(); bool update(uint64 newDelta, uint64 oldDelta); void addAction(AnimateAction* action) {actions.insert(action);} typedef set<AnimateAction*>::iterator AIter; }; #endif // _ANIMATEMESSAGE_H_

AnimateMessage.cpp

#include "AnimateMessage.h" AnimateMessage::AnimateMessage(): actions() {} AnimateMessage::~AnimateMessage() { for (AIter p = actions.begin(); p != actions.end(); ++p) { delete *p; } } bool AnimateMessage::update(uint64 newDelta, uint64 oldDelta) { bool r = false; for (AIter p = actions.begin(); p != actions.end(); ++p) { if ((*p)->isSheduled(oldDelta)) { r = true; (*p)->update(newDelta); } else { (*p)->clear(); } } return r; }

Метод обновления принимает две метки времени: текущее и предыдущее значения.

Его реализация тривиальна.

Все AnimateActions перебираются и, если анимация не завершена, вызывается метод обновления.

В противном случае состояние AnimateAction возвращается в исходное состояние.

Если найден хотя бы один элемент, анимация которого не завершена, метод обновления возвращает true. AnimateAction.h

#ifndef _ANIMATEACTION_H_ #define _ANIMATEACTION_H_ #include "s3eTypes.h" #include "AbstractScreenObject.h" class AnimateAction { private: uint64 startDelta; uint64 stopDelta; protected: AbstractScreenObject* sprite; virtual void doAction(int timeDelta) = 0; virtual int getTimeInterval() {return (int)(stopDelta - startDelta);} public: AnimateAction(AbstractScreenObject* sprite, uint64 startDelta, uint64 stopDelta); virtual ~AnimateAction() {} virtual bool isSheduled(uint64 timeDelta); virtual void update(uint64 timeDelta); virtual void clear() {} }; #endif // _ANIMATEACTION_H_

AnimateAction.cpp

#include "AnimateAction.h" AnimateAction::AnimateAction(AbstractScreenObject* sprite, uint64 startDelta, uint64 stopDelta): sprite(sprite) , startDelta(startDelta) , stopDelta(stopDelta) { } bool AnimateAction::isSheduled(uint64 timeDelta) { return timeDelta < stopDelta; } void AnimateAction::update(uint64 timeDelta) { if (timeDelta >= startDelta) { uint64 delta = timeDelta - startDelta; if (timeDelta > stopDelta) { delta = stopDelta - startDelta; } doAction((int)delta); } }

Метод isSheduled возвращает значение true, если полученная метка времени меньше времени окончания действия.

Если полученная метка времени больше или равна времени начала действия, методу doAction передается количество времени, прошедшее с момента запуска действия (но не более общей продолжительности действия).

Поскольку предыдущее значение временной метки передается методу isSheduled, doAction гарантированно будет выполнен хотя бы один раз.

Наследуя AnimateAction, мы имеем возможность расширить список основных эффектов анимации.

В качестве примеров потомков AnimateAction рассмотрим MoveAction, выполняющий линейное перемещение объекта, и SoundAction, воспроизводящий звуковой эффект через заданное время.

MoveAction.h

#ifndef _MOVEACTION_H_ #define _MOVEACTION_H_ #include "AnimateAction.h" class MoveAction: public AnimateAction { private: int x, y; int startX, startY; bool isCleared; protected: virtual void doAction(int timeDelta); virtual void clear() {isCleared = true;} public: MoveAction(AbstractScreenObject* sprite, uint64 startDelta, uint64 stopDelta, int x, int y); MoveAction(AbstractScreenObject* sprite, uint64 delta, int x, int y); }; #endif // _MOVEACTION_H_

MoveAction.cpp

#include "MoveAction.h" MoveAction::MoveAction(AbstractScreenObject* sprite, uint64 startDelta, uint64 stopDelta, int x, int y): AnimateAction(sprite, startDelta, stopDelta) , x(x), y(y), isCleared(true) {} MoveAction::MoveAction(AbstractScreenObject* sprite, uint64 delta, int x, int y): AnimateAction(sprite, delta, delta) , x(x), y(y), isCleared(true) {} void MoveAction::doAction(int timeDelta) { if (isCleared) { startX = sprite->getXDelta(); startY = sprite->getYDelta(); isCleared = false; } int timeInterval = getTimeInterval(); if (timeInterval <= 0) { sprite->setDeltaXY(x, y); } else if (timeDelta > timeInterval) { sprite->setDeltaXY(x, y); } else { int xInterval = x - startX; int yInterval = y - startY; int xDelta = (xInterval * timeDelta) / timeInterval; int yDelta = (yInterval * timeDelta) / timeInterval; sprite->setDeltaXY(startX + xDelta, startY + yDelta); } }

SoundAction.h

#ifndef _SOUNDACTION_H_ #define _SOUNDACTION_H_ #include <string> #include "IwSound.h" #include "AnimateAction.h" #include "Locale.h" using namespace std; class SoundAction: public AnimateAction { private: string res; int loc; bool checkSound(); protected: virtual void doAction(int timeDelta); public: SoundAction(AbstractScreenObject* sprite, uint64 timeDelta, const char* r, int loc = elSound); }; #endif // _SOUNDACTION_H_

SoundAction.cpp

#include "SoundAction.h" #include "Desktop.h" SoundAction::SoundAction(AbstractScreenObject* sprite, uint64 timeDelta, const char* r, int loc): AnimateAction(sprite, timeDelta, timeDelta) , res(r), loc(loc) { } void SoundAction::doAction(int timeDelta) { CIwResGroup* resGroup; const char* groupName = Locale::getGroupName(loc); if (checkSound() &&(groupName != NULL)) { resGroup = IwGetResManager()->GetGroupNamed(groupName); CIwSoundSpec* SoundSpec = (CIwSoundSpec*)resGroup->GetResNamed(res.c_str(), IW_SOUND_RESTYPE_SPEC); CIwSoundInst* SoundInstance = SoundSpec->Play(); } } bool SoundAction::checkSound() { IObject* o = (IObject*)desktop.getName("soundon"); if (o != NULL) { return (o->getState() != 0); } return false; }

Как мы видим, набор AnimateAction можно легко расширить.

Используя описанные выше механизмы анимации, мы создадим небольшой пользовательский интерфейс для управления настройками типичного Android-приложения.

Во-первых, нам нужны кнопки.

Кнопки должны реагировать на нажатия, выполняя простую анимацию: Кнопка.

h

#ifndef _BUTTON_H_ #define _BUTTON_H_ #include "AnimatedSprite.h" #include "AbstractSpriteOwner.h" enum EButtonMessage { ebmDown = 0x0100, ebmUp = 0x0101, ebmOutUp = 0x0111, ebmPressed = 0x0102 }; class Button: public AnimatedSprite { protected: AnimateMessage* msgDown; AnimateMessage* msgUp; int message; AbstractSpriteOwner* receiver; void configure(); public: Button(ISpriteOwner* scene, const char* res, int x, int y, int zOrder = 0, int loc = elNothing); Button(ISpriteOwner* scene, int x, int y, int zOrder = 0); virtual bool isValidMessage(int msg); virtual bool sendMessage(int msg, uint64 timestamp = 0, void* data = NULL); virtual void doMessage(int msg, void* data = NULL, uint64 timestamp = 0); virtual bool isPausable() const {return false;} void addReceiver(int m, AbstractSpriteOwner* r); }; #endif // _BUTTON_H_

Кнопка.

cpp

#include "Button.h" #include "Desktop.h" #include "MoveAction.h" #include "SendMessageAction.h" #include "SoundAction.h" Button::Button(ISpriteOwner* scene, const char* res, int x, int y, int zOrder, int loc): AnimatedSprite(scene, res, x, y, zOrder, loc), receiver(NULL) { Button::configure(); } Button::Button(ISpriteOwner* scene, int x, int y, int zOrder): AnimatedSprite(scene, x, y, zOrder), receiver(NULL) { Button::configure(); } void Button::configure() { msgDown = new AnimateMessage(); msgDown->addAction(new MoveAction(this, 0, 50, 10, 10)); msgDown->addAction(new SoundAction(this, 50, "menubutton")); addMessageRule(ebmDown, msgDown); msgUp = new AnimateMessage(); msgUp->addAction(new MoveAction(this, 100, 150, 0, 0)); addMessageRule(ebmOutUp, msgUp); msgUp = new AnimateMessage(); msgUp->addAction(new MoveAction(this, 100, 150, 0, 0)); msgUp->addAction(new SendMessageAction(this, 100, ebmPressed)); msgUp->addAction(new SendMessageAction(this, 110, emtInit)); addMessageRule(ebmUp, msgUp); } bool Button::isValidMessage(int msg) { switch (msg) { case emtTouchDown: case emtTouchUp: case ebmDown: case ebmUp: case ebmOutUp: case ebmPressed: return true; default: return AnimatedSprite::isValidMessage(msg); } } void Button::doMessage(int msg, void* data, uint64 timestamp) { if (msg == ebmPressed) { if (receiver != NULL) { receiver->sendMessage(message, 0, (IObject*)this); } return; } AnimatedSprite::doMessage(msg, data, timestamp); } bool Button::sendMessage(int msg, uint64 timestamp, void* data) { if ((msg & emtTouchEvent) != 0) { switch (msg & emtTouchMask) { case emtTouchDown: sendMessage(ebmDown, desktop.getCurrentTimestamp()); break; case emtTouchUp: sendMessage(ebmUp, desktop.getCurrentTimestamp()); break; case emtTouchOutUp: sendMessage(ebmOutUp, desktop.getCurrentTimestamp()); break; } return true; } return AnimatedSprite::sendMessage(msg, timestamp, data); } void Button::addReceiver(int m, AbstractSpriteOwner* r) { message = m; receiver = r; }

В методе sendMessage мы обрабатываем коды событий сенсорной панели, генерируя внутренние события, с которыми работает кнопка.

В конфигурации с этими событиями связана анимация; при нажатии кнопка перемещается на 10 единиц вниз и вправо, а при отпускании возвращается на место.

Если при отпускании точка касания не отходит от кнопки, генерируется событие ebmPressed, с которым мы можем связать произвольный обработчик.

Помимо обычных кнопок нам понадобятся кнопки-переключатели с изменяемой картинкой.

Поскольку эта кнопка также должна анимироваться при нажатии, давайте унаследуем ее от Button: SwitchButton.h

#ifndef _SWITCHBUTTON_H_ #define _SWITCHBUTTON_H_ #include "Button.h" class SwitchButton: public Button { protected: void configure(); public: SwitchButton(ISpriteOwner* scene, int x, int y, int zOrder = 0); virtual bool sendMessage(int msg, uint64 timestamp = 0, void* data = NULL); }; #endif // _SWITCHBUTTON_H_

SwitchButton.cpp

#include "SwitchButton.h" #include "Desktop.h" #include "MoveAction.h" #include "SendMessageAction.h" #include "SoundAction.h" SwitchButton::SwitchButton(ISpriteOwner* scene, int x, int y, int zOrder): Button(scene, x, y, zOrder) { SwitchButton::configure(); } void SwitchButton::configure() { msgUp->addAction(new SendMessageAction(this, 50, emtSwitch)); } bool SwitchButton::sendMessage(int msg, uint64 timestamp, void* data) { if (msg == emtSwitch) { doMessage(msg, 0, timestamp); if (receiver != NULL) { receiver->sendMessage(message, 0, (IObject*)this); } return true; } return Button::sendMessage(msg, timestamp, data); }

Мы просто добавляем событие emtSwitch в анимацию отпускания кнопки, которое переключает изображение для любого AnimatedSprite. Теперь вы готовы разработать пользовательский интерфейс.

Класс Intro будет содержать два экрана — стартовый экран и экран настроек.

Введение.

h

#ifndef _INTRO_H_ #define _INTRO_H_ #include "Scene.h" #include "CompositeSprite.h" enum EIntroMessage { eimPlay = 0x100, eimSettings = 0x101, eimBack = 0x102, eimCheckMusic = 0x103 }; enum EIntroStatus { eisMain = 0, eisSettings = 1 }; class Intro: public Scene { private: Sprite* background; CompositeSprite* title; CompositeSprite* menu; CompositeSprite* settings; int state; void checkMusic(); protected: virtual bool doKeyMessage(int msg, s3eKey key); virtual int getState() {return state;} void setState(int s) {state = s;} public: Intro(); virtual bool init(); virtual bool sendMessage(int msg, uint64 timestamp = 0, void* data = NULL); }; extern Intro* introScene; #endif // _INTRO_H_

Введение.

cpp

#include "Intro.h" #include "Background.h" #include "IntroTitle.h" #include "IntroMenu.h" #include "IntroSound.h" #include "Desktop.h" Intro* introScene = NULL; Intro::Intro(): state(eisMain) { introScene = this; } bool Intro::init() { if (!Scene::init()) return false; regKey(s3eKeyBack); regKey(s3eKeyAbsBSK); #if defined IW_DEBUG regKey(s3eKeyLSK); #endif background = new Background(this, "background.png", 1); title = new IntroTitle(this, 2); menu = new IntroMenu(this, 3); settings = new IntroSound(this, 4); settings->doMessage(emtHide); checkMusic(); return true; } bool Intro::doKeyMessage(int msg, s3eKey key) { if (msg == emtKeyPressed) { switch (state) { case eisSettings: sendMessage(eimBack); return true; } } return false; } bool Intro::sendMessage(int msg, uint64 timestamp, void* data) { switch (msg) { case eimPlay: // TODO: return true; case eimSettings: background->setAlpha(IW_2D_ALPHA_HALF); title->doMessage(emtHide); menu->doMessage(emtHide); settings->doMessage(emtShow); setState(eisSettings); return true; case eimBack: background->setAlpha(IW_2D_ALPHA_NONE); title->doMessage(emtShow); menu->doMessage(emtShow); settings->doMessage(emtHide); setState(eisMain); return true;

Теги: #мармелад #Android #iOS #разработка для iOS #разработка для Android

Вместе с данным постом часто просматривают: