Основи Cocos2d-x

Статті про розробку ігор за допомогою Cocos2d-x

Складніша робота з об'єктами

Posted at — Apr 2, 2020

У попередній статті було показано, як за допомогою Action можна ініціювати рух та інші перетворення об'єктів. Зараз ми продовжимо роботу цьому напрямі, розглядаючи складніші сценарії.

Завдання

Для початку сформулюємо завдання, яке має бути реалізоване.

Нехай потрібно створити форму, на якій буде два спрайти, вже знайомі нам зелене НЛО та синій корабель.

  1. Зелене НЛО має весь час злегка коливатись (обертатись) навколо власного центру
  2. на сцені на рівні НЛО мають бути дві кнопки: права та ліва стрілка. При натисненні НЛО починає рухатись у відповідному напрямку, поки не дійде до крайньої позиції
  3. якщо під час руху натиснули іншу кнопку, рух у поточному напрямі припиняється, починається у протилежному.
  4. Синій корабель з'являється у лівій частині екану носом доверху. У правій знаходиться кнопка-стрілка.
  5. при натисненні на стрілку корабель повертається і починає рух.
  6. коли корабель доходить до місця призначення, він повертається носом доверху; після цього кнопка-стрілка змінюється на протилежну.
  7. якщо знову натиснути на кнопку, корабель почне рухатись у іншому напрямі.

Таким чином, зелене НЛО буде поводити себе так:

Зелене НЛО рухається в сторони і трохи обертається

А синій корабель так:

Рух та зупинка синього корабля

Нагадаю, що зображення для прикладів взяті безкоштовних наборів: кнопки та космічні кораблі

Теги для Action

У класу Action є параметр tag — просте число, яке за яким можна відрізнити одну акцію від іншої. Тег встановлюється методом setTag або іноді передається у конструкторі акції.

Відповідно, у Node є ряд методів для роботи з акціями:

Крім того, існує синглтон ActionManager зі схожими методами, але у документації не рекомендують ним користуватись.

У нашому прикладі теги знадобляться, щоб відділити обертання НЛО від його горизонтального руху. Перший пункт для зеленого НЛО реалізується за допомогою послідовності та акції RepeatForever якось так:

Sequence* rseq = Sequence::create(RotateBy::create(3, 30),
                                  RotateBy::create(3, -30), nullptr);

RepeatForever* reps = RepeatForever::create(rseq);
reps->setTag(AT_UFO_ROTATION);
greenUfo->runAction(reps);

Тут AT_UFO_ROTATION — це числова константа і тег, що позначає акцію обертання.

Запуск руху НЛО в сторону виглядає приблизно так:

MoveBy* moveTo = MoveTo::create(time, Vec2(newX,240));
moveTo->setTag(AT_UFO_MOVING);
greenUfo->runAction(moveTo);

а зупинка ось так

greenUfo->stopAllActionsByTag(AT_UFO_MOVING);

Тут stopAllActionsByTag зупиняє акцію, позначену константою-тегом AT_UFO_MOVING, натомість акція обертання буде продовжуватись.

У випадку синього корабля доведеться зупиняти послідовність. Це не відрізняється від одної акції, просто тег треба ставити саме на послідовність, а не на окремі акції, приблизно так:

// створюємо ряд акцій для послідовності
///....

Sequence* seq = Sequence::create(rotateAct, moveAct, rotateBackAct, nullptr);
seq->setTag(AT_BS_MOVE);

blueShip->runAction(seq);

Визначення акції

За логікою завдання для синього корабля виходить, що його рух не можна переривати у той час, коли він обертається назад під час зупинки. Для того, щоб це реалізувати, треба у колбеках перевіряти, чи не відбувається зараз ця акція.

Є два методи, які можуть показати, що певна акція триває у даний час: це методи getNumberOfRunningActionsByTag() та getActionByTag().

Застосовуються вони однаково:

if (blueShip->getNumberOfRunningActionsByTag(AT_BS_ROTATION_BACK)>0 ) {
  log("%s: rotating back, do not interfere with moving", __func__);
  return;
}

або

if (blueShip->getActionByTag(AT_BS_ROTATION_BACK) != nullptr) {
  log("%s: rotating back, do not interfere", __func__);
  return;
}

Обробка завершення акції

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

Можна зробити лямбда-вираз, яка виконає необхідні дії при завершенні послідовності:

CallFunc* finalCf = CallFunc::create([this]() {
  // запуск акції по зворотньому повороту корабля
  RotateTo* ra2 = RotateTo::create(3, 0);
  ra2->setTag(AT_BS_ROTATION_BACK);
  this->blueShip->runAction(ra2);

  // заміна зображень на правій кнопці
  // ......
});

Sequence* seq = Sequence::create(rotateAct, moveAct, finalCf, nullptr);
seq->setTag(AT_BS_MOVE);

blueShip->runAction(seq);

Нагадаю, що конструктор Sequence отримує акції, які треба об'єднати у послідовність, а також nullptr у кінці. В даному випадку ми виконуємо три дії: поворот, рух та обробку завершення.