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

マーマレードフレームワークの開発に関するシリーズの最後の記事では、アニメーションスプライトとコンポジットスプライトをプロジェクトに追加し、ライブラリの使用原理を示す小さなデモアプリケーションを作成します。



イベント指向のフレームワークを開発しているため、アクションの実行(アニメーションを含む)はイベントの処理の結果として実行されます。 基本的なイベントのセットは、デスクトップモジュールで定義されます。



Desktop.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 }; ...
      
      







後で見るように、デスクトップモジュールを変更せずに新しいイベントを簡単に識別できます。 EMessageTypeを列挙することは、すべてのベースイベントコードをまとめる便利な方法にすぎません。 アニメーションスプライトのタスクは、基本的なイベントの処理をカプセル化することです。 アニメーションスプライトインターフェイスには、2つのメソッドのみが含まれています。



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に加えて、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で行われます。



ライブラリのもう1つの重要なコンポーネントは、スプライトのセット(コンポジットとアニメーションを含む)全体を操作できるコンポジットスプライトです。 このクラスの実装は非常に原始的です:



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_
      
      







CompositeSprite.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(); }
      
      







十分に複雑なアニメーションを定義できるように、次のスキームに従ってアニメーションルールを定義します。



画像



AnimatedSpriteはいくつかのコレクションを追加します。 ルールには、数値メッセージコードと、AnimateMessageをアニメートするためのいくつかの拡大されたルールの対応が含まれます。 currentMessagesリストには、現在アニメーション化されているルールのリストが含まれます(複数のルールを同時に処理できます)。メッセージには、処理されるメッセージが含まれます。



次に、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; }
      
      







更新メソッドは、現在の値と前の値の2つのタイムスタンプを取ります。 その実装は簡単です。 すべてのAnimateActionが列挙され、そのアニメーションが完了していない場合、updateメソッドが呼び出されます。 それ以外の場合、AnimateAction状態はリセットされます。 アニメーションが完全でない要素が少なくとも1つ見つかった場合、updateメソッドは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メソッドに渡されるため、少なくとも1つの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アプリケーションの設定を管理します。 まず、ボタンが必要です。 ボタンは、単純なアニメーションを実行してクリックに応答する必要があります。



Button.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_
      
      







Button.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クラスには、開始画面と設定画面の2つの画面が含まれます。



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_
      
      







Intro.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; case emtInit: case eimCheckMusic: checkMusic(); return true; } return false; } void Intro::checkMusic() { bool f = false; IObject* o = (IObject*)desktop.getName("musicon"); if (o == NULL) { desktop.stopMusic(); return; } f = (o->getState() != 0); if (f) { desktop.startMusic("music.mp3"); } else { desktop.stopMusic(); } }
      
      







Introクラスは、アプリケーションで複数のCompositeSpriteをロードする唯一のシーンを表します。 SendMessageはイベントハンドラを定義します。 次に、いくつかの複合スプライトを定義します。このタスクの目的は、プログラムの名前と、ボタンのセットとして実装されるメニューを表示することです。



IntroTitle.h
 #ifndef _INTROTITLE_H_ #define _INTROTITLE_H_ #include <string.h> #include "CompositeSprite.h" class IntroTitle: public CompositeSprite { public: IntroTitle(Scene* scene, int zOrder): CompositeSprite(scene, 0, 0, zOrder) {} virtual bool init(); virtual void refresh(); }; #endif // _INTROTITLE_H_
      
      







IntroTitle.cpp
 #include "IntroTitle.h" #include "Sprite.h" bool IntroTitle::init() { if (!AbstractScreenObject::init()) return false; // Sprite settings setXY(122, 100); // Sprite components new Sprite(this, "sprite.png", 0, 0, 1); return true; } void IntroTitle::refresh() { CompositeSprite::refresh(); }
      
      







IntroMenu.h
 #ifndef _INTROMENU_H_ #define _INTROMENU_H_ #include "CompositeSprite.h" #include "Button.h" class IntroMenu: public CompositeSprite { private: Scene* scene; Button* firstButton; Button* secondButton; public: IntroMenu(Scene* scene, int zOrder): CompositeSprite(scene, 0, 0, zOrder), scene(scene) {} virtual bool init(); }; #endif // _INTROMENU_H_
      
      







IntroMenu.cpp
 #include "IntroMenu.h" #include "Locale.h" #include "Intro.h" #include "Desktop.h" bool IntroMenu::init() { if (!AbstractScreenObject::init()) return false; setXY(297, 384); firstButton = new Button(this, "play", 0, 0, 1, Locale::getCurrentImageLocale()); firstButton->addReceiver(eimPlay, scene); secondButton = new Button(this, "setup", 0, 157, 2, Locale::getCurrentImageLocale()); secondButton->addReceiver(eimSettings, scene); return true; }
      
      







IntroSound.h
 #ifndef _INTROSOUND_H_ #define _INTROSOUND_H_ #include "CompositeSprite.h" class IntroSound: public CompositeSprite { private: Scene* scene; public: IntroSound(Scene* scene, int zOrder): CompositeSprite(scene, 0, 0, zOrder), scene(scene) {} virtual bool init(); }; #endif // _INTROSOUND_H_
      
      







IntroSound.cpp
 #include "IntroSound.h" #include "SwitchButton.h" #include "Button.h" #include "Intro.h" #include "Locale.h" bool IntroSound::init() { if (!AbstractScreenObject::init()) return false; setXY(346, 227); SwitchButton* s = new SwitchButton(this, 0, 0, 1); s->addImage("musicoff", 0, Locale::getCurrentImageLocale()); s->addImage("musicon", 1, Locale::getCurrentImageLocale()); s->setName("musicon"); s->setState(1); s->addReceiver(eimCheckMusic, scene); s = new SwitchButton(this, 0, 157, 2); s->addImage("soundoff", 0, Locale::getCurrentImageLocale()); s->addImage("soundon", 1, Locale::getCurrentImageLocale()); s->setName("soundon"); s->setState(1); Button* b = new Button(this, "back.png", -300, 350, 3, Locale::getCommonImageLocale()); b->addReceiver(eimBack, scene); return true; }
      
      







アプリケーションを起動することで、ボタンが押されたことに応答し、画面間で指定された切り替えを実行することを確認できます。



そのため、クロスプラットフォームモバイルアプリケーションを開発するためのMarmalade APIの機能のいくつかを研究しながら、最もシンプルなフレームワークを開発することに成功しました。 もちろん、開発されたライブラリには、本格的なゲームフレームワークと見なされるための多くの機能が欠けています(たとえば、物理実装をサポートしていません)が、その基盤となる柔軟なアーキテクチャにより、必要な拡張機能を簡単に追加できます。



プロジェクトのソースコードは、次のリンクにあります。



マーマレードフレームワークの開発では、次の資料が使用されました




All Articles