Сегодня мы сделаем первый шаг к анимации, добавив в наш проект два контейнера спрайтов. Один из них позволит загружать несколько изображений, сопоставляя каждое из них с неким числовым статусом спрайта. Второй позволит манипулировать набором спрайтов, как единым целым. Начнем, как обычно, с добавления новых файлов в проект:
#!/usr/bin/env mkb
...
includepath
{
includepath
{
...
./source/Animate
}
files
{
./source/Animate
}
files
{
...
[Animate]
(source/Animate)
IAnimatedSprite.h
AnimatedSprite.cpp
AnimatedSprite.h
CompositeSprite.cpp
CompositeSprite.h
...
}
...
[Animate]
(source/Animate)
IAnimatedSprite.h
AnimatedSprite.cpp
AnimatedSprite.h
CompositeSprite.cpp
CompositeSprite.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_
#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, позволит передать сообщение на обработку. В параметре data могут передаваться произвольные данные. Для чего нужен timestamp, мы рассмотрим в следующей статье. Поскольку мы заговорили о сообщениях, сразу добавим новые коды сообщений в 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
};
...
#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
};
...
Пока не будем останавливаться на каждом коде. Для чего они нужны, будет ясно из последующего изложения. После того как мы проделали всю предварительную работу, можно добавлять наши контейнеры.
AnimatedSprite.h:
#define _ANIMATEDSPRITE_H_
#include <map>
#include <vector>
#include "Sprite.h"
#include "IAnimatedSprite.h"
#include "ResourceManager.h"
#define REFRESH_CNT 2
using namespace std;
class AnimatedSprite: public Sprite,
public IAnimatedSprite {
protected:
int state;
map<int, ResourceHolder*> images;
uint64 lastTimestamp;
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 = elImage);
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 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;
};
#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)
, isAnimated(false)
, refreshCnt(REFRESH_CNT) {}
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)
, isAnimated(false)
, refreshCnt(REFRESH_CNT) {
AnimatedSprite::addImage(res, 0, loc);
}
void AnimatedSprite::unload() {
for (IIter p = images.begin(); p != images.end(); ++p) {
p->second->unload();
}
}
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();
}
}
bool AnimatedSprite::sendMessage(int msg, uint64 timestamp, void* data) {
if (!isValidMessage(msg)) {
return false;
}
if (timestamp <= lastTimestamp) {
doMessage(msg, data);
return true;
}
return false;
}
void AnimatedSprite::refresh() {
if (isAnimated) {
if (--refreshCnt <= 0) {
refreshCnt = REFRESH_CNT;
doMessage(emtSwitch);
}
}
Sprite::refresh();
}
#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)
, isAnimated(false)
, refreshCnt(REFRESH_CNT) {}
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)
, isAnimated(false)
, refreshCnt(REFRESH_CNT) {
AnimatedSprite::addImage(res, 0, loc);
}
void AnimatedSprite::unload() {
for (IIter p = images.begin(); p != images.end(); ++p) {
p->second->unload();
}
}
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();
}
}
bool AnimatedSprite::sendMessage(int msg, uint64 timestamp, void* data) {
if (!isValidMessage(msg)) {
return false;
}
if (timestamp <= lastTimestamp) {
doMessage(msg, data);
return true;
}
return false;
}
void AnimatedSprite::refresh() {
if (isAnimated) {
if (--refreshCnt <= 0) {
refreshCnt = REFRESH_CNT;
doMessage(emtSwitch);
}
}
Sprite::refresh();
}
Главным отличием этого класса от Sprite является возможность загрузки нескольких излбражений, ассоциированных с числовыми кодами статусов. Кроме того, AnimatedSprite обрабатывает служебные сообщения, позволяющие управлять его видимостью, переключать текущее изображение и т.п.
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_
#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();
}
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();
}
Можно видеть, что CompositeSprite представляет собой контейнер спрайтов с минимальной функциональностью. Пожалуй, единственные методы, заслуживающие внимания, здесь - это getXPos и getYPos, вычисляющие координаты, с учетом координат самого CompositeSprite. Изменив координаты CompositeSprite, мы переместим на экране все включенные в него спрайты (в том числе и композитные). Вследующей статье, мы заставим картинки двигаться.
Как обычно, полные исходники проекта можно загрузить здесь.
Комментариев нет:
Отправить комментарий