В прошлой статье, мы начали писать простой Framework, предназначенный для разработки 2D-игр, с использованием Marmalade. Сегодня мы продолжим это начинание, добавив обработку событий TouchPad-а и клавиатуры.
TouchPad является основным нашим средством ввода и, по возможности, должен поддерживать широко используемый, в настоящее время, Multitouch. Обработка событий клавиатуры, на мобильных устройствах используется реже, но есть у нее одно важное применение, используемое практически в любом приложении - кнопка s3eKeyAbsBSK ("Назад") должна возвращать приложение к предыдущей активности (в терминологии Android) и закрывать приложение, если мы находимся в начальном экране.
Как обычно, начнем доработку с mkb-файла. В секцию files добавим новые файлы:
#!/usr/bin/env mkb
...
files
{
[Main]
(source/Main)
{
[Main]
(source/Main)
...
TouchPad.cpp
TouchPad.h
}
...
TouchPad.cpp
TouchPad.h
}
...
Содержимое этих файлов будет следующим:
TouchPad.h:
#ifndef _TOUCHPAD_H_
#define _TOUCHPAD_H_
#include "IwGeom.h"
#include "s3ePointer.h"
#define MAX_TOUCHES 11
struct Touch {
public:
int x, y;
bool isActive, isPressed, isMoved;
int id;
};
class TouchPad {
private:
bool IsAvailable;
bool IsMultiTouch;
Touch Touches[MAX_TOUCHES];
public:
static bool isTouchDown(int eventCode);
static bool isTouchUp(int eventCode);
bool isAvailable() const { return IsAvailable; }
bool isMultiTouch() const { return IsMultiTouch; }
Touch* getTouchByID(int id);
Touch* getTouch(int index) { return &Touches[index]; }
Touch* findTouch(int id);
int getTouchCount() const;
bool init();
void release();
void update();
void clear();
};
extern TouchPad touchPad;
#endif // _TOUCHPAD_H_
#define _TOUCHPAD_H_
#include "IwGeom.h"
#include "s3ePointer.h"
#define MAX_TOUCHES 11
struct Touch {
public:
int x, y;
bool isActive, isPressed, isMoved;
int id;
};
class TouchPad {
private:
bool IsAvailable;
bool IsMultiTouch;
Touch Touches[MAX_TOUCHES];
public:
static bool isTouchDown(int eventCode);
static bool isTouchUp(int eventCode);
bool isAvailable() const { return IsAvailable; }
bool isMultiTouch() const { return IsMultiTouch; }
Touch* getTouchByID(int id);
Touch* getTouch(int index) { return &Touches[index]; }
Touch* findTouch(int id);
int getTouchCount() const;
bool init();
void release();
void update();
void clear();
};
extern TouchPad touchPad;
#endif // _TOUCHPAD_H_
TouchPad.cpp:
#include "TouchPad.h"
#include "Desktop.h"
TouchPad touchPad;
bool TouchPad::isTouchDown(int eventCode) {
return (eventCode & emtTouchMask) == emtTouchDown;
}
bool TouchPad::isTouchUp(int eventCode) {
return (eventCode & emtTouchMask) == emtTouchUp;
}
void HandleMultiTouchButton(s3ePointerTouchEvent* event) {
Touch* touch = touchPad.findTouch(event->m_TouchID);
if (touch != NULL) {
touch->isPressed = event->m_Pressed != 0;
touch->isActive = true;
touch->x = event->m_x;
touch->y = event->m_y;
touch->id = event->m_TouchID;
}
}
void HandleMultiTouchMotion(s3ePointerTouchMotionEvent* event) {
Touch* touch = touchPad.findTouch(event->m_TouchID);
if (touch != NULL) {
if (touch->isActive) {
touch->isMoved = true;
}
touch->isActive = true;
touch->x = event->m_x;
touch->y = event->m_y;
}
}
void HandleSingleTouchButton(s3ePointerEvent* event) {
Touch* touch = touchPad.getTouch(0);
touch->isPressed = event->m_Pressed != 0;
touch->isActive = true;
touch->x = event->m_x;
touch->y = event->m_y;
touch->id = 0;
}
void HandleSingleTouchMotion(s3ePointerMotionEvent* event) {
Touch* touch = touchPad.getTouch(0);
if (touch->isActive) {
touch->isMoved = true;
}
touch->isActive = true;
touch->x = event->m_x;
touch->y = event->m_y;
}
Touch* TouchPad::getTouchByID(int id) {
for (int i = 0; i < MAX_TOUCHES; i++) {
if (Touches[i].isActive && Touches[i].id == id)
return &Touches[i];
}
return NULL;
}
Touch* TouchPad::findTouch(int id) {
if (!IsAvailable)
return NULL;
for (int i = 0; i < MAX_TOUCHES; i++) {
if (Touches[i].id == id)
return &Touches[i];
if (!Touches[i].isActive) {
Touches[i].id = id;
return &Touches[i];
}
}
return NULL;
}
int TouchPad::getTouchCount() const {
if (!IsAvailable)
return 0;
int r = 0;
for (int i = 0; i < MAX_TOUCHES; i++) {
if (Touches[i].isActive) {
r++;
}
}
return r;
}
void TouchPad::update() {
for (int i = 0; i < MAX_TOUCHES; i++) {
Touches[i].isMoved = false;
}
if (IsAvailable) {
s3ePointerUpdate();
}
}
void TouchPad::clear() {
for (int i = 0; i < MAX_TOUCHES; i++) {
if (!Touches[i].isPressed) {
Touches[i].isActive = false;
}
Touches[i].isMoved = false;
}
}
bool TouchPad::init() {
IsAvailable = s3ePointerGetInt(S3E_POINTER_AVAILABLE) ? true : false;
if (!IsAvailable) return false;
for (int i = 0; i < MAX_TOUCHES; i++) {
Touches[i].isPressed = false;
Touches[i].isActive = false;
Touches[i].isMoved = false;
Touches[i].id = 0;
}
IsMultiTouch = s3ePointerGetInt(S3E_POINTER_MULTI_TOUCH_AVAILABLE)
#include "Desktop.h"
TouchPad touchPad;
bool TouchPad::isTouchDown(int eventCode) {
return (eventCode & emtTouchMask) == emtTouchDown;
}
bool TouchPad::isTouchUp(int eventCode) {
return (eventCode & emtTouchMask) == emtTouchUp;
}
void HandleMultiTouchButton(s3ePointerTouchEvent* event) {
Touch* touch = touchPad.findTouch(event->m_TouchID);
if (touch != NULL) {
touch->isPressed = event->m_Pressed != 0;
touch->isActive = true;
touch->x = event->m_x;
touch->y = event->m_y;
touch->id = event->m_TouchID;
}
}
void HandleMultiTouchMotion(s3ePointerTouchMotionEvent* event) {
Touch* touch = touchPad.findTouch(event->m_TouchID);
if (touch != NULL) {
if (touch->isActive) {
touch->isMoved = true;
}
touch->isActive = true;
touch->x = event->m_x;
touch->y = event->m_y;
}
}
void HandleSingleTouchButton(s3ePointerEvent* event) {
Touch* touch = touchPad.getTouch(0);
touch->isPressed = event->m_Pressed != 0;
touch->isActive = true;
touch->x = event->m_x;
touch->y = event->m_y;
touch->id = 0;
}
void HandleSingleTouchMotion(s3ePointerMotionEvent* event) {
Touch* touch = touchPad.getTouch(0);
if (touch->isActive) {
touch->isMoved = true;
}
touch->isActive = true;
touch->x = event->m_x;
touch->y = event->m_y;
}
Touch* TouchPad::getTouchByID(int id) {
for (int i = 0; i < MAX_TOUCHES; i++) {
if (Touches[i].isActive && Touches[i].id == id)
return &Touches[i];
}
return NULL;
}
Touch* TouchPad::findTouch(int id) {
if (!IsAvailable)
return NULL;
for (int i = 0; i < MAX_TOUCHES; i++) {
if (Touches[i].id == id)
return &Touches[i];
if (!Touches[i].isActive) {
Touches[i].id = id;
return &Touches[i];
}
}
return NULL;
}
int TouchPad::getTouchCount() const {
if (!IsAvailable)
return 0;
int r = 0;
for (int i = 0; i < MAX_TOUCHES; i++) {
if (Touches[i].isActive) {
r++;
}
}
return r;
}
void TouchPad::update() {
for (int i = 0; i < MAX_TOUCHES; i++) {
Touches[i].isMoved = false;
}
if (IsAvailable) {
s3ePointerUpdate();
}
}
void TouchPad::clear() {
for (int i = 0; i < MAX_TOUCHES; i++) {
if (!Touches[i].isPressed) {
Touches[i].isActive = false;
}
Touches[i].isMoved = false;
}
}
bool TouchPad::init() {
IsAvailable = s3ePointerGetInt(S3E_POINTER_AVAILABLE) ? true : false;
if (!IsAvailable) return false;
for (int i = 0; i < MAX_TOUCHES; i++) {
Touches[i].isPressed = false;
Touches[i].isActive = false;
Touches[i].isMoved = false;
Touches[i].id = 0;
}
IsMultiTouch = s3ePointerGetInt(S3E_POINTER_MULTI_TOUCH_AVAILABLE)
? true : false;
if (IsMultiTouch) {
s3ePointerRegister(S3E_POINTER_TOUCH_EVENT,
if (IsMultiTouch) {
s3ePointerRegister(S3E_POINTER_TOUCH_EVENT,
(s3eCallback)HandleMultiTouchButton, NULL);
s3ePointerRegister(S3E_POINTER_TOUCH_MOTION_EVENT,
s3ePointerRegister(S3E_POINTER_TOUCH_MOTION_EVENT,
(s3eCallback)HandleMultiTouchMotion, NULL);
} else {
s3ePointerRegister(S3E_POINTER_BUTTON_EVENT,
} else {
s3ePointerRegister(S3E_POINTER_BUTTON_EVENT,
(s3eCallback)HandleSingleTouchButton, NULL);
s3ePointerRegister(S3E_POINTER_MOTION_EVENT,
s3ePointerRegister(S3E_POINTER_MOTION_EVENT,
(s3eCallback)HandleSingleTouchMotion, NULL);
}
return true;
}
void TouchPad::release() {
if (IsAvailable) {
if (IsMultiTouch) {
s3ePointerUnRegister(S3E_POINTER_TOUCH_EVENT,
}
return true;
}
void TouchPad::release() {
if (IsAvailable) {
if (IsMultiTouch) {
s3ePointerUnRegister(S3E_POINTER_TOUCH_EVENT,
(s3eCallback)HandleMultiTouchButton);
s3ePointerUnRegister(S3E_POINTER_TOUCH_MOTION_EVENT,
s3ePointerUnRegister(S3E_POINTER_TOUCH_MOTION_EVENT,
(s3eCallback)HandleMultiTouchMotion);
} else {
s3ePointerUnRegister(S3E_POINTER_BUTTON_EVENT,
} else {
s3ePointerUnRegister(S3E_POINTER_BUTTON_EVENT,
(s3eCallback)HandleSingleTouchButton);
s3ePointerUnRegister(S3E_POINTER_MOTION_EVENT,
s3ePointerUnRegister(S3E_POINTER_MOTION_EVENT,
(s3eCallback)HandleSingleTouchMotion);
}
}
}
}
}
}
За основу этого кода была взята следующая статья. Здесь следует обратить внимание на код регистрации и разрегистрации Callback-вызовов в методах init и release. Сама по себе, логика обработки событий довольно тривиальна. Далее, внесем необходимые изменения в класс Desktop.
Desktop.h:
#ifndef _DESKTOP_H_
#define _DESKTOP_H_
#include <set>
#include "s3eKeyboard.h"
#include "Scene.h"
using namespace std;
enum EMessageType {
emtNothing = 0x00,
emtTouchEvent = 0x10,
emtTouchIdMask = 0x07,
emtTouchMask = 0x70,
emtMultiTouch = 0x14,
emtTouchDown = 0x30,
emtTouchUp = 0x50,
emtTouchMove = 0x70,
emtSingleTouchDown = 0x30,
emtSingleTouchUp = 0x50,
emtSingleTouchMove = 0x70,
emtMultiTouchDown = 0x34,
emtMultiTouchUp = 0x54,
emtMultiTouchMove = 0x74,
emtKeyEvent = 0x80,
emtKeyAction = 0x82,
emtKeyDown = 0x81,
emtKeyPressed = 0x83,
emtKeyReleased = 0x82
};
class Desktop {
private:
int width, height;
bool isChanged;
Scene* currentScene;
bool isKeyAvailable;
bool IsQuitMessageReceived;
bool checkBounce(int id, int msg);
void getScreenSizes();
static int32 ScreenSizeChangeCallback(void* systemData, void* userData);
public:
Desktop(): touches() {}
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);
void sendQuitMessage() {IsQuitMessageReceived = true;}
bool isQuitMessageReceived();
set<int> touches;
typedef set<int>::iterator TIter;
};
extern Desktop desktop;
#endif // _DESKTOP_H_
#define _DESKTOP_H_
#include <set>
#include "s3eKeyboard.h"
#include "Scene.h"
using namespace std;
enum EMessageType {
emtNothing = 0x00,
emtTouchEvent = 0x10,
emtTouchIdMask = 0x07,
emtTouchMask = 0x70,
emtMultiTouch = 0x14,
emtTouchDown = 0x30,
emtTouchUp = 0x50,
emtTouchMove = 0x70,
emtSingleTouchDown = 0x30,
emtSingleTouchUp = 0x50,
emtSingleTouchMove = 0x70,
emtMultiTouchDown = 0x34,
emtMultiTouchUp = 0x54,
emtMultiTouchMove = 0x74,
emtKeyEvent = 0x80,
emtKeyAction = 0x82,
emtKeyDown = 0x81,
emtKeyPressed = 0x83,
emtKeyReleased = 0x82
};
class Desktop {
private:
int width, height;
bool isChanged;
Scene* currentScene;
bool isKeyAvailable;
bool IsQuitMessageReceived;
bool checkBounce(int id, int msg);
void getScreenSizes();
static int32 ScreenSizeChangeCallback(void* systemData, void* userData);
public:
Desktop(): touches() {}
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);
void sendQuitMessage() {IsQuitMessageReceived = true;}
bool isQuitMessageReceived();
set<int> touches;
typedef set<int>::iterator TIter;
};
extern Desktop desktop;
#endif // _DESKTOP_H_
Desktop.cpp:
#include "Desktop.h"
#include "Iw2D.h"
#include "TouchPad.h"
Desktop desktop;
void Desktop::init() {
IsQuitMessageReceived = false;
getScreenSizes();
setScene(NULL);
isKeyAvailable = (s3eKeyboardGetInt(S3E_KEYBOARD_HAS_KEYPAD)
#include "Iw2D.h"
#include "TouchPad.h"
Desktop desktop;
void Desktop::init() {
IsQuitMessageReceived = false;
getScreenSizes();
setScene(NULL);
isKeyAvailable = (s3eKeyboardGetInt(S3E_KEYBOARD_HAS_KEYPAD)
|| s3eKeyboardGetInt(S3E_KEYBOARD_HAS_ALPHA));
s3eSurfaceRegister(S3E_SURFACE_SCREENSIZE, ScreenSizeChangeCallback, NULL);
}
void Desktop::release() {
s3eSurfaceUnRegister(S3E_SURFACE_SCREENSIZE, ScreenSizeChangeCallback);
touches.clear();
}
int32 Desktop::ScreenSizeChangeCallback(void* systemData, void* userData) {
desktop.isChanged = true;
return 0;
}
void Desktop::setScene(Scene* scene) {
if (scene != NULL) {
scene->init();
}
currentScene = scene;
}
void Desktop::getScreenSizes() {
width = Iw2DGetSurfaceWidth();
height = Iw2DGetSurfaceHeight();
isChanged = false;
}
bool Desktop::checkBounce(int id, int msg) {
TIter p = touches.find(id);
if (TouchPad::isTouchDown(msg)) {
if (p != touches.end()) return true;
touches.insert(id);
} else {
if (p == touches.end()) return true;
if (TouchPad::isTouchUp(msg)) {
touches.erase(p);
}
}
return false;
}
void Desktop::update(uint64 timestamp) {
if (isChanged) {
getScreenSizes();
}
int cnt = touchPad.getTouchCount();
if (cnt > 0) {
for (int i = 0; i < MAX_TOUCHES; i++) {
Touch* t = touchPad.getTouch(i);
if (t->isActive) {
int msg = (cnt > 1)?emtMultiTouchUp:emtSingleTouchUp;
if (t->isMoved) {
msg = (cnt > 1)?emtMultiTouchMove:emtSingleTouchMove;
}
if (t->isPressed) {
msg = (cnt > 1)?emtMultiTouchDown:emtSingleTouchDown;
}
if (checkBounce(t->id, msg)) return;
if (currentScene != NULL) {
currentScene->sendMessage(msg | t->id, t->x, t->y);
}
}
}
}
if (isKeyAvailable) {
s3eKeyboardUpdate();
}
if (currentScene != NULL) {
currentScene->update(timestamp);
}
}
void Desktop::refresh() {
if (currentScene != NULL) {
currentScene->refresh();
}
}
bool Desktop::isQuitMessageReceived() {
if (s3eDeviceCheckQuitRequest()) {
return true;
}
return IsQuitMessageReceived;
}
s3eSurfaceRegister(S3E_SURFACE_SCREENSIZE, ScreenSizeChangeCallback, NULL);
}
void Desktop::release() {
s3eSurfaceUnRegister(S3E_SURFACE_SCREENSIZE, ScreenSizeChangeCallback);
touches.clear();
}
int32 Desktop::ScreenSizeChangeCallback(void* systemData, void* userData) {
desktop.isChanged = true;
return 0;
}
void Desktop::setScene(Scene* scene) {
if (scene != NULL) {
scene->init();
}
currentScene = scene;
}
void Desktop::getScreenSizes() {
width = Iw2DGetSurfaceWidth();
height = Iw2DGetSurfaceHeight();
isChanged = false;
}
bool Desktop::checkBounce(int id, int msg) {
TIter p = touches.find(id);
if (TouchPad::isTouchDown(msg)) {
if (p != touches.end()) return true;
touches.insert(id);
} else {
if (p == touches.end()) return true;
if (TouchPad::isTouchUp(msg)) {
touches.erase(p);
}
}
return false;
}
void Desktop::update(uint64 timestamp) {
if (isChanged) {
getScreenSizes();
}
int cnt = touchPad.getTouchCount();
if (cnt > 0) {
for (int i = 0; i < MAX_TOUCHES; i++) {
Touch* t = touchPad.getTouch(i);
if (t->isActive) {
int msg = (cnt > 1)?emtMultiTouchUp:emtSingleTouchUp;
if (t->isMoved) {
msg = (cnt > 1)?emtMultiTouchMove:emtSingleTouchMove;
}
if (t->isPressed) {
msg = (cnt > 1)?emtMultiTouchDown:emtSingleTouchDown;
}
if (checkBounce(t->id, msg)) return;
if (currentScene != NULL) {
currentScene->sendMessage(msg | t->id, t->x, t->y);
}
}
}
}
if (isKeyAvailable) {
s3eKeyboardUpdate();
}
if (currentScene != NULL) {
currentScene->update(timestamp);
}
}
void Desktop::refresh() {
if (currentScene != NULL) {
currentScene->refresh();
}
}
bool Desktop::isQuitMessageReceived() {
if (s3eDeviceCheckQuitRequest()) {
return true;
}
return IsQuitMessageReceived;
}
Здесь мы добавили перечисление EMessageType, определив коды системных событий и добавили обработку событий в метод update. Стоит обратить внимание на метод checkBounce, с помощью которого мы избавляемся от "дребезга" при обработке событий TouchPad-а. Также, не следует забывать о необходимости вызова s3eKeyboardUpdate, в том случае если клавиатура доступна в используемой нами аппаратной конфигурации. Помимо сказанного, в Desktop добавлен обработчик событий изменения размеров экрана (например в результате изменения его ориентации). Файл Main.cpp изменяется незначительно:
#include "Main.h"
#include "s3e.h"
#include "Iw2D.h"
#include "IwGx.h"
#include "TouchPad.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);
touchPad.init();
desktop.init();
}
void release() {
desktop.release();
touchPad.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 (!desktop.isQuitMessageReceived()) {
// Update keyboard system
s3eKeyboardUpdate();
// Update
touchPad.update();
desktop.update(s3eTimerGetMs());
// Clear the screen
IwGxClear(IW_GX_COLOUR_BUFFER_F | IW_GX_DEPTH_BUFFER_F);
touchPad.clear();
// Refresh
desktop.refresh();
// Show the surface
Iw2DSurfaceShow();
// Yield to the opearting system
s3eDeviceYield(duration);
}
}
release();
return 0;
}
#include "s3e.h"
#include "Iw2D.h"
#include "IwGx.h"
#include "TouchPad.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);
touchPad.init();
desktop.init();
}
void release() {
desktop.release();
touchPad.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 (!desktop.isQuitMessageReceived()) {
// Update keyboard system
s3eKeyboardUpdate();
// Update
touchPad.update();
desktop.update(s3eTimerGetMs());
// Clear the screen
IwGxClear(IW_GX_COLOUR_BUFFER_F | IW_GX_DEPTH_BUFFER_F);
touchPad.clear();
// Refresh
desktop.refresh();
// Show the surface
Iw2DSurfaceShow();
// Yield to the opearting system
s3eDeviceYield(duration);
}
}
release();
return 0;
}
Здесь добавлены вызовы инициализации и деинициализации TouchPad и изменена логика обработки завершения работы приложения. Мы убрали непосредственную проверку нажатия кнопки s3eKeyAbsBSK и заменили вызов s3eDeviceCheckQuitRequest в условии цикла на вызов метода Desktop::isQuitMessageReceived. Также в основной цикл приложения добавлены вызовы методов Touchpad::update и clear. Важную роль, в обработке событий клавиатуры, будет играть измененный класс Scene:
Scene.h:
#ifndef _SCENE_H_
#define _SCENE_H_
#include <map>
#include <set>
#include "s3eKeyboard.h"
#include "AbstractSpriteOwner.h"
#include "AbstractScreenObject.h"
using namespace std;
class Scene: public AbstractSpriteOwner {
private:
AbstractScreenObject* background;
map<s3eKey, int> keys;
bool isInitialized;
uint64 lastTime;
protected:
virtual bool doKeyMessage(int msg, s3eKey key) {return false;}
virtual void regKey(s3eKey key);
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);
typedef map<s3eKey, int>::iterator KIter;
typedef pair<s3eKey, int> KPair;
};
#endif // _SCENE_H_
#define _SCENE_H_
#include <map>
#include <set>
#include "s3eKeyboard.h"
#include "AbstractSpriteOwner.h"
#include "AbstractScreenObject.h"
using namespace std;
class Scene: public AbstractSpriteOwner {
private:
AbstractScreenObject* background;
map<s3eKey, int> keys;
bool isInitialized;
uint64 lastTime;
protected:
virtual bool doKeyMessage(int msg, s3eKey key) {return false;}
virtual void regKey(s3eKey key);
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);
typedef map<s3eKey, int>::iterator KIter;
typedef pair<s3eKey, int> KPair;
};
#endif // _SCENE_H_
Scene.cpp:
#include "Scene.h"
#include "Desktop.h"
Scene::Scene(): AbstractSpriteOwner()
, isInitialized(false)
, background(NULL)
, keys()
, lastTime(0) {
// regKey(s3eKeyLSK);
regKey(s3eKeyBack);
regKey(s3eKeyAbsBSK);
}
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::regKey(s3eKey key) {
keys.insert(KPair(key, 0));
}
void Scene::update(uint64 timestamp) {
for (KIter p = keys.begin(); p != keys.end(); ++p) {
int msg = 0;
int32 keyState = s3eKeyboardGetState(p->first);
if (keyState & (S3E_KEY_STATE_DOWN | S3E_KEY_STATE_PRESSED)) {
msg = emtKeyDown;
if (p->second == 0) {
msg = emtKeyPressed;
p->second = 1;
}
}
if (keyState == S3E_KEY_STATE_UP) {
if (p->second == 1) {
msg = emtKeyReleased;
p->second = 0;
}
}
if (msg != 0) {
if (doKeyMessage(msg, p->first)) {
lastTime = timestamp;
} else {
if (timestamp - lastTime >= 1000) {
lastTime = 0;
}
if ((lastTime == 0)&&(msg == emtKeyPressed)) {
switch (p->first) {
// case s3eKeyLSK:
case s3eKeyBack:
case s3eKeyAbsBSK:
desktop.sendQuitMessage();
break;
}
}
}
}
}
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)
, keys()
, lastTime(0) {
// regKey(s3eKeyLSK);
regKey(s3eKeyBack);
regKey(s3eKeyAbsBSK);
}
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::regKey(s3eKey key) {
keys.insert(KPair(key, 0));
}
void Scene::update(uint64 timestamp) {
for (KIter p = keys.begin(); p != keys.end(); ++p) {
int msg = 0;
int32 keyState = s3eKeyboardGetState(p->first);
if (keyState & (S3E_KEY_STATE_DOWN | S3E_KEY_STATE_PRESSED)) {
msg = emtKeyDown;
if (p->second == 0) {
msg = emtKeyPressed;
p->second = 1;
}
}
if (keyState == S3E_KEY_STATE_UP) {
if (p->second == 1) {
msg = emtKeyReleased;
p->second = 0;
}
}
if (msg != 0) {
if (doKeyMessage(msg, p->first)) {
lastTime = timestamp;
} else {
if (timestamp - lastTime >= 1000) {
lastTime = 0;
}
if ((lastTime == 0)&&(msg == emtKeyPressed)) {
switch (p->first) {
// case s3eKeyLSK:
case s3eKeyBack:
case s3eKeyAbsBSK:
desktop.sendQuitMessage();
break;
}
}
}
}
}
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;
}
Можно видеть, что метод update сильно раззросся по сравнению с предыдущей версией. Для каждой клавиши, занесенной в список прослушиваемых клавиш keys, мы проверяем состояние, формируем события клавиатуры и передаем переопределяемому методу doKeyMessage. Если этот метод не обрабатывает полученные события, мы выполняем обработку по умолчанию для s3eKeyBack и s3eKeyAbsBSK, заключающуюся в вызове метода desktop.sendQuitMessage, приводящего к завершению приложения. С целью защиты от "дребезга" мы не выполняем обработку по умолчанию в случае, если сцена успешно обработала клавиатурное событие менее 1 секунды назад.
Обработка s3eKeyLSK добавлена исключительно в отладочных целях. По неведомым мне причинам, разработчики Marmalade не предусмотрели в эмуляторе маппинг s3eKeyAbsBSK (либо я его не нашел). Возможно это будет исправлено в следующих версиях.
Для завершения статьи, нам осталось добавить обработку позиционных событий в класс 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;
int capturedId;
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);
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;
int capturedId;
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);
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"
#include "Desktop.h"
Sprite::Sprite(ISpriteOwner* owner, int x, int y , int zOrder):
#include "Locale.h"
#include "Desktop.h"
Sprite::Sprite(ISpriteOwner* owner, int x, int y , int zOrder):
AbstractScreenObject(x, y)
, owner(owner)
, capturedId(-1)
, img(NULL) {
owner->addSprite((AbstractScreenObject*)this, zOrder);
}
Sprite::Sprite(ISpriteOwner* owner, const char* res, int x, int y, int zOrder):
, owner(owner)
, capturedId(-1)
, 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)
, capturedId(-1)
, 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);
}
bool Sprite::sendMessage(int msg, int x, int y) {
if ((msg & emtTouchEvent) != emtTouchEvent) return false;
if (!isVisible) return false;
int id = msg & emtTouchIdMask;
msg &= emtTouchMask;
if (capturedId >= 0) {
if (id != capturedId) return false;
if (msg == emtTouchDown) {
capturedId = -1;
}
}
if (capturedId < 0) {
int X = owner->getXSize(owner->getXPos(getXPos()));
int Y = owner->getYSize(owner->getYPos(getYPos()));
if ((x < X)||(y < Y)) return false;
X += owner->getXSize(getWidth());
Y += owner->getYSize(getHeight());
if ((x > X)||(y > Y)) return false;
}
switch (msg) {
case emtTouchDown:
capturedId = id;
break;
case emtTouchUp:
capturedId = -1;
break;
}
if (isBuzy()) {
return true;
}
return sendMessage(msg) ||
owner->sendMessage(msg);
}
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)
, capturedId(-1)
, 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);
}
bool Sprite::sendMessage(int msg, int x, int y) {
if ((msg & emtTouchEvent) != emtTouchEvent) return false;
if (!isVisible) return false;
int id = msg & emtTouchIdMask;
msg &= emtTouchMask;
if (capturedId >= 0) {
if (id != capturedId) return false;
if (msg == emtTouchDown) {
capturedId = -1;
}
}
if (capturedId < 0) {
int X = owner->getXSize(owner->getXPos(getXPos()));
int Y = owner->getYSize(owner->getYPos(getYPos()));
if ((x < X)||(y < Y)) return false;
X += owner->getXSize(getWidth());
Y += owner->getYSize(getHeight());
if ((x > X)||(y > Y)) return false;
}
switch (msg) {
case emtTouchDown:
capturedId = id;
break;
case emtTouchUp:
capturedId = -1;
break;
}
if (isBuzy()) {
return true;
}
return sendMessage(msg) ||
owner->sendMessage(msg);
}
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())));
}
}
В обработчике позиционного события sendMessage мы проверяем попадает ли точка касания в регион спрайта и если да, передаем непозиционное событие с тем-же кодом спрайту. Напомню, что в AbstractSpriteOwner::update мы поочередно вызываем sendMessage для всех спрайтов кроме Background-а, в порядке обратном Z-order (используемому для отрисовки спрайтов).
}
}
В обработчике позиционного события sendMessage мы проверяем попадает ли точка касания в регион спрайта и если да, передаем непозиционное событие с тем-же кодом спрайту. Напомню, что в AbstractSpriteOwner::update мы поочередно вызываем sendMessage для всех спрайтов кроме Background-а, в порядке обратном Z-order (используемому для отрисовки спрайтов).
Для событий отпускания касания используется специальная обработка. Получив событие emtTouchDown. Sprite запоминает id-касания и передает последующие события с тем-же id в обработчик того-же спрайта, независимо от координат касания. Это позволяет корректно обработать следующий кейс:
1. Пользователь касается экранной кнопки
2. Пользовователь перемещает точку касания за пределы кнопки (не разрывая касания)
3. Пользователь разрывает касание за пределами экранной кнопки
В следующей статье, я планирую реализовать работу со звуком.
Исходный текст проекта можно загрузить здесь.
Комментариев нет:
Отправить комментарий