I'm trying to make infinite number of coins which drop down one by one (with equal spaces made by waitforduration:
method) and for some reasons I couldn't figure out, [NSTread sleepForTimeInterval:]
didn't work well in sprite kit (the gap between first coin and the second one was always different than the rest of the gaps which were equal) so I tried to use this code but the fact is, It never worked too, what am I doing wrong? However my code works with dispatch_queue_t fallingCoinsQueue = dispatch_queue_create("falling coins", nil);
dispatch_async(fallingCoinsQueue, ^{ //Do Whatever });
but I want to use SKAction runBlock^(void){}
instead of that, any help is appreciated a lot.
@interface MyScene()
{
SKTexture* goldenCoin;
SKTexture* blackCoin;
}
@property (nonatomic,strong) SKSpriteNode * randomCoinNode;
@end
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
[self AddRightColumnCoins:self.size];
}
return self;
}
-(void)AddRightColumnCoins:(CGSize)size{
__block int counter = 0;
_randomCoinNode = [SKSpriteNode new];
SKAction *run = [SKAction runBlock:^(void){
NSArray * coinArray= [[NSArray alloc] initWithObjects:goldenCoin,blackCoin,nil];
NSUInteger randomIndex = arc4random() % [coinArray count];
_randomCoinNode =[SKSpriteNode spriteNodeWithTexture:coinArray[randomIndex]];
_randomCoinNode.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius: _randomCoinNode.size.width/2];
CGFloat x = self.view.frame.size.width - ( _randomCoinNode.frame.size.width - 10 );
CGFloat y = self.view.frame.size.height;
_randomCoinNode.position = CGPointMake(x , y);
_randomCoinNode.physicsBody.dynamic = NO;
if( _randomCoinNode.texture == goldenCoin){
_randomCoinNode.physicsBody.categoryBitMask = goldenCoinCategory;
}else{
_randomCoinNode.physicsBody.categoryBitMask = blackCoinCategory;
}
_randomCoinNode.physicsBody.contactTestBitMask = flappyCategory;
counter++;
[self addChild: _randomCoinNode];
_fall = [SKAction moveToY:-50.0f duration:2];
[_randomCoinNode runAction:_fall];
}];
SKAction *wait = [SKAction waitForDuration:0.6];
SKAction *sequence = [SKAction sequence:@[run, wait]];
SKAction *repeat = [SKAction repeatActionForever:sequence];
[_randomCoinNode runAction: repeat];
}
Personally, I would probably do this in a different way when it comes to few things, like naming coin textures, or using properties vs instance variables etc. But, to keep my example, similar to your code, this is how you can do it (copy & paste it to see how it works):
#import "GameScene.h"
@interface GameScene()
{
SKTexture* goldenCoin;
SKTexture* blackCoin;
}
@end
@implementation GameScene
-(instancetype)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
SKTextureAtlas *atlas = [SKTextureAtlas atlasNamed:@"coins"];
goldenCoin = [atlas textureNamed:@"goldenCoin"];
blackCoin = [atlas textureNamed:@"blackCoin"];
}
return self;
}
-(void)didMoveToView:(SKView *)view{
//use withRange: parameter if you want to randomize a delay
SKAction *wait = [SKAction waitForDuration:1];
__weak typeof(self) weakSelf = self;
SKAction *block = [SKAction runBlock:^{
SKSpriteNode *coin = [weakSelf spawnRandomCoin];
[weakSelf addChild:coin];
}];
SKAction *sequence = [SKAction sequence:@[wait, block]];
SKAction *loop = [SKAction repeatActionForever:sequence];
//access this key later to stop spawning
[self runAction:loop withKey:@"spawning"];
}
-(SKSpriteNode*)spawnRandomCoin {
NSArray * coinArray= [[NSArray alloc] initWithObjects:goldenCoin,blackCoin,nil];
NSUInteger randomIndex = arc4random() % [coinArray count];
NSLog(@"random index : %lu", randomIndex);
//Create and setup a coin
SKSpriteNode *coin = [SKSpriteNode spriteNodeWithTexture:coinArray[randomIndex]];
coin.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius: coin.size.width/2.0f];
CGFloat x = self.view.frame.size.width - ( coin.frame.size.width - 10.0f );
CGFloat y = self.view.frame.size.height;
coin.position = CGPointMake(x , y);
coin.physicsBody.dynamic = NO;
//setup coin's category here
if (coin.texture == blackCoin){
}else{
}
//setup moving action
SKAction *move = [SKAction moveToY:-coin.size.width duration:5];
SKAction *moveAndRemove = [SKAction sequence:@[move, [SKAction removeFromParent]]];
[coin runAction:moveAndRemove withKey:@"moving"];
return coin;
}
@end
About your code:
_randomCoinNode
, in best case will store a reference to the last coin added to the scene. Which doesn't make much sense and I guess it is not what you want.
anywhere outside of initializers, deinitializers and overridden accessor methods (getters & setters), you should use accessor methods, or dot syntax to access instance variables (as per docs recommendation). Means, inside your methods, you should use self.randomCoinNode
rather than _randomCoinNode
.
other problems (strong reference cycles & capturing self inside of a block) I've mentioned in my comments already.