The previous article described how to use Action to move the objects. Now we’ll learn how to do more complicated stuff.
But first let’s create a task to implement.
We have to create a scene with two elements: a green UFO and a blue ship.
So, the green UFO will work like this:
The green UFO moves from side to side and rotates slightly
And the blue ship:
Blue ship behavior
I should remind you the images here came from free image sets: buttons and spaceships
The Action class has a special “tag” parameter. That’s an integer value used to distinguish one action from another. You can set this value with setTag method or sometimes provide as a parameter to constructor.
Also, the Node class has some methods for processing action tags:
void stopActionByTag(int tag) stops action with given tag (if there is any)Action* getActionByTag(int tag) returns a pointer to a running action with this tag. Note there may be few actions with the same tag running at the same time; I guess the behavior for this case is undefined.ssize_t getNumberOfRunningActionsByTag(int tag) const returns amount of running actions with given tagAnd there is a singleton instance of ActionManager with similar methods, but it’s not recommended using it.
In our example we’ll use tags to distinguish UFO rotation and horizontal movement. As for rotation, it’s implemented with the corresponding action and the 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);
Here AT_UFO_ROTATION is an integer constant used as tag value.
The UFO launch looks like this:
MoveBy* moveTo = MoveTo::create(time, Vec2(newX,240));
moveTo->setTag(AT_UFO_MOVING);
greenUfo->runAction(moveTo);
and stopping like this
greenUfo->stopAllActionsByTag(AT_UFO_MOVING);
Here stopAllActionsByTag stops the action marked by AT_UFO_MOVING tag, but the rotation continues.
In case of blue ship we’ll have to stop the whole sequence. There is not much difference from single action; just set tag to the sequence and not on the action.
// create some actions
///....
Sequence* seq = Sequence::create(rotateAct, moveAct, rotateBackAct, nullptr);
seq->setTag(AT_BS_MOVE);
blueShip->runAction(seq);
According to the task, it’s not allowed to interrupt the blue ship’s rotation. In order to do this we’ll have to check if the action is running at the moment.
There are two methods we can use for it : getNumberOfRunningActionsByTag() and getActionByTag().
The code will be almost same for both of them:
if (blueShip->getNumberOfRunningActionsByTag(AT_BS_ROTATION_BACK)>0 ) {
log("%s: rotating back, do not interfere with moving", __func__);
return;
}
or
if (blueShip->getActionByTag(AT_BS_ROTATION_BACK) != nullptr) {
log("%s: rotating back, do not interfere", __func__);
return;
}
The task says the image on arrow button must be performed right after the ship reaches its destination. We can do it with an instance of CallFunc class. This action calls the function provided to the constructor.
So, we can create a lambda-expression, and provide it to the CallFunc‘s constructor:
CallFunc* finalCf = CallFunc::create([this]() {
// create and launch action to rotate the ship back
RotateTo* ra2 = RotateTo::create(3, 0);
ra2->setTag(AT_BS_ROTATION_BACK);
this->blueShip->runAction(ra2);
// change image on the button
// ......
});
Sequence* seq = Sequence::create(rotateAct, moveAct, finalCf, nullptr);
seq->setTag(AT_BS_MOVE);
blueShip->runAction(seq);
As you already know, the Sequence constructor receives all the actions and as parameters and the nullptr at the end. Here we perform three actions: rotation, movement and callback. Then callback will add one more action to rotate the ship back.