Обробка подій та управління об'єктами у Cocos2d-x

7 хв. читання

Для початку давайте повернемось до дефолної програми, яку ми створили під час знайомства з фреймворком. Там залишився без пояснення один момент — кнопка, за допомогою якої ця програма закривалась.

Реагування на події

Сама програма, якщо пам'ятаєте, була такою:

Обробка подій та управління об'єктами у Cocos2d-x
дефолтна програма

Якщо ви мишкою натискали на кнопку в правому нижньому куті, спрацьовував ось такий метод, який припиняв роботу програми:

void SmokeTestScene::menuCloseCallback(Ref *pSender) {
  // Close the cocos2d-x game scene and quit the application
  Director::getInstance()->end();
}

Але чому цей колбек взагалі викликається? Вся кнопка створюється ось так:

/////////////////////////////
// 2. add a menu item with "X" image, which is clicked to quit the program
//    you may modify it.

// add a "close" icon to exit the progress. it's an autorelease object
auto closeItem = MenuItemImage::create(
  "CloseNormal.png",
  "CloseSelected.png",
  CC_CALLBACK_1(SmokeTestScene::menuCloseCallback, this));

if ((closeItem == nullptr) ||
    (closeItem->getContentSize().width <= 0) ||
    (closeItem->getContentSize().height <= 0)) {
  problemLoading("'CloseNormal.png' and 'CloseSelected.png'");
}
else {
  float x = origin.x + visibleSize.width - closeItem->getContentSize().width / 2;
  float y = origin.y + closeItem->getContentSize().height / 2;
  closeItem->setPosition(Vec2(x, y));
}


// create menu, it's an autorelease object
auto menu = Menu::create(closeItem, NULL);
menu->setPosition(Vec2::ZERO);
this->addChild(menu, 1);

Більшість цього коду нагадує конструювання складного спрайту, яке ми вже розглядали раніше. Але є один новий момент: використання макросу CC_CALLBACK_1 для того, щоб прив'язати до об'єкта меню метод, котрий буде обробляти колбек.

Макроси CC_CALLBACK_* у Cocos2d трапляються дуже часто, а реалізовані вони за допомогою магії C++11, приблизно так:

// new callbacks based on C++11
#define CC_CALLBACK_0(__selector__,__target__, ...) std::bind(&__selector__,__target__, ##__VA_ARGS__)
#define CC_CALLBACK_1(__selector__,__target__, ...) std::bind(&__selector__,__target__, std::placeholders::_1, ##__VA_ARGS__)
#define CC_CALLBACK_2(__selector__,__target__, ...) std::bind(&__selector__,__target__, std::placeholders::_1, std::placeholders::_2, ##__VA_ARGS__)
#define CC_CALLBACK_3(__selector__,__target__, ...) std::bind(&__selector__,__target__, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, ##__VA_ARGS__)

Функція MenuItemImage::create оголошена у такий спосіб:

static MenuItemImage * 	create (const std::string &normalImage, const std::string &selectedImage, const std::string &disabledImage, const ccMenuCallback &callback)

де ccMenuCallback є просто короткою формою для std::function:

typedef std::function<void(Ref*)> ccMenuCallback;

Тож макрос CC_CALBACK_* отримує два параметри:

  • функцію (або метод), який треба буде викликати;
  • об'єкт, якому належить цей метод.

Число у макросі CC_CALBACK_* означає кількість аргументів, які отримає функція-колбек.

Результатом є std::function, створений std::bind. Об'єкт Cocos2d, який откримав такий колбек, збереже його і викличе, коли буде потрібно.

Клавіатура

Ще одним прикладом використання макросу CC_CALLBACK може бути робота з клавіатурою.

Щоб додати у програму можливість обробки клавіатури, в init сцени треба додати такий код:

// keyboard processing
EventListenerKeyboard* sceneKeyboardListener = EventListenerKeyboard::create();
sceneKeyboardListener->onKeyPressed = CC_CALLBACK_2(ActionsDemoScene::onKeyPressedScene, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(sceneKeyboardListener, this);

При цьому сама обробка натискання може мати такий вигляд:

void ActionsDemoScene::onKeyPressedScene(EventKeyboard::KeyCode keyCode,
                                         Event                 *event) {
  printf("%s: processing key %d pressed\
", __func__, (int)keyCode);

  if (EventKeyboard::KeyCode::KEY_A == keyCode) {
    printf("%s: key 'a' was pressed\
", __func__);
  }
  else {
    printf("%s: failed to process.\
", __func__);
  }
}

Тобто спочатку створюється об'єкт класу EventListenerKeyboard. Це такий маленький клас, що об'єднує кілька вказівників на функції: onKeyPressed та onKeyPressed. Для наших потреб досить буде встановити лише один з них. Ми це робимо через макрос CC_CALLBACK_2, тому що метод, який оброблятиме натискання, отримує два параметри.

Потім ми реєструємо новостворений об'єкт у глобальному EventDispatcher, який у Cocos2d відповідає за обробку подій.

Теоретично, обробник колбеків зовсім не обов'язково має бути методом сцени. Можна було б реалізувати цю функціональність у якому-небудь іншому класі. Просто це досить зручно, тому що у невеликій програмі саме сцена має доступ до всіх об'єктів, що на ній розташовані.

Action

Тепер, коли ми вже навчились обробляти події, можна спробувати щось зробити з об'єктом у якості реакції.

Наприклад, ви хочете, щоб після натиснення кнопки спрайт перемістився з одного місця екрана до іншого. Щоб це було схоже саме на пересування, а не телепортацію, координати треба змінювати дуже маленькими й частими кроками, десятки разів на секунду. Вручну це програмувати незручно, тому в Cococ2d зробили клас Action та його нащадків. Зокрема, для пересування призначений MoveTo, який використовується так:

void ActionsDemoScene::sideButtonCallback(Ref *pSender) {
  MoveTo* moveTo = MoveTo::create(2, Vec2(320,240));
  greenUfo->runAction(moveTo);
}

Тут greenUfo — спрайт, розташований на сцені.

moveBy — це акція, що рухатиме спрайт. Фабричний метод MoveTo::create отримує два параметри:

  • час у секундах, протягом якого буде виконуватись перетворення;
  • Vec2 — масив з двох чисел, які є координатами нового положення об'єкта.

В результаті зелене НЛО почне рухатись після натиснення кнопки, через дві секунди воно опиниться у новій точці. Приблизно так:

Обробка подій та управління об'єктами у Cocos2d-x

Cocos2d може виконувати дуже багато різних перетворень:

Більшість перетворень має два варіанти:

  • «To» отримує чіткі параметри, які описують новий стан об'єкта (координати, кут повороту тощо);
  • «By» отримує величину, на яку треба змінити поточні параметри, щоб отримати новий стан.

Апроксимація

Протягом акції параметри змінюються рівномірно — так налаштовано автоматично, але це можна змінити. Наприклад, можна повільно змінювати значення на початку часового інтервалу і швидко у кінці — результат буде схожим на зрушення масивного тіла.

Реалізується це за допомогою класів Easing* — їх багато на будь-який смак.

Використовуються вони так:

MoveBy* bma = MoveBy::create(6, Vec2(360,0));
EaseIn* bea = EaseIn::create(bma, 1.5f);
blueShip->runAction(bea);

Другий параметр конструктора EaseIn — це множник, з яким будуть змінюватись параметри. Чим він більший, тим помітнішими будуть зміни.

Результат модифікаторів EaseIn та EaseOut:

Обробка подій та управління об'єктами у Cocos2d-x

Зелений корабель рухається стандартно, синій та червоний — з модифікаціями. У підсумку дистанцію вони проходять за один і той самий час, але по-різному.

Послідовності

Якщо вам потрібно виконати кілька акцій одну за одною, на допомогу прийде клас Sequence.

Наприклад, синій корабель автоматично створюється «носом догори». Щоб зобразити його «переліт» направо, спочатку треба зробити поворот на 90 градусів, а вже потім переміщувати.

Щоб це спрацювало, треба створити акції RotateTo та MoveTo, а потім об'єднати їх у послідовність:

RotateBy* rotateBy = RotateBy::create(2, 90);
MoveBy* moveBy = MoveBy::create(4, Vec2(160,0));

Sequence* seq = Sequence::create(rotateBy, moveBy, nullptr);

blueShip->runAction(seq);

Фабричний метод, який створює Sequence, може отримувати будь-яке число вказівників на об'єкти-нащадки Action, а останнім обов'язково має бути nullptr. Sequence виконає акції одну за одною, приблизно так:

Обробка подій та управління об'єктами у Cocos2d-x

Також у Cocos2d-x є кілька «службових» акцій, які часто використовуються у побудові послідовностей:

  • DelayTime просто чекає певний час;
  • Repeat та RepeatForever повторюють задану акцію чи послідовність;
  • Spawn запускає кілька акцій одночасно;
  • RemoveSelf видаляє об'єкт, щодо якого виконується.

Більше про такі акції та послідовності буде у наступній статті, а поки що на цьому все, дякую за увагу.

Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
Codeguida 6.2K
Приєднався: 7 місяців тому
Коментарі (0)

    Ще немає коментарів

Щоб залишити коментар необхідно авторизуватися.

Вхід / Реєстрація