Эффект дрожания камеры для SpriteKit

Кто-нибудь знает некоторые из библиотек ящиков, которые могут обеспечить такие эффекты, как сотрясение камеры до SKNode?

Если нет, то есть простой способ реализовать дрожание камеры с помощью действий?

Спасибо

Ответ 1

Я нашел элегантный метод, использующий SKAction, который встряхнет ваш node. Например, при горизонтальном дрожании:

-(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;
    }];
}

Ответ 2

Метод встряски в принятом ответе имеет caveat - он может применяться только к узлам, у которых их координаты x, y не изменяются другими работающими SKActions. Это связано с тем, что мы reset позиционируем node в исходное положение после завершения тряски.

Если вы хотите, чтобы действие трясти, которое также работает с другим SKAction, которое изменяет встряхивающую позицию node (координаты x или y) последовательно/параллельно, вам нужно добавить обратное действие случайных трясков, которые вы применяете.

Быстрое расширение для ссылки:

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)
}
}

Обратите внимание, что эта модификация не требует передачи node в качестве аргумента

Ответ 3

Если вы уже используете Swift, вы можете использовать следующий код...

let shake = SKAction.shake(node.position, duration: 0.3)
node.runAction(shake)

или для более интенсивного встряска вы также можете настроить амплитуду

let shake = SKAction.shake(node.position, duration: 0.3, amplitudeX: 12, amplitudeY: 3)
node.runAction(shake)

здесь есть расширение....

extension SKAction {
    class func shake(initialPosition:CGPoint, duration:Float, amplitudeX:Int = 12, amplitudeY:Int = 3) -> SKAction {
        let startingX = initialPosition.x
        let startingY = initialPosition.y
        let numberOfShakes = duration / 0.015
        var actionsArray:[SKAction] = []
        for index in 1...Int(numberOfShakes) {
            let newXPos = startingX + CGFloat(arc4random_uniform(UInt32(amplitudeX))) - CGFloat(amplitudeX / 2)
            let newYPos = startingY + CGFloat(arc4random_uniform(UInt32(amplitudeY))) - CGFloat(amplitudeY / 2)
            actionsArray.append(SKAction.moveTo(CGPointMake(newXPos, newYPos), duration: 0.015))
        }
        actionsArray.append(SKAction.moveTo(initialPosition, duration: 0.015))
        return SKAction.sequence(actionsArray)
    }
}

кредиты идут на Стивен Хейни для реализации, я только переместил его в расширение

Ответ 4

Здесь используется метод. Надеюсь, поможет. Измените его по своему усмотрению.

-(void)shakeScreenFor:(int)numberOfShakes withIntensity:(CGVector)intensity andDuration:(CGFloat)duration{
    UIView *sceneView = self.scene.view;
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
    [animation setDuration:(duration/numberOfShakes)];
    [animation setRepeatCount:numberOfShakes];
    [animation setAutoreverses:YES];
    [animation setFromValue:[NSValue valueWithCGPoint: CGPointMake([sceneView center].x - intensity.dx, [sceneView center].y - intensity.dy)]];
    [animation setToValue:[NSValue valueWithCGPoint: CGPointMake([sceneView center].x + intensity.dx, [sceneView center].y + intensity.dy)]];
    [[sceneView layer] addAnimation:animation forKey:@"position"];
}

Ответ 5

Если вы хотите поколебать всю сцену, вот версия ответа Роба, написанная в Swift:

func sceneShake(shakeCount: Int, intensity: CGVector, shakeDuration: Double) {
    let sceneView = self.scene!.view! as UIView
    let shakeAnimation = CABasicAnimation(keyPath: "position")
    shakeAnimation.duration = shakeDuration / Double(shakeCount)
    shakeAnimation.repeatCount = Float(shakeCount)
    shakeAnimation.autoreverses = true
    shakeAnimation.fromValue = NSValue(cgPoint: CGPoint(x: sceneView.center.x - intensity.dx, y: sceneView.center.y - intensity.dy))
    shakeAnimation.toValue = NSValue(cgPoint: CGPoint(x: sceneView.center.x + intensity.dx, y: sceneView.center.y + intensity.dy))
    sceneView.layer.add(shakeAnimation, forKey: "position")
  }

Я включаю эту функцию в дочерний класс SKScene. Вот пример вызова:

sceneShake(shakeCount: 3, intensity: CGVector(dx: shakeIntensity, dy: shakeIntensity), shakeDuration: shakeDuration)

Ответ 6

-(void)screen_shake
 {
   if (shake)
   {
    current_number_of_shakes++;

    float randx = (float)[self randomValueBetween:1 and:90]/50.0f;

    if ([self randomValueBetween:0 and:1] == 0)
        randx = -randx;

    float randy = (float)[self randomValueBetween:1 and:90]/50.0f;

    if ([self randomValueBetween:0 and:1] == 0)
        randy = -randy;


    camera.position = CGPointMake(camera.position.x + randx, camera.position.y + randy);

    if (current_number_of_shakes >= total_amount_of_shakes)
    {

        current_number_of_shakes = 0;
        move_back_from_shake = YES;
        [camera runAction:[SKAction sequence:@[[SKAction moveTo:start_point_before_shake duration:.7], [SKAction performSelector:@selector(shake_done) onTarget:self]]]];

    }




   }

}

После некоторой работы и ссылок на то, как люди это делают, Cocoa2D вот что я придумал. В вашем методе обновления вам нужно будет вызвать этот метод. Есть некоторые константы, которые вы можете заполнить пробелами, но я надеюсь, что это поможет всем, кто в этом нуждается. Также весь метод, который выполняет shake_done, обновляет Bool shake до false.