Теперь, после того как мы проделали всю подготовительную работу, самое время заняться главным - реализацией анимации. Немного переформулируем требования, составленные нами в первой статье цикла, детализируя их, в контексте решаемой задачи:
- Интерфейс подсистемы анимации должен быть событийно-ориентированным. Для того чтобы инициировать анимационный эффект мы должны передать сообщение ответственному за этот эффект объекту.
- При выполнении равномерного прямолинейного перемещения спрайта, интерфейс подсистемы анимации должен принимать начальное и конечное положение перемещения и вычислять все промежуточные положения спрайта самостоятельно.
- Разрабатываемый интерфейс должен поддерживать возможность составления анимации из последовательности перемещений. Также, должна поддерживаться возможность синхронного перемещения нескольких объектов, в рамках одного анимационного эффекта.
- Реализация подсистемы анимации должна быть расширяемой, таким образом, чтобы, при необходимости мы могли добавить к списку базовых анимационных эффектов, например, поворот изображения, проигрывание звукового файла или что-то, что мы еще не придумали.
Основную нагрузку, по реализации анимационных эффектов, возьмет на себя класс AnimatedSprite. Поскольку CompositeSprite является его наследником, все сказанное ниже, в равной мере, будет относится и к нему. Построим диаграмму классов, иллюстрирующую архитектуру разрабатываемой нами подсистемы:
Мы видим, что в AnimatedSprite добавляется несколько коллекций. В rules будет содержаться соответствие числовых кодов сообщений некоторым укрупненным правилам выполнения анимации AnimateMessage. Список currentMessages будет содержать список правил, по которым производится анимация в настоящее время (одновременно может обрабатываться несколько правил), в messages будут содержаться сообщения, которые предстоит обработать.
В свою очередь, AnimateMessage будет содержать список базовых действий AnimateAction, для каждого из которых будут заданы время начала и завершения действия, относительно момента начала анимации (эти значения будут совпадать, для действий, выполняемых единомоментно). Наследуя от AnimateAction мы имеем возможность расширения списка базовых анимационных эффектов. В целях иллюстрации концепции, создадим два наследника: MoveAction - осуществляющий равномерное прямолинейное перемещение спрайта и SendMessageAction - выполняющий передачу произвольному объекту сообщения, в заданное время. Реализуем эти классы:
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_
#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;
}
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;
}
Метод update принимает два timestamp-а: текущее и предыдущее значения. Его реализация тривиальна. Производится перебор всех AnimateAction и, в случае, если его анимация не завершена, вызывается метод update. В противном случае, состояние AnimateAction сбрасывается в исходное. Если найден хотя-бы один элемент, анимация которого не завершена, метод 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,
#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_
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);
}
}
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);
}
}
Реализация AnimateAction также не вызывает вопросов. Метод isSheduled возвращает true, в случае если полученная временная отметка меньше времени завершения действия. Если полученная временная отметка больше или равна времени начала действия, в метод doAction передается значение времени, прошедшего с момента начала действия (но не более общей продолжительности выполнения действия). Поскольку в метод isSheduled передается предыдущее значение timestamp-а, гарантируется по крайней мере однократное выполнение doAction, независимо от того, с какой периодичностью будет вызываться метод update.
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,
#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(AbstractScreenObject* sprite, uint64 delta, int x, int y);
};
#endif // _MOVEACTION_H_
MoveAction.cpp:
#include "MoveAction.h"
MoveAction::MoveAction(AbstractScreenObject* sprite, uint64 startDelta,
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);
}
}
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);
}
}
Реализация MoveAction выглядит несколько переусложненной, но именно такой вид она обрела в процессе отладки framework-а. Основная задача этого класса - вычисление промежуточных положений спрайта при выполнении его равномерного прямолинейного движения.
SendMessageAction.h:
#ifndef _SENDMESSAGEACTION_H_
#define _SENDMESSAGEACTION_H_
#include "AnimateAction.h"
class SendMessageAction: public AnimateAction {
private:
int msg;
void* data;
protected:
virtual void doAction(int timeDelta);
public:
SendMessageAction(AbstractScreenObject* sprite, uint64 timeDelta,
#define _SENDMESSAGEACTION_H_
#include "AnimateAction.h"
class SendMessageAction: public AnimateAction {
private:
int msg;
void* data;
protected:
virtual void doAction(int timeDelta);
public:
SendMessageAction(AbstractScreenObject* sprite, uint64 timeDelta,
int msg, void* data = NULL);
};
#endif // _SENDMESSAGEACTION_H_
};
#endif // _SENDMESSAGEACTION_H_
SendMessageAction.cpp:
#include "SendMessageAction.h"
SendMessageAction::SendMessageAction(AbstractScreenObject* sprite,
SendMessageAction::SendMessageAction(AbstractScreenObject* sprite,
uint64 timeDelta, int msg,
void* data): AnimateAction(sprite, timeDelta, timeDelta), msg(msg), data(data) {}
void SendMessageAction::doAction(int timeDelta) {
sprite->sendMessage(msg, 0, data);
}
void SendMessageAction::doAction(int timeDelta) {
sprite->sendMessage(msg, 0, data);
}
Реализация SendMessageAction прямолинейна. Заданному объекту передается сообщение, при достижении временной метки. В класс AbstractScreenObject внесены небольшие дополнения:
#ifndef _ABSTRACTSCREENOBJECT_H_
#define _ABSTRACTSCREENOBJECT_H_
#define _ABSTRACTSCREENOBJECT_H_
...
class AbstractScreenObject: public IScreenObject {
...
...
public:
int getXDelta() const {return xDelta;}
int getYDelta() const {return yDelta;}
void setDeltaXY(int x = 0, int y = 0) {xDelta = x; yDelta = y;}
int getXDelta() const {return xDelta;}
int getYDelta() const {return yDelta;}
void setDeltaXY(int x = 0, int y = 0) {xDelta = x; yDelta = y;}
...
};
#endif // _ABSTRACTSCREENOBJECT_H_
#endif // _ABSTRACTSCREENOBJECT_H_
Осталось внести необходимые изменения в 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):
#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):
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):
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):
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,
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();
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,
#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,
, 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;
}
, 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();
}
Здесь стоит обратить внимание на метод update, осуществляющий обработку правил анимации, а также на изменения в методах sendMessage и doMessage, благодоря которым заполняется очередь сообщений ожидающих обработку.
В следующей части, я продемонстрирую как можно использовать разработанный framework для построения простого пользовательского интерфейса.
Комментариев нет:
Отправить комментарий