2

如何为具有多个子节点的节点创建 SpriteKit 动画,并将节点的多个部分作为一个组进行动画处理?

我有以下节点结构:

  • 鸟有身体、尾巴、头部等的子节点
  • 头部有上喙、下喙等子节点

在下面的示例中,我想为整个小鸟制作动画,所有孩子都弯腰捡起种子。在弯腰时,鸟张开嘴吃种子。请注意,喙不是鸟的孩子,而是头节点的孩子。

//body does this action
    SKAction* bendDown = [SKAction rotateToAngle:M_PI*0.3 duration:lookUpTiming];

//beak does this action
    SKAction* openBeak = [SKAction rotateToAngle:-M_PI*0.175 duration:beakDuration + arc4random()%20/100.0];

//I need both actions to be  played together as a group:
    SKAction* bendAndOpenBeak = [SKAction group:@[bendDown, openBeak]];


    //if I call this, the body will perform both actions. 
    [self runAction:bendAndOpenBeak completion:^{

    }];

这是我可以在头节点上运行以使其子节点执行操作的代码runAction:onChildWithName:但是,我需要将其作为一个组与主体(父节点的父节点)节点一起运行

-(SKAction*)openBeak
{
    float angleUp = -M_PI*0.175;
    float duration = beakDuration + arc4random()%20/100.0;

    SKAction* rotateUp = [SKAction rotateToAngle:angleUp duration:duration];
    rotateUp.timingMode = SKActionTimingEaseOut;

    SKAction* rotateDown = [SKAction rotateToAngle:-angleUp duration: duration];
    rotateUp.timingMode = SKActionTimingEaseOut;


   return  [SKAction group:@[[SKAction runAction:rotateUp onChildWithName:@"beakUpper"],
                      [SKAction runAction:rotateDown onChildWithName:@"beakLower"] ]];

}
4

3 回答 3

2

[SKAction runAction:onChildWithName:] has an instantaneous duration, and so your group action won't wait for its completion.

The simple way to get the group to wait for all actions to complete is to add an [SKAction waitForDuration:] to the group, and use the duration property of the action passed into [SKAction runAction:onChildWithName:]

So in your example the code needed looks like this

-(SKAction*)openBeak {
  float angleUp = -M_PI*0.175;
  float duration = beakDuration + arc4random()%20/100.0;

  SKAction* rotateUp = [SKAction rotateToAngle:angleUp duration:duration];
  rotateUp.timingMode = SKActionTimingEaseOut;
  SKAction* waitForRotateUp = [SKAction waitForDuration:rotateUp.duration];

  SKAction* rotateDown = [SKAction rotateToAngle:-angleUp duration: duration];
  rotateUp.timingMode = SKActionTimingEaseOut;
  SKAction* waitForRotateDown = [SKAction waitForDuration:rotateDown.duration];

  return  [SKAction group:@[[SKAction runAction:rotateUp onChildWithName:@"beakUpper"],
                  [SKAction runAction:rotateDown onChildWithName:@"beakLower"],
                  waitForRotateDown, waitForRotateUp]];

}

I've used the SKActions duration property in the example above even though your explicitly setting duration values as this approach can be used for actions such as sprite animations, where the duration isn't explicitly set.

于 2014-09-17T10:51:25.207 回答
1

您不需要使用group,只需在每个节点上分别运行它们,一个一个:

//body does this action
SKAction* bendDown = [SKAction rotateToAngle:M_PI*0.3 duration:lookUpTiming];

//beak does this action
SKAction* openBeak = [SKAction rotateToAngle:-M_PI*0.175 duration:beakDuration + arc4random()%20/100.0];

[bodyNode runAction:bendDown];
[beakNode runAction:openBeak];
于 2014-04-07T17:48:22.917 回答
0

这是您所要求的解决方案。这个答案使用 Swift 3。

import SpriteKit

class Scene: SKScene {
  // 1
  let lookUpTiming = 0.2
  let beakDuration = 0.2

  // 2
  let body = SKNode()
  let beak = SKNode()

  override func sceneDidLoad() {
    // 3
    let bendDown = SKAction.run {
      let action = SKAction.rotate(toAngle: .pi * 0.3, duration: self.lookUpTiming)
      self.body.run(action)
    }
    // 4
    let openBeak = SKAction.run {
      let duration = self.beakDuration + Double(arc4random_uniform(20)) / 100
      let action = SKAction.rotate(toAngle: -.pi * 0.175, duration: duration)
      self.beak.run(action)
    }
    // 5
    let group = SKAction.group([bendDown, openBeak])
    // 6
    run(group)
  }
}

这是这段代码中发生的事情:

  1. 初始化设置
  2. 初始化节点
  3. 为第一个动画创建一个 SKAction
  4. 为第二个动画创建一个 SKAction
  5. 创建一组这两个动作
  6. 从现场运行该组。即使它们在不同的节点上,两个动画也会同时触发!
于 2016-09-21T01:04:42.943 回答