Getting a nice working card flipping effect has been more difficult than I expected.
Here’s the basic description of the process that can be used to obtain a card flip effect:
- Animate the card sprite for half the rotation.
- Swap the texture.
- Animate the second half of the rotation
My initial implementation was using the
CCOrbitCamera action, which worked beautifully and was perfect. And then I moved the sprite I was animating off the center of the screen and what do you know? The effect was no longer working as one would expect. When the sprite is rotated 90 degrees on the X axis you would expect that it would become invisible, but in fact, due to the field of view of the camera observing the scene a 90 degrees rotation will expose more and more of the sprite as you distance yourself from the center of the screen.
To check this just create an empty project and set the pSprite position in the HelloWorldScene file at the following position:
pSprite->setPosition(ccp(visibleSize.width/2 + origin.x + 100, visibleSize.height/2 + origin.y));
Then, run the flipping animation, preferably after adding the sprite to the scene layer.
pSprite->runAction(CCOrbitCamera::create(2, 1, 0, 0, 90, 0, 0));
Not exactly what would be preferable.
An alternative would be to change the projection mode of the CCDirector. You would do this by calling during the
init() of the scene:
But now the effect looks, like you would expect, very flat and the near side of the sprite is no longer enlarged when the card is spinning on its axis.
What’s left then? Faking it, of course.
To fake a 3D flipping effect we would use a combination of two effects: scaling the sprite on the X axis, therefore reducing its width, and applying a skew effect so we can simulate at least a part of the near-side of the sprite exiting the view plane towards us. For this you’ll need to extend CCSprite and create your own sprite. In it, place a
runFlipAnimation() method that you’ll call when you’ll want to flip the sprite.
#define kCardFlipTime 2.0 // reduce the width to zero and then expand back to initial width CCActionInterval *action = CCScaleTo::create(kCardFlipTime / 2, 0, getScaleY()); CCActionInterval *actionReverse = CCScaleTo::create(kCardFlipTime / 2, getScaleX(), getScaleY()); this->runAction(CCSequence::create(action, CCCallFunc::create(this, callfunc_selector(CardSprite::updateTexture)), actionReverse, NULL)); // simulate the rotational 3d effect by playing with the skew float skewAmount = 12; CCActionInterval *firstSkew = CCSkewBy::create(kCardFlipTime / 2, 0, skewAmount); // flip the skew value after the initial half of the animation for a more credible 3d effect CCActionInterval *instantFlipSkew = CCSkewTo::create(0, 0, -skewAmount); CCActionInterval *resetSkew = CCSkewTo::create(kCardFlipTime / 2, 0, 0); this->runAction(CCSequence::create(firstSkew, instantFlipSkew, resetSkew, NULL));
CardSprite::updateTexture() implementation I just call a setTexture on the current sprite with the back or the front of the card, depending on the direction of the rotation.
Sure, it’s not quite as pretty as the
CCOrbitCamera effect, but I think it’s safe to say it’s a second best.