У попередній статті було показано, як за допомогою Action можна ініціювати рух та інші перетворення об'єктів. Зараз ми продовжимо роботу цьому напрямі, розглядаючи складніші сценарії.
Для початку сформулюємо завдання, яке має бути реалізоване.
Нехай потрібно створити форму, на якій буде два спрайти, вже знайомі нам зелене НЛО та синій корабель.
Таким чином, зелене НЛО буде поводити себе так:
Зелене НЛО рухається в сторони і трохи обертається
А синій корабель так:
Рух та зупинка синього корабля
Нагадаю, що зображення для прикладів взяті безкоштовних наборів: кнопки та космічні кораблі
У класу Action є параметр tag — просте число, яке за яким можна відрізнити одну акцію від іншої. Тег встановлюється методом setTag або іноді передається у конструкторі акції.
Відповідно, у Node є ряд методів для роботи з акціями:
void stopActionByTag(int tag) — зупинка акції з відповідним тегомAction* getActionByTag(int tag) — отримання вказівника на акціюssize_t getNumberOfRunningActionsByTag(int tag) const — кількість діючих акційКрім того, існує синглтон 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 у кінці. В даному випадку ми виконуємо три дії: поворот, рух та обробку завершення.