3

I'd like to run a repeating SKAction but with random values on each repeat. I have read this question here that shows one way to do this. However, I want my sprite's movements to be animated, not simply changing its position. One solution I have come up with is to run a sequence of actions, with the final action calling my move method in a recursive fashion:

- (void)moveTheBomber {
    __weak typeof(self) weakSelf = self;

    float randomX = //  determine new "randomX" position

    SKAction *moveAction = [SKAction moveToX:randomX duration:0.25f];
    SKAction *waitAction = [SKAction waitForDuration:0.15 withRange:0.4];
    SKAction *completionAction = [SKAction customActionWithDuration:0 actionBlock:^(SKNode *node, CGFloat elapsedTime) {
        [weakSelf moveTheBomber];
    }];

    SKAction *sequence = [SKAction sequence:@[moveAction, waitAction, completionAction]];

    [self.bomber runAction:sequence];
}

Recursively calling this method feels 'icky' to me, but given my limited experience with SpriteKit, seemed like the most obvious way to accomplish this.

How can I animate the random movement of a sprite on screen forever?

Community
  • 1
  • 1
Wayne Hartman
  • 18,369
  • 7
  • 84
  • 116

3 Answers3

7

No bells or whistles, but I think this should get you on your way:

Edit: Sorry it should be:

SKAction *randomXMovement = [SKAction runBlock:^(void){
    NSInteger xMovement = arc4random() % 20;
    NSInteger leftOrRight = arc4random() % 2;
    if (leftOrRight == 1) {
        xMovement *= -1;
    }
    SKAction *moveX = [SKAction moveByX:xMovement y:0 duration:1.0];
    [aSprite runAction:moveX];
}];

SKAction *wait = [SKAction waitForDuration:1.0];
SKAction *sequence = [SKAction sequence:@[randomXMovement, wait]];
SKAction *repeat = [SKAction repeatActionForever:sequence];
[aSprite runAction: repeat];
T. Benjamin Larsen
  • 6,373
  • 4
  • 22
  • 32
  • 1
    Adding a corresponding wait that is equal to the duration of the `runBlock:` seems a little goofy to me, but at least it takes the recursion out. – Wayne Hartman Feb 19 '14 at 23:43
  • Perhaps, but the main idea is to have an action that works as a separate entity and those block-actions are basically there to fulfill any non-basic requirements. Putting another action inside it is just one of the infinite possibilities. – T. Benjamin Larsen Feb 20 '14 at 06:49
2

Yep there's a great Action you can use.

So instead of doing your current runAction do this:

[self.bomber runAction:[SKAction repeatActionForever:sequence]];

You'll also need to change your moveAction moveToX: value to something like arc4random

John Riselvato
  • 12,854
  • 5
  • 62
  • 89
  • I'm a tad concerned about how you'll approach the random value since after you set repeatActionForever you are running that sequence not outside var (the random value). If you have it all set then never mind but if you want a little help with that let me know in a comment. – John Riselvato Feb 19 '14 at 05:17
  • You can't use a `repeatActionForever`, because the moveAction needs to be different in each iteration. – Wayne Hartman Feb 19 '14 at 13:45
  • @WayneHartman, you can use repeatActionForever, you need to do the iteration in an actionBlock. – John Riselvato Feb 19 '14 at 18:09
  • @nickfalk does exactly what I'm talking about. – John Riselvato Feb 19 '14 at 18:10
1

As random function is only computed once, it doesn't work to put it into a sequence or a repeatForever SKAction. Ref to: Why doesn't the call to the random function work in the sequence?

So a good Swift answer :

func newLocation(_ circle: SKShapeNode) {
    circle.run(SKAction.move(to: randomPosition(), duration: 2), completion: {
        [weak self] in self?.newLocation(circle)
    })
}

With randomPosition():

func randomPosition() -> CGPoint {
    let height = Int(self.view?.frame.height ?? 0)
    let width = Int(self.view?.frame.width ?? 0)

    let randomPosition = CGPoint(x: Int.random(in: 0..<width), y: Int.random(in: 0..<height))

    return randomPosition
}

The completion of the run action is calling itself again and again, with a new computed random position.

dippas
  • 58,591
  • 15
  • 114
  • 126
Max
  • 141
  • 1
  • 4