Clone
Clone is exactly like it sounds. If you have an Action
, you can apply it to
multiple Node
objects by using clone()
. Why do you have to clone? Good question.
Action
objects have an internal state. When they run, they are actually
changing the Node
objects properties. Without the use of clone()
you don't
truly have a unique Action
being applied to the Node
. This will produce
unexpected results, as you can't know for sure what the properties of the Action
are currently set at.
Let's hash through an example, say you have a heroSprite and it has a position
of (0,0). If you run an Action
of:
MoveBy::create(10, Vec2(400,100));
This will move heroSprite from (0,0) to (400, 100) over the course of
10 seconds. heroSprite now has a new position of (400, 100) and more
importantly the Action
has this position in it's internal state. Now, say
you have an enemySprite with a position of (200, 200). If you were to apply
this same:
MoveBy::create(10, Vec2(400,100));
to your enemySprite, it would end up at a position of (800, 200) and not
where you thought it would. Do you see why? It is because the Action
already
had an internal state to start from when performing the MoveBy
. Cloning
an Action
prevents this. It ensures you get a unique version Action
applied
to your Node
.
Let's also see this in code, first, incorrect.
// create our Sprites
auto heroSprite = Sprite::create("herosprite.png");
auto enemySprite = Sprite::create("enemysprite.png");
// create an Action
auto moveBy = MoveBy::create(10, Vec2(400,100));
// run it on our hero
heroSprite->runAction(moveBy);
// run it on our enemy
enemySprite->runAction(moveBy); // oops, this will not be unique!
// uses the Actions current internal state as a starting point.
Correctly, using clone()!:
// create our Sprites
auto heroSprite = Sprite::create("herosprite.png");
auto enemySprite = Sprite::create("enemysprite.png");
// create an Action
auto moveBy = MoveBy::create(10, Vec2(400,100));
// run it on our hero
heroSprite->runAction(moveBy);
// run it on our enemy
enemySprite->runAction(moveBy->clone()); // correct! This will be unique
Reverse
Reverse is also exactly like it sounds. If you run a series of actions, you
can call reverse()
to run it, in the opposite order. Otherwise known as, backwards.
However, it is not just simply running the Action
in reverse order. Calling
reverse()
is actually manipulating the properties of the original Sequence
or
Spawn
in reverse too.
Using the Spawn
example above, reversing is simple.
// reverse a sequence, spawn or action
mySprite->runAction(mySpawn->reverse());
Most Action
and Sequence
objects are reversible!
It's easy to use, but let's make sure we see what is happening. Given:
// create a Sprite
auto mySprite = Sprite::create("mysprite.png");
mySprite->setPosition(50, 56);
// create a few Actions
auto moveBy = MoveBy::create(2.0f, Vec2(500,0));
auto scaleBy = ScaleBy::create(2.0f, 2.0f);
auto delay = DelayTime::create(2.0f);
// create a sequence
auto delaySequence = Sequence::create(delay, delay->clone(), delay->clone(),
delay->clone(), nullptr);
auto sequence = Sequence::create(moveBy, delay, scaleBy, delaySequence, nullptr);
// run it
newSprite2->runAction(sequence);
// reverse it
newSprite2->runAction(sequence->reverse());
What is really happening? If we lay out the steps as a list it might be helpful:
- mySprite is created
- mySprite position is set to (50, 56)
- sequence starts to run
- sequence moves mySprite by 500, over 2 seconds, mySprite new position (550, 56)
- sequence delays for 2 seconds
- sequence scales mySprite by 2x over 2 seconds
- sequence delays for 6 more seconds (notice we run another sequence to accomplish this)
- we run a reverse() on the sequence so we re-run each action backwards
- sequence is delayed for 6 seconds
- sequence scales mySprite by -2x over 2 seconds
- sequence delays for 2 seconds
- sequence moves mySprite by -500, over 2 seconds, mySprite new position (50, 56)
You can see that a reverse()
is simple for you to use, but not so simple in
its internal logic. Cocos2d-x does all the heavy lifting!