【问题标题】:A camera shake effect for SpriteKitSpriteKit 的相机抖动效果
【发布时间】:2014-01-02 18:59:29
【问题描述】:

有没有人知道一些开箱即用的库,可以为SKNode 提供相机抖动等效果?

如果没有,有没有一种简单的方法可以使用动作来实现相机抖动?

谢谢

【问题讨论】:

    标签: ios7 sprite-kit


    【解决方案1】:

    我发现了一种使用 SKAction 的优雅方法,它可以让你的节点动摇。例如,水平摇动:

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

    【讨论】:

    • 酷。我会试试这个,看看它比我现在做的好多少。
    • 哇,这真的很好用,希望我可以投票两次,因为它还有助于解决一些 FPS 问题,因为我可以清除更新声明中的混乱。
    • 这对我不起作用我得到错误:动画 SKScene 的位置没有效果。 - 有什么想法吗?
    • 与 user1216855 的结果相同,我不再可以从这段代码中看到抖动效果。我认为 iOS7.1 对其进行了一些更新,但它不再有效。如何解决?
    • @Rocotilos 我不在我的代码前面,但我认为您试图为场景设置动画。你试过一个简单的子节点吗?
    【解决方案2】:

    已接受答案中的抖动方法有一个警告 - 它只能应用于其 x,y 坐标未被其他正在运行的 SKAction 修改的节点。这是因为我们在摇动完成后将节点的位置重置为其初始位置。

    如果您想要一个可以与其他 SKAction 一起工作的摇动动作,这些 SKAction 会按顺序/平行修改摇动节点的位置(x 或 y 坐标),您需要添加正在应用的随机摇动的反向动作。

    一个 Swift 扩展供参考:

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

    注意,这个修改不需要将节点作为参数传入

    【讨论】:

      【解决方案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)
          }
      }
      

      感谢 Stephen Haney 我刚刚将它移到扩展中的实现

      【讨论】:

      • 感谢您这样做!我的代码基于这个线程中的标记答案,所以我们正式回到原点;)
      【解决方案4】:

      如果你想动摇整个场景,这里是 Rob 用 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)
      

      【讨论】:

        【解决方案5】:

        这是我使用的方法。希望能帮助到你。根据需要修改它。

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

        【讨论】:

        • 这在我的游戏中效果很好,可以将带有值[self shakeScreenFor:2 withIntensity:CGVectorMake(2, 2) andDuration:0.1];的东西撞到屏幕上
        • 这行得通!我喜欢这个。投票赞成。谢谢朋友。
        【解决方案6】:

        @Benzi 的 Swift 4 版本回答:

        func shake(duration: Double, amplitudeX: CGFloat = 3, amplitudeY: CGFloat = 3) -> SKAction {
            let numberOfShakes = duration / 0.015 / 2.0
            var actionsArray:[SKAction] = []
            for _ 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.moveBy(x: dx, y:dy, duration: 0.015)
                let reverse = forward.reversed()
                actionsArray.append(forward)
                actionsArray.append(reverse)
            }
            return SKAction.sequence(actionsArray)
        }
        

        【讨论】:

        • 注意不要在场景中使用它,否则你会得到:“动画 SKScene 的位置没有效果。”您需要在包含所有其他节点的父节点上运行该操作。
        【解决方案7】:
        -(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 抖动更新为 false。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-09-27
          • 2018-12-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多