Reputation: 18477
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
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
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
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