Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CABasicAnimation to make NSView flip

I'm making a card game for mac and I'm using a CABasicAnimation to making the card flip around. It's almost working, but it could be a bit better.

As it works now, the card flips inwards (to the left) - Screenshot 1. When the card has moved "flipped" all the way to the left, I change the image of the NSView and flip the card outwards again - Screenshot 2.

Screenshot 1 (flipping in):

Screenshot 1

Screenshot 2 (flipping out):

Screenshot 2

Code for flipping in:

- (void)flipAnimationInwards{
    // Animate shadow
    NSShadow *dropShadow = [[NSShadow alloc] init];
    [dropShadow setShadowOffset:NSMakeSize(0, 1)];
    [dropShadow setShadowBlurRadius:15];
    [dropShadow setShadowColor:[NSColor colorWithCalibratedWhite:0.0 alpha:0.5]];
    [[self animator] setShadow:dropShadow];

    // Create CAAnimation
    CABasicAnimation* rotationAnimation;
    rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
    rotationAnimation.fromValue = [NSNumber numberWithFloat: 0.0];
    rotationAnimation.toValue = [NSNumber numberWithFloat: M_PI/2];
    rotationAnimation.duration = 3.1;
    rotationAnimation.repeatCount = 1.0; 
    rotationAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
    rotationAnimation.fillMode = kCAFillModeForwards;
    rotationAnimation.removedOnCompletion = NO;
    [rotationAnimation setValue:@"flipAnimationInwards" forKey:@"flip"];
    rotationAnimation.delegate = self;

    // Get the layer
    CALayer* lr = [self layer];

    // Add perspective
    CATransform3D mt = CATransform3DIdentity;
    mt.m34 = 1.0/-1000;
    lr.transform = mt;

    // Set z position so the layer will be on top
    lr.zPosition = 999;

    // Keep cards tilted when flipping
    if(self.tiltCard)
        self.frameCenterRotation = self.frameCenterRotation;

    // Do rotation
    [lr addAnimation:rotationAnimation forKey:@"flip"];
}

The code for flipping out:

- (void)flipAnimationOutwards{
    // Set correct image
    if (self.faceUp){
        [self setImage:self.faceImage];
    }else{
        [self setImage:[NSImage imageNamed:@"Card_Background"]];
    }

    // Animate shadow
    NSShadow *dropShadow = [[NSShadow alloc] init];
    [dropShadow setShadowOffset:NSMakeSize(0, 1)];
    [dropShadow setShadowBlurRadius:0];
    [dropShadow setShadowColor:[NSColor colorWithCalibratedWhite:0.0 alpha:0.0]];
    [[self animator] setShadow:dropShadow];

    // Create CAAnimation
    CABasicAnimation* rotationAnimation;
    rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
    rotationAnimation.fromValue = [NSNumber numberWithFloat: M_PI/2];
    rotationAnimation.toValue = [NSNumber numberWithFloat: 0.0];
    rotationAnimation.duration = 3.1;
    rotationAnimation.repeatCount = 1.0; 
    rotationAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
    rotationAnimation.fillMode = kCAFillModeForwards;
    rotationAnimation.removedOnCompletion = YES;
    [rotationAnimation setValue:@"flipAnimationOutwards" forKey:@"flip"];
    rotationAnimation.delegate = self;

    // Get the layer
    CALayer* lr = [self layer];

    // Add perspective
    CATransform3D mt = CATransform3DIdentity;
    mt.m34 = 1.0/1000;
    lr.transform = mt;

    // Set z position so the layer will be on top
    lr.zPosition = 999;

    // Keep cards tilted when flipping
    if(self.tiltCard)
        self.frameCenterRotation = self.frameCenterRotation;

    // Commit animation
    [lr addAnimation:rotationAnimation forKey:@"flip"];
}

The problem:

The flipping out part looks fine. The right side of the card is taller/stretched than the left side, like it's supposed to be.

Flipping in is not perfect though. Here the right side is smaller/stretched, when it should be the left side that is taller/stretched.

How do I make the left side taller/stretched on flipping in, instead of making the right side smaller/stretched?

like image 738
Holger Sindbaek Avatar asked Apr 07 '14 09:04

Holger Sindbaek


1 Answers

You ask:

How do I make the left side taller/stretched on flipping in, instead of making the right side smaller/stretched?

You also say that flipping out works fine but flipping in is wrong.

The difference between the two is in the sign of the perspective:

Flipping out code:

CATransform3D mt = CATransform3DIdentity;
mt.m34 = 1.0/1000; // note the lack of a minus sign
lr.transform = mt;

Flipping in code:

CATransform3D mt = CATransform3DIdentity;
mt.m34 = 1.0/-1000; // note the minus sign
lr.transform = mt;

If you want the two to look the same then they should most likely have the same perspective.


In my experience you usually want the negative perspective value (as you have done in the flipping in example). This has to do with the fact that the value represents the position of the "eye" / "camera" / "observer" or whatever you call it.

If you imagine a 3D scene where the position of the eye is (ex, ey, ez) then the perspective part of the transform is:

perspective transform

Assuming that you are looking at right at the world (i.e. not looking at it from the side) the position would be (0, 0, ez) which is the reason why we usually only set m34 (3rd column, 4th row) when adding perspective to a transform.

You can also see that this is how it is used in the Core Animation Programming Guide:

Listing 5-8 Adding a perspective transform to a parent layer

CATransform3D perspective = CATransform3DIdentity;
perspective.m34 = -1.0/eyePosition;

If the rotation looks wrong to you should probably rotate in the other direction (for example changing a rotation from 0 to π into a rotation from 0 to -π or the other way around: changing a rotation from π to 0 into a rotation from -π to 0.

like image 156
David Rönnqvist Avatar answered Oct 13 '22 03:10

David Rönnqvist