Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A camera shake effect for SpriteKit

Does anyone know some out of the box libraries that might provide effects such as camera shake to an SKNode?

If not is there an easy way to implement a camera shake using actions?

Thanks

like image 567
Krzemienski Avatar asked Jan 02 '14 18:01

Krzemienski


2 Answers

I found an elegant method using SKAction, that make shake your node. For example, with an horizontal shake:

-(void)shake:(NSInteger)times {
    CGPoint initialPoint = self.position;
    NSInteger amplitudeX = 32;
    NSInteger amplitudeY = 2;
    NSMutableArray * randomActions = [NSMutableArray array];
    for (int i=0; i<times; i++) {
        NSInteger randX = self.position.x+arc4random() % amplitudeX - amplitudeX/2;
        NSInteger randY = self.position.y+arc4random() % amplitudeY - amplitudeY/2;
        SKAction *action = [SKAction moveTo:CGPointMake(randX, randY) duration:0.01];
        [randomActions addObject:action];
    }

    SKAction *rep = [SKAction sequence:randomActions];

    [self runAction:rep completion:^{
        self.position = initialPoint;
    }];
}
like image 75
Martin Avatar answered Nov 11 '22 21:11

Martin


The shake method in the accepted answer has a caveat - it can only be applied to nodes that do not have their x,y coordinates being modified by other running SKActions. This is because we reset the node's position to its initial position after the shake completes.

If you want a shake action that works also with other SKAction's that modify the shaking node's position (x or y coordinates) in sequence/parallel, you need to add a reverse action of the random shakes you are applying.

A Swift extension for reference:

extension SKAction {
class func shake(duration:CGFloat, amplitudeX:Int = 3, amplitudeY:Int = 3) -> SKAction {
    let numberOfShakes = duration / 0.015 / 2.0
    var actionsArray:[SKAction] = []
    for index in 1...Int(numberOfShakes) {
        let dx = CGFloat(arc4random_uniform(UInt32(amplitudeX))) - CGFloat(amplitudeX / 2)
        let dy = CGFloat(arc4random_uniform(UInt32(amplitudeY))) - CGFloat(amplitudeY / 2)
        let forward = SKAction.moveByX(dx, y:dy, duration: 0.015)
        let reverse = forward.reversedAction()
        actionsArray.append(forward)
        actionsArray.append(reverse)
    }
    return SKAction.sequence(actionsArray)
}
}

Note that this modification does not require the node to be passed in as an argument

like image 14
Benzi Avatar answered Nov 11 '22 23:11

Benzi