Sprite Kit - Add random Sprite Nodes to the scene with Switch Case - ios

I have the following code to create random objects and add them to the scene. At the end the method is repeated after a random delay.
-(void)createObjects {
// Create random start point
float randomStartPoint = arc4random_uniform(4) * 64 + 32;
CGPoint startPoint = CGPointMake(self.size.width + 50, randomStartPoint);
// Create random object and add to scene
switch (arc4random_uniform(2)) {
case 0:
{
SKSpriteNode *crate = [SKSpriteNode spriteNodeWithImageNamed: #"crate"];
crate.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:crate.frame.size];
crate.physicsBody.dynamic = YES;
crate.physicsBody.affectedByGravity = NO;
crate.physicsBody.categoryBitMask = objectCategory;
crate.physicsBody.collisionBitMask = 0;
crate.physicsBody.contactTestBitMask = playerCategory;
crate.position = startPoint;
crate.name = #"object";
crate.zPosition = 20;
[self addChild:crate];
break;
}
case 1:
{
SKSpriteNode *cactus = [SKSpriteNode spriteNodeWithImageNamed: #"cactus"];
cactus.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:cactus.frame.size];
cactus.physicsBody.dynamic = YES;
cactus.physicsBody.affectedByGravity = NO;
cactus.physicsBody.categoryBitMask = objectCategory;
cactus.physicsBody.collisionBitMask = 0;
cactus.physicsBody.contactTestBitMask = playerCategory;
cactus.position = startPoint;
cactus.name = #"object";
cactus.zPosition = 20;
[self addChild:cactus];
break;
}
default:
break;
}
// After adding child, call same method at random delay
float randomNum = arc4random_uniform(4) * 0.4 + 0.25;
[self performSelector:#selector(createObjects) withObject:nil afterDelay:randomNum];
}
In reality I have many more cases and a lot of the same code is repeated, since every Sprite Node has the same settings. Is there a way to create a sprite node once, and in every case set a different image for the node, and add it to the scene?

You can create SKTexture in switch case and then simply create node with that texture.
Like this:
-(void)createObjects {
// Create random start point
float randomStartPoint = arc4random_uniform(4) * 64 + 32;
CGPoint startPoint = CGPointMake(self.size.width + 50, randomStartPoint);
// Create random object and add to scene
SKTexture* objectTexture;
switch (arc4random_uniform(2)) {
case 0:
objectTexture = [SKTexture textureWithImageNamed:#"crate"];
break;
case 1:
objectTexture = [SKTexture textureWithImageNamed:#"cactus"];
break;
default:
break;
}
SKSpriteNode *object = [SKSpriteNode spriteNodeWithTexture:objectTexture];
object.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:crate.frame.size];
object.physicsBody.dynamic = YES;
object.physicsBody.affectedByGravity = NO;
object.physicsBody.categoryBitMask = objectCategory;
object.physicsBody.collisionBitMask = 0;
object.physicsBody.contactTestBitMask = playerCategory;
object.position = startPoint;
object.name = #"object";
object.zPosition = 20;
[self addChild: object];
}
Another way would be to create object before switch case and then simply create texture and set it in switch case using setTexture:

Related

How to add physicsBody to tiles generated by JSTileMap and change them into one physicsBody

I use JSTileMap to draw a level. I changed it a little bit to add SKPhysicsBody to every tile but sometimes when I apply some impulse to main character and he hits the wall/ground/ceiling he is acting weird. He reflects from surfaces with the way that is contrary to the principles of physics. I think that it happens because the player hits the point where two physics bodies (for example two physics bodies of the ground) connects.
SKPhysicsBody class provides a method to create one physics body from different physics bodies + (SKPhysicsBody *)bodyWithBodies:(NSArray *)bodies; Can I use this method to create one physics body from ale the tiles physics bodies?
Here's a method from JSTileMap where I add physics bodies:
+(id) layerWithTilesetInfo:(NSArray*)tilesets layerInfo:(TMXLayerInfo*)layerInfo mapInfo:(JSTileMap*)mapInfo
{
TMXLayer* layer = [TMXLayer node];
layer.map = mapInfo;
layer.tilesByColumnRow = [NSMutableDictionary dictionary];
// basic properties from layerInfo
layer.layerInfo = layerInfo;
layer.layerInfo.layer = layer;
layer.mapTileSize = mapInfo.tileSize;
layer.alpha = layerInfo.opacity;
layer.position = layerInfo.offset;
// recalc the offset if we are isometriic
if (mapInfo.orientation == OrientationStyle_Isometric)
{
layer.position = CGPointMake((layer.mapTileSize.width / 2.0) * (layer.position.x - layer.position.y),
(layer.mapTileSize.height / 2.0) * (-layer.position.x - layer.position.y));
}
NSMutableDictionary* layerNodes = [NSMutableDictionary dictionaryWithCapacity:tilesets.count];
//MY CODE
NSMutableArray *arrayOfSprites = [[NSMutableArray alloc] init];
SKNode *theSprite;
//----||----
// loop through the tiles
for (NSInteger col = 0; col < layerInfo.layerGridSize.width; col++)
{
for (NSInteger row = 0; row < layerInfo.layerGridSize.height; row++)
{
// get the gID
NSInteger gID = layerInfo.tiles[col + (NSInteger)(row * layerInfo.layerGridSize.width)];
// mask off the flip bits and remember their result.
bool flipX = (gID & kTileHorizontalFlag) != 0;
bool flipY = (gID & kTileVerticalFlag) != 0;
bool flipDiag = (gID & kTileDiagonalFlag) != 0;
gID = gID & kFlippedMask;
// skip 0 GIDs
if (!gID)
continue;
// get the tileset for the passed gID. This will allow us to support multiple tilesets!
TMXTilesetInfo* tilesetInfo = [mapInfo tilesetInfoForGid:gID];
[layer.tileInfo addObject:tilesetInfo];
if (tilesetInfo) // should never be nil?
{
SKTexture* texture = [tilesetInfo textureForGid:gID];
SKSpriteNode* sprite = [SKSpriteNode spriteNodeWithTexture:texture];
sprite.name = [NSString stringWithFormat:#"%ld",(long)(col + row * layerInfo.layerGridSize.width)];
// make sure it's in the right position.
if (mapInfo.orientation == OrientationStyle_Isometric)
{
sprite.position = CGPointMake((layer.mapTileSize.width / 2.0) * (layerInfo.layerGridSize.width + col - row - 1),
(layer.mapTileSize.height / 2.0) * ((layerInfo.layerGridSize.height * 2 - col - row) - 2) );
}
else
{
sprite.position = CGPointMake(col * layer.mapTileSize.width + layer.mapTileSize.width/2.0,
(mapInfo.mapSize.height * (tilesetInfo.tileSize.height)) - ((row + 1) * layer.mapTileSize.height) + layer.mapTileSize.height/2.0);
}
// flip sprites if necessary
if(flipDiag)
{
if(flipX)
sprite.zRotation = -M_PI_2;
else if(flipY)
sprite.zRotation = M_PI_2;
}
else
{
if(flipY)
sprite.yScale *= -1;
if(flipX)
sprite.xScale *= -1;
}
// add sprite to correct node for this tileset
SKNode* layerNode = layerNodes[tilesetInfo.name];
if (!layerNode) {
layerNode = [[SKNode alloc] init];
layerNodes[tilesetInfo.name] = layerNode;
}
//adding physicsbody to every tile
//sprite.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(sprite.frame.size.width, sprite.frame.size.height)];
//sprite.physicsBody.dynamic = NO;
//sprite.physicsBody.categoryBitMask = mapCategory;
//[arrayOfSprites addObject:sprite.physicsBody];
[layerNode addChild:sprite];
NSUInteger indexes[] = {col, row};
NSIndexPath *indexPath = [NSIndexPath indexPathWithIndexes:indexes length:2];
[layer.tilesByColumnRow setObject:sprite forKey:indexPath];
//MY CODE
sprite.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:sprite.frame.size];
sprite.physicsBody.dynamic = NO;
[arrayOfSprites addObject:sprite.physicsBody];
//-----||------
#ifdef DEBUG
// CGRect textRect = [texture textureRect];
// NSLog(#"atlasNum %2d (%2d,%2d), gid (%d,%d), rect (%f, %f, %f, %f) sprite.pos (%3.2f,%3.2f) flipx%2d flipy%2d flipDiag%2d", gID+1, row, col, [tilesetInfo rowFromGid:gID], [tilesetInfo colFromGid:gID], textRect.origin.x, textRect.origin.y, textRect.size.width, textRect.size.height, sprite.position.x, sprite.position.y, flipX, flipY, flipDiag);
#endif
}
}
}
//MY CODE
NSArray *array = [arrayOfSprites copy];
theSprite = [SKNode node];
theSprite.physicsBody = [SKPhysicsBody bodyWithBodies:array];
theSprite.physicsBody.dynamic = NO;
theSprite.position = CGPointMake(layer.position.x+16, layer.position.y+16);
[layer addChild:theSprite];
//-----||------
// add nodes for any tilesets that were used in this layer
for (SKNode* layerNode in layerNodes.allValues) {
if (layerNode.children.count > 0) {
[layer addChild:layerNode];
}
}
[layer calculateAccumulatedFrame];
return layer;
}
After adding physics bodies to every tile and adding those physics bodies to NSMutableArray I assign a copy of this NSMutableArray to the NSArray and try to create one physics body out of it like this:
//MY CODE
NSArray *array = [arrayOfSprites copy];
theSprite = [SKNode node];
theSprite.physicsBody = [SKPhysicsBody bodyWithBodies:array];
theSprite.physicsBody.dynamic = NO;
theSprite.position = CGPointMake(layer.position.x+16, layer.position.y+16);
[layer addChild:theSprite];
//-----||------
In result, one physics body with the height and width of one tile is added.
If you want to use [SKPhysicsBody bodyWithBodies:array], then you need to make sure that all the bodies in the array are relative to the parent node.
sprite.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:sprite.frame.size]; means that your physics body position is relative to the sprite node. You need the body relative to the parent node.
The only way I know how to do this is with center:
sprite.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:sprite.frame.size center:sprite.position];
This should place the SKPhysicsBody at the location of the sprite when it is added to the parent node.

Sprite not keeping track of count

The following code I'm using is to end the game when I my number of balls in the scene reaches zero. Now I'm not getting any error when I put the code in but it doesn't do any thing as of counting the balls and reacting to that count. I am fairly new to sprite kit so I understand that this is a problem that comes so easy to others but this is the first time of worked with reacting to the count of a certain sprite in a scene so please help.
SKSpriteNode *ball;
int numberOfBalls = 3;
#implementation EasyScene
-(void)didBeginContact:(SKPhysicsContact *)contact {
if ([self isGameWon]) {
EasyEndGameScene *end = [EasyEndGameScene sceneWithSize:self.size];
[self.view presentScene:end transition:[SKTransition doorsCloseHorizontalWithDuration:1]];
}
}
-(BOOL)isGameWon {
int numberOfBalls = 3;
for (SKNode* node in self.children) {
if ([node.name isEqual: ball]) {
numberOfBalls = 0;
}
}
return numberOfBalls =0;
}
- (void) addBalls:(CGSize)size {
for (int i = 0; i < 3; i++) {
//create brick sprite from image
SKSpriteNode *ball = [SKSpriteNode spriteNodeWithImageNamed:#"ball-image"];
ball.name = #"ball";
//resize balls
ball.size = CGSizeMake(self.size.width/5.5, self.size.width/5.5);
//position and space out balls
int xPos = size.width/3 * (i+.5);
int yPos = self.size.height - (self.size.width/7);
ball.position = CGPointMake(xPos, yPos);
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:ball.size.width/2];
ball.physicsBody.categoryBitMask = ballCategory;
ball.physicsBody.contactTestBitMask = bottomEdgeCategory;
[self addChild:ball];
}
}
In the following code I got rid of the Global Variable and tested all nodes for the specified name of #"ball" and return YES if none were found:
static const uint32_t ballCategory = 1; //00000000000000000000000000000001
static const uint32_t edgeCategory = 2; //00000000000000000000000000000010
static const uint32_t bottomEdgeCategory = 4; //00000000000000000000000000000100
SKSpriteNode *ball;
#implementation EasyScene
-(void)didBeginContact:(SKPhysicsContact *)contact {
//create placeholder for the "non ball" object
SKPhysicsBody *notTheBall;
SKPhysicsBody *theBall;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask) {
notTheBall = contact.bodyB;
theBall = contact.bodyA;
} else {
notTheBall = contact.bodyA;
theBall = contact.bodyB;
}
if (notTheBall.categoryBitMask == bottomEdgeCategory) {
NSLog(#"hit bottom edge");
// SKAction *playSFX = [SKAction playSoundFileNamed:#"gameover.mp3" waitForCompletion:NO];
// [self runAction:playSFX];
// EasyEndGameScene *end = [EasyEndGameScene sceneWithSize:self.size];
// [self.view presentScene:end transition:[SKTransition doorsCloseHorizontalWithDuration:1]];
// [GameState sharedInstance].score = 0;
// [gameMusic pause];
[theBall.node removeFromParent];
}
if ([self isGameWon]) {
EasyEndGameScene *end = [EasyEndGameScene sceneWithSize:self.size];
[self.view presentScene:end transition:[SKTransition doorsCloseHorizontalWithDuration:1]];
}
}
-(BOOL)isGameWon {
unsigned count = 0;
for (SKNode* node in self.children)
if ([node.name isEqual:ball])
count++;
return count == 0;
}
- (void) addBalls:(CGSize)size {
for (int i = 0; i < 3; i++) {
//create brick sprite from image
SKSpriteNode *ball = [SKSpriteNode spriteNodeWithImageNamed:#"ball-image"];
ball.name = #"ball";
//resize balls
ball.size = CGSizeMake(self.size.width/5.5, self.size.width/5.5);
//position and space out balls
int xPos = size.width/3 * (i+.5);
int yPos = self.size.height - (self.size.width/7);
ball.position = CGPointMake(xPos, yPos);
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:ball.size.width/2];
ball.physicsBody.categoryBitMask = ballCategory;
ball.physicsBody.contactTestBitMask = bottomEdgeCategory;
[self addChild:ball];
}
}
Remove the ball global variable, as it's not needed.
Test all nodes for the specified name of #"ball" and return YES if none were found:
-(BOOL)isGameWon {
for (SKNode* node in self.children)
if ([node.name isEqualToString:#"ball"])
return NO;
return YES;
}
I think it should be
return numberOfBalls == 0
in your isgameWon method
you forget an extra =

SpriteKit Scrolling Bottom Has Gap When Loops

In my app, I have an image that acts as the ground, and scrolls along the bottom. I initialize and scroll it with:
-(void)initalizingScrollingBackground
{
for (int i = 0; i < 2; i++)
{
SKSpriteNode *bg = [SKSpriteNode spriteNodeWithImageNamed:#"Bottom_Scroller"];
bg.zPosition = BOTTOM_BACKGROUND_Z_POSITION;
bottomScrollerHeight = bg.size.height;
bg.position = CGPointMake((i * bg.size.width) + (bg.size.width * 0.5f) - 1, bg.size.height * 0.5f);
bg.name = #"bg";
bg.physicsBody = [SKPhysicsBody bodyWithTexture:bg.texture size:bg.texture.size];
bg.physicsBody.categoryBitMask = bottomBackgroundCategory;
bg.physicsBody.contactTestBitMask = flappyBirdCategory;
bg.physicsBody.collisionBitMask = 0;
bg.physicsBody.affectedByGravity = NO;
[self addChild:bg];
}
}
- (void)moveBottomScroller
{
[self enumerateChildNodesWithName:#"bg" usingBlock: ^(SKNode *node, BOOL *stop)
{
SKSpriteNode * bg = (SKSpriteNode *) node;
CGPoint bgVelocity = CGPointMake(-BG_VELOCITY, 0);
CGPoint amtToMove = CGPointMultiplyScalar(bgVelocity,_dt);
bg.position = CGPointAdd(bg.position, amtToMove);
//Checks if bg node is completely scrolled of the screen, if yes then put it at the end of the other node
if (bg.position.x + bg.size.width * 0.5f <= 0)
{
bg.position = CGPointMake(bg.size.width*2 - (bg.size.width * 0.5f) - 2,
bg.position.y);
}
}];
}
However, after it scrolls for so long, it shows a gap in it, as shown below. How can I fix this?
What I'd do is:
// This would go in the init or didMoveToView method of your scene
const NSUInteger numBgs = 3;
for (NSUInteger i = 0; i < numBgs; i++) {
CGFloat color = 0.2f * i;
SKSpriteNode *bg = [SKSpriteNode spriteNodeWithColor:[UIColor colorWithRed:color green:color blue:color alpha:1.0] size:CGSizeMake(512, 300)];
bg.anchorPoint = CGPointMake(0.5, 0);
bg.position = CGPointMake((i * bg.size.width) + (bg.size.width * 0.5f), CGRectGetMinY(self.frame));
bg.name = #"bg";
[self addChild:bg];
if (i == numBgs-1) { // Means it's last bg on the right
lastBg = bg;
}
}
[self enumerateChildNodesWithName:#"bg" usingBlock:^(SKNode *node, BOOL *stop) {
SKSpriteNode * bg = (SKSpriteNode *) node;
//Checks if bg node is completely scrolled of the screen, if yes then put it at the end of the other node
if (bg.position.x + bg.size.width * 0.5f <= 0)
{
bg.position = CGPointMake(lastBg.position.x + bg.frame.size.width,
bg.position.y);
lastBg = bg;
}
CGPoint bgVelocity = CGPointMake(-BG_VELOCITY, 0);
CGPoint amtToMove = CGPointMultiplyScalar(bgVelocity,_dt);
bg.position = CGPointAdd(bg.position, amtToMove);
}];
Where lastBg is an instance variable that points to the bg located at the top most right so that re-positioning is always going to be relative to this sprite.
Other things you can try are switching the check of the position before the re-positioning (as I did in the example) and also remove the check out of the enumerate block and do it independently of the re-positioning.
I used to do like in the example and it worked fine. Let me know how it does.

How can i make a node collide with the sides of moving scene?

Hi i wish to know how can my nodes can collide with a moving scene. I have a main node that cans move along the x axis and cans collide with other nodes. I want to make the non main nodes collide with the sides of the moving scene. How can i make that ? ( the camera only follow the main node).
I have an obstacle class.
in my MLObstacle.m
-(id)init
{
if(self = [super init])
{
int aux = arc4random()%3;
switch (aux) {
case 0:
self = [MLObstaculo spriteNodeWithColor:[UIColor greenColor] size:CGSizeMake((aux+1)*10,(aux+1)*13 )];
break;
case 1:
self = [MLObstaculo spriteNodeWithColor:[UIColor blueColor] size:CGSizeMake((aux+1)*10,(aux+1)*15 )];
break;
case 2:
self = [MLObstaculo spriteNodeWithColor:[UIColor redColor] size:CGSizeMake((aux+1)*10,(aux+1)*14 )];
break;
}
self.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.size];
self.physicsBody.affectedByGravity = NO;
self.physicsBody.categoryBitMask = obstacleCategory;
self.physicsBody.contactTestBitMask = visibleSidesCategory;
self.physicsBody.collisionBitMask = visibleSidesCategory;
[self setDx:0.0];
[self setDy:2.4];
}
return self;
}
So i add Obstacles to my WorldClass
In my WorlGen.m
-(void)GenerateWorld
{
SKSpriteNode *Ground = [SKSpriteNode spriteNodeWithColor:[UIColor greenColor] size:CGSizeMake(self.scene.frame.size.width,100)];
Ground.position = CGPointMake(0, -self.scene.frame.size.height/2 + Ground.size.height/2);
Ground.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:Ground.size];
Ground.physicsBody.dynamic = NO;
[self addChild:Ground];
for(int i = 0; i < 3; i++)
{
MLObstaculo * auxobs= [[MLObstaculo alloc]initObstaculo];
auxobs.position = CGPointMake((i+1)*50, Ground.position.y + Ground.size.height/2 +auxobs.size.height/2 );
[self.Obstaculos addObject:auxobs];
[self addChild:auxobs];
auxobs = NULL;
}
}
and then i add the world to my scene
Scene.m
-(id)initWithSize:(CGSize)size
{
if(self = [super initWithSize:size]
{
WorldGen * world = [[WorldGen alloc] init];
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
self.physicsWorld.contactDelegate = self;
self.physicsBody.categoryBitMask = VisibleSidesCategory
self.physicsBody.collisionBitMask = ObstacleCategory;
self.anchorPoint = CGPointMake(0.5,0.5);
[self addChild:world];
[world GenerateWorld];
}
}
Update...
-(void)didSimulatePhysics
{
[self centerOnNode:hero];
}
-(void)centerOnNode:(SKNode *)node
{
CGPoint pointinScene = [self convertPoint:node.position fromNode:node.parent];
world.position = CGPointMake(world.position.x -pointinScene.x , world.position.y);
}
I initalized all the category variables with this format ( static const uint32_t ObstacleCategory = 0x1 <<1; )
First edit by defining your collisionBitMask
-(id)init
{
self.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.size];
self.physicsBody.categoryBitMask = ObstacleCategory;
self.physicsBody.contactTestBitMask = VisibleSidesCategory;
self.physicsBods.collisionBitMask = VisibleSidesCategory;
}
Second make this steps in Xcode -> File -> New -> File -> C and C++ -> Header File
Give a name of Physics.h and add those lines in it:
typedef enum : uint8_t {
ObstacleCategory = 1,
VisibleSidesCategory = 2,
} ColliderType;
and then import Physics.h into all your classes (You don't need to alloc or init for Physics.h, just use ObstacleCategory and VisibleSidesCategory)
From this and another question's problem, I have finally determined there is an error in the API. Specifically, a node's anchorPoint is considered (0,0) during initialization if using a WithRect: or FromRect: based initializer(but not a RectOfSize: or center: based one), so you get unexpected behavior.
Thus, even when the anchorPoint of the Scene and other objects are set to (0.5,0.5), a node places its lower left corner relative to the parent node's midpoint, rather than placing the child's midpoint relative to the parent node's midpoint.
Specifically, this can be solved in any one of these three ways:
Use an anchorPoint of (0,0).
Create your nodes without using Rect based initializers. You can use shapeNodeWithRectOfSize: instead of shapeNodeWithRect: and bodyWithEdgeLoopFromPath: instead of bodyWithEdgeLoopFromRect:
Set the Rect's initial position as lower and to the left, like so:
node.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:CGRectMake(-node.size.width/2, -node.size.height/2, node.size.width, node.size.height)];

SKSpriteNode loses its physicsBody properties

I'm using SpriteKit to create a game in which objects at the bottom of the map move.
The objects are crocodiles and coins.
The scene uses an NSTimer to that calls this selector:
timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(generateBottomSprite) userInfo:nil repeats:YES];
-(void) generateBottomSprite{
int random = (arc4random() % difficulty+3) + difficulty-3;
if (counter >random){
SKSpriteNode *bottomSprite;
if (random>difficulty){
bottomSprite = [SKSpriteNode spriteNodeWithImageNamed:#"crocodile"];
bottomSprite.size = CGSizeMake(70, 120);
bottomSprite.physicsBody.categoryBitMask = crocMask;
}else{
bottomSprite = [SKSpriteNode spriteNodeWithImageNamed:#"coin"];
bottomSprite.size = CGSizeMake(50, 50);
bottomSprite.physicsBody.categoryBitMask = coinMask;
}
//create a crocodile
bottomSprite.position = CGPointMake(self.frame.size.width,bottomSprite.frame.size.height/2);
bottomSprite.name = #"bottomSprite";
[self addChild: bottomSprite];
counter = 0;
[self enumerateChildNodesWithName:#"bottomSprite" usingBlock: ^(SKNode *node, BOOL *stop) {
node.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:node.frame.size];
node.physicsBody.affectedByGravity = NO;
node.physicsBody.mass = 8000.0;
node.physicsBody.friction = 2.50;
node.physicsBody.usesPreciseCollisionDetection = YES;
}];
}
}
The problem: all the physicsWorld's properties are re-initialized on each call to the selector and i must iterate over the children of the scene in order to re-assign them.
Why are the properties get re-initialized?
you are setting the categorybitmask before creating the skphysicsbody itself
bottomSprite = [SKSpriteNode spriteNodeWithImageNamed:#"crocodile"];
bottomSprite.size = CGSizeMake(70, 120);
//add the following
bottomSprite.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:bottomSprite.frame.size];
///////////////
bottomSprite.physicsBody.categoryBitMask = crocMask;
move all the physics settings outside the enumeration like so
if (counter >random){
SKSpriteNode *bottomSprite;
int currentBitMask;
if (random>difficulty){
bottomSprite = [SKSpriteNode spriteNodeWithImageNamed:#"crocodile"];
bottomSprite.size = CGSizeMake(70, 120);
currentBitMask = crocMask;
}else{
bottomSprite = [SKSpriteNode spriteNodeWithImageNamed:#"coin"];
bottomSprite.size = CGSizeMake(50, 50);
currentBitMask = coinMask;
}
**bottomSprite.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:bottomSprite.frame.size];**
bottomSprite.physicsBody.categoryBitMask = currentBitMask ;
bottomSprite.physicsBody.affectedByGravity = NO;
bottomSprite.physicsBody.mass = 8000.0;//way too high
bottomSprite.physicsBody.friction = 2.50;//probably high
bottomSprite.physicsBody.usesPreciseCollisionDetection = YES;//overkill, set only if critical
//create a crocodile
bottomSprite.position = CGPointMake(self.frame.size.width,bottomSprite.frame.size.height/2);
bottomSprite.name = #"bottomSprite";
[self addChild: bottomSprite];
you also might wanna make use of the update method in the skscene and nstimeintervals to measure time till you pass half a second then call the generateBottomSprite method, it should be more efficient than using an additional NSTimer.
Feel free to ask if anything is unclear, hope this helps.

Resources