Wayne Hartman
Wayne Hartman

Reputation: 18477

SKAction: How to Animate Random Repeated Actions

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?

Upvotes: 3

Views: 3044

Answers (3)

Max
Max

Reputation: 161

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.

Upvotes: 1

T. Benjamin Larsen
T. Benjamin Larsen

Reputation: 6393

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

Upvotes: 7

John Riselvato
John Riselvato

Reputation: 12904

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

Upvotes: 2

Related Questions