Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

iOS Build a Game with Sprite Kit Physics and Collision World Setup and Physics Bodies

My ground node is not appearing.

There is nothing there, and I'm can't find what I'm missing. Can someone tell me what I've missed?

from groundNode.m:

#import "GroundNode.h"
#import "Util.h"

@implementation GroundNode

+ (instancetype) groundWithSize:(CGSize)size {
    GroundNode *ground = [self spriteNodeWithColor:[SKColor greenColor] size:size];
    ground.name = @"ground";
    ground.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:size];
    ground.position = CGPointMake(size.width/2, size.height/2);

    return ground;
}

From GamePlayScene.m:

GroundNode *ground = [GroundNode groundWithSize:CGSizeMake(self.frame.size.width, 22)];
    [self addChild:ground];

if you solve your issue pls let us know how ? and what was the problem ?

15 Answers

try adding this into your GroundNode.M implementation class: try messing around with the value changing from 1, to 15 etc.. it seems to be hiding the green box behind the background by default.

hope this helps.

      ground.zPosition = 1;

This worked. Thanks Brittany!

Short and easy thanks

are you sure its not just dropping off the screen because of gravity? what do you have gravity set to in your physics world?

try commenting out the line that sets the gravity force and see if the ground node stays put. that way you can see if its actually being added to the scene correctly

I'm not sure. Here is what I have for physicsWorld:

 self.physicsWorld.gravity = CGVectorMake(0, -9.8);
 self.physicsWorld.contactDelegate = self;

comment out the gravity line and try running the app, does the ground node appear?

    // self.physicsWorld.gravity = CGVectorMake(0, -9.8);
    self.physicsWorld.contactDelegate = self;

Thanks Stone. although, no change.

can you post the full code you have in your gamePlayScene

Definitely

//
//  GamePlayScene.m
//  Space Cat
//
//  Created by Jared Watkins on 12/16/14.
//  Copyright (c) 2014 Jared Watkins. All rights reserved.
//

#import "GamePlayScene.h"
#import "MachineNode.h"
#import "SpaceCatNode.h"
#import "ProjectileNode.h"
#import "SpaceDogNode.h"
#import "GroundNode.h"
#import "Util.h"

@interface GamePlayScene ()

@property (nonatomic) NSTimeInterval lastUpdateTimeInterval;
@property (nonatomic) NSTimeInterval timeSinceEnemyAdded;
@property (nonatomic) NSTimeInterval totalGameTime;
@property (nonatomic) NSInteger minSpeed;
@property (nonatomic) NSTimeInterval addEnemyTimeInterval;
//  SFX
@property (nonatomic) SKAction *damageSFX;
@property (nonatomic) SKAction *explodeSFX;
@property (nonatomic) SKAction *laserSFX;

@end

@implementation GamePlayScene

-(void)didMoveToView:(SKView *)view {

    self.lastUpdateTimeInterval = 0;
    self.timeSinceEnemyAdded = 0;
    self.addEnemyTimeInterval = 1.5;
    self.totalGameTime = 0;
    self.minSpeed = SpaceDogMinSpeed;

    /* Setup your scene here */
    SKSpriteNode *background = [SKSpriteNode spriteNodeWithImageNamed:@"background_1"];
    background.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
    background.xScale = 1.18;
    background.yScale = 1.18;
    [self addChild:background];

    MachineNode *machine = [MachineNode machineAtPosition:CGPointMake(CGRectGetMidX(self.frame), 12) ];
    [self addChild:machine];

    SpaceCatNode *spaceCat = [SpaceCatNode spaceCatAtPosition:CGPointMake(machine.position.x, machine.position.y-2)];
    [self addChild:spaceCat];



//    Add Physics to the scene (World)
    self.physicsWorld.gravity = CGVectorMake(0, -9.8);
    self.physicsWorld.contactDelegate = self;

    GroundNode *ground = [GroundNode groundWithSize:CGSizeMake(self.frame.size.width, 22)];
    [self addChild:ground];

    [self setupSounds];

}

//  SFX
- (void) setupSounds {
    self.damageSFX = [SKAction playSoundFileNamed:@"Damage.caf" waitForCompletion:NO];
    self.explodeSFX = [SKAction playSoundFileNamed:@"Explode.caf" waitForCompletion:NO];
    self.laserSFX = [SKAction playSoundFileNamed:@"Laser.caf" waitForCompletion:NO];
}

- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    for (UITouch *touch in touches) {
        CGPoint position = [touch locationInNode:self];
        [self shootProjectileTowardsPosition:position];
    }

}

- (void) shootProjectileTowardsPosition:(CGPoint)position {
    SpaceCatNode *spaceCat = (SpaceCatNode*)[self childNodeWithName:@"SpaceCat"];
    [spaceCat performTap];

    MachineNode *machine = (MachineNode *)[self childNodeWithName:@"machine"];


    ProjectileNode *projectile = [ProjectileNode projectileAtPosition:CGPointMake(machine.position.x, machine.position.y + machine.frame.size.height - 15)];
    [self addChild:projectile];
    [projectile moveTowardsPosition:position];
//    SFX - Laser
    [self runAction:self.laserSFX];

}

//  place enemies
- (void) addSpaceDog {

    NSUInteger randomSpaceDog = [Util randomWithMin:0 max:2];

    //    randomly place spaceDogs
    SpaceDogNode *spaceDog = [SpaceDogNode spaceDogOfType:randomSpaceDog];

    //    vary the velocity between -100 and -50 as found in Util.h
    float dy = [Util randomWithMin:SpaceDogMinSpeed max:spaceDogMaxSpeed];
    spaceDog.physicsBody.velocity = CGVectorMake(0, dy);

    //    keep the spaceDogs within the bounds of the frame
    float y = self.frame.size.height + spaceDog.size.height;  // right above the height of the frame
    float x = [Util randomWithMin:10 + spaceDog.size.width max:self.frame.size.width - spaceDog.size.width - 10]; // between the left edge and right edge of the screen. left side (min) has 10 point margin.

    spaceDog.position = CGPointMake(x, y);
    [self addChild:spaceDog];

//    Hared Coded add 2 spaceDogs

//    SpaceDogNode *spaceDogA = [SpaceDogNode spaceDogOfType:SpaceDodTypeA];
//    spaceDogA.position = CGPointMake(100, 300);
//    [self addChild:spaceDogA];
//    
//    SpaceDogNode *spaceDogB = [SpaceDogNode spaceDogOfType:SpaceDogTypeB];
//    spaceDogB.position = CGPointMake(200, 300);
//    [self addChild:spaceDogB];
}

//  add spaceDogs depending on how many seconds have passed since the last spaceDog was added
- (void) update:(NSTimeInterval)currentTime {
    if ( self.lastUpdateTimeInterval) {
        self.timeSinceEnemyAdded += currentTime - self.lastUpdateTimeInterval;
        self.totalGameTime += currentTime - self.lastUpdateTimeInterval;
    }
    //  add a space dog if one hasn't been added in the last addEnemyTimeInterval in seconds
    if (self.timeSinceEnemyAdded > self.addEnemyTimeInterval ) {
        [self addSpaceDog];
        self.timeSinceEnemyAdded = 0;
    }
    //    set the last update to the current time
    self.lastUpdateTimeInterval = currentTime;

//    Increase the difficulty over time
    //    if 480 second or 8 minutes have passed
    if (self.totalGameTime > 480) {
        self.addEnemyTimeInterval = 0.50;
        self.minSpeed = -160;
        //        4 minutes
    } else if ( self.totalGameTime > 240 ) {
        self.addEnemyTimeInterval = 0.65;
        self.minSpeed = -150;
        //        2 minutes
    } else if ( self.totalGameTime > 20 ) {
        self.addEnemyTimeInterval = .75;
        self.minSpeed = -125;
        //        30 seconds
    } else if ( self.totalGameTime > 10 ) {
        self.addEnemyTimeInterval = 1.00;
        self.minSpeed = -100;
    }

}

- (void) didBeginContact:(SKPhysicsContact *)contact {

        SKPhysicsBody *firstBody, *secondBody;

//    check which categoryBitMask is bigger by comparing enemy = 0010 to projectile 0000
    if ( contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask ) {
        firstBody = contact.bodyA;
        secondBody = contact.bodyB;
    } else {
        firstBody = contact.bodyA;
        secondBody = contact.bodyB;
    }


    if ( firstBody.categoryBitMask == collisionCategoryEnemy && secondBody.categoryBitMask == collisionCategoryProjectile ) {
        NSLog(@"BAM!");

        SpaceDogNode *spaceDog = (SpaceDogNode *)firstBody.node;
        ProjectileNode *projectile = (ProjectileNode *)secondBody.node;

//        SFX - Explode
        [self runAction:self.explodeSFX];

//      if the projectile and spaceDog collide, remove them from the scene
        [spaceDog removeFromParent];
        [projectile removeFromParent];


    } else if (firstBody.categoryBitMask == collisionCategoryEnemy && secondBody.categoryBitMask == collisionCategoryGround ) {
        NSLog(@"Hit Ground");

//        SFX - Damage
        [self runAction:self.damageSFX];

        //        remove spaceDog if it hits the ground
        SpaceDogNode *spaceDog = (SpaceDogNode *)firstBody.node;
        [spaceDog removeFromParent];

    }

    [self createDebrisAtPosition:contact.contactPoint];

}

//  debris
- (void) createDebrisAtPosition:(CGPoint)position {
    NSInteger numberOfPieces = [Util randomWithMin:5 max:20];


    for (int i=0; i < numberOfPieces; i++) {
        NSInteger randomPiece = [Util randomWithMin:1 max:4];
        NSString *imageName = [NSString stringWithFormat:@"debri_%d",randomPiece];

        SKSpriteNode *debris = [SKSpriteNode spriteNodeWithImageNamed:imageName];
        debris.position = position;
        [self addChild:debris];

        debris.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:debris.frame.size];
        debris.physicsBody.categoryBitMask = collisionCategoryDebris;
        debris.physicsBody.contactTestBitMask = 0;
        debris.physicsBody.collisionBitMask = collisionCategoryGround | collisionCategoryDebris;
        debris.name = @"debris";

        //     scatter debris on explosion contact
        debris.physicsBody.velocity = CGVectorMake([Util randomWithMin:-150 max:150],
                                                   [Util randomWithMin:150 max:350]);

        [debris runAction:[SKAction waitForDuration:2.0] completion:^{
            [debris removeFromParent];
        }];

    }
}

//-(void) update:(NSTimeInterval)currentTime {
//    
//    NSLog(@"%f",fmod(currentTime, 60));
//}



@end

And here is the full groundNode.m. I commented out the bodyWithRectangleOfSize line and have setupPhysicsBody, but even when I comment out setupPhysicsBody, and uncomment bodyWithRectangleOfSize, The ground still does not appear.

//
//  GroundNode.m
//  Space Cat
//
//  Created by Jared Watkins on 12/17/14.
//  Copyright (c) 2014 Jared Watkins. All rights reserved.
//

#import "GroundNode.h"
#import "Util.h"

@implementation GroundNode

+ (instancetype) groundWithSize:(CGSize)size {
    GroundNode *ground = [self spriteNodeWithColor:[SKColor greenColor] size:size];
    ground.name = @"ground";
//    ground.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:size];
    ground.position = CGPointMake(size.width/2, size.height/2);
    [ground setupPhysicsBody];

    return ground;
}

- (void) setupPhysicsBody {
    self.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.frame.size];
    self.physicsBody.affectedByGravity = NO;
    self.physicsBody.dynamic = NO;
//    collisions
    self.physicsBody.categoryBitMask = collisionCategoryGround;
    self.physicsBody.collisionBitMask = collisionCategoryDebris;
    self.physicsBody.contactTestBitMask = collisionCategoryEnemy;

}

@end

well that looks correct. not too sure why its not working. in your ground class try setting the position to something static:

ground.position = CGPointMake(200, 200);

maybe its an issue with the size being passed in or something

ok, I see that the ground is mid screen because of the exploding debris, but it still doesn't appear.

Here's a screen shot: Imgur

The bigger problem is that the spaceDogs pass through the ground. it's like there are 5 space dogs stacked and only the top one is destroyed. the same thing happens with the projectiles when they contact the spaceDogs. Also the NSLog(@"Hit Ground"); does not get triggered or show up in the console. and the SFX for enemy hitting the ground is not triggered.

I won't be able to count lives for when the spaceDogs hit the ground.

An ideas?

did you uncomment the line that you commented out earlier

+ (instancetype) groundWithSize:(CGSize)size {
    GroundNode *ground = [self spriteNodeWithColor:[SKColor greenColor] size:size];
    ground.name = @"ground";
//    ground.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:size];
    ground.position = CGPointMake(size.width/2, size.height/2);
    [ground setupPhysicsBody];

    return ground;
}

I have it like this:

+ (instancetype) groundWithSize:(CGSize)size {
    GroundNode *ground = [self spriteNodeWithColor:[SKColor greenColor] size:size];
    ground.name = @"ground";
//    ground.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:size];
    ground.position = CGPointMake(size.width/2, size.height/2);
    [ground setupPhysicsBody];

    return ground;
}

un comment that line:

+ (instancetype) groundWithSize:(CGSize)size {
    GroundNode *ground = [self spriteNodeWithColor:[SKColor greenColor] size:size];
    ground.name = @"ground";
    ground.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:size];
    ground.position = CGPointMake(size.width/2, size.height/2);
    [ground setupPhysicsBody];

    return ground;
}

ok, It's not appearing, and I can't tell an change.

Screenshot Imgur

//
//  GroundNode.m
//  Space Cat
//
//  Created by Jared Watkins on 12/17/14.
//  Copyright (c) 2014 Jared Watkins. All rights reserved.
//

#import "GroundNode.h"
#import "Util.h"

@implementation GroundNode

+ (instancetype) groundWithSize:(CGSize)size {
    GroundNode *ground = [self spriteNodeWithColor:[SKColor greenColor] size:size];
    ground.name = @"ground";
    ground.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:size];
    ground.position = CGPointMake(size.width/2, size.height/2);
    [ground setupPhysicsBody];

    return ground;
}

- (void) setupPhysicsBody {
    self.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.frame.size];
    self.physicsBody.affectedByGravity = NO;
    self.physicsBody.dynamic = NO;
//    collisions
    self.physicsBody.categoryBitMask = collisionCategoryGround;
    self.physicsBody.collisionBitMask = collisionCategoryDebris;
    self.physicsBody.contactTestBitMask = collisionCategoryEnemy;

}

@end

hmm I really dont understand why its not green. it should be green. its not making much sense

Well, I'm glad it's not just me. Thanks for your help, nonetheless.

can you post your groundNode header file real quick

sure

groundNode.h

//
//  GroundNode.h
//  Space Cat
//
//  Created by Jared Watkins on 12/17/14.
//  Copyright (c) 2014 Jared Watkins. All rights reserved.
//

#import <SpriteKit/SpriteKit.h>

@interface GroundNode : SKSpriteNode

+ (instancetype) groundWithSize:(CGSize)size;

@end

alright that looks fine. was thinking maybe you inherited from the wrong class but thats fine

I think there is a problem in GamePlayScene.m because The NSLog(@"Hit Ground"); is not being logged in the console. However, NSLog(@"BAM!"); is being logged.

- (void) didBeginContact:(SKPhysicsContact *)contact {

        SKPhysicsBody *firstBody, *secondBody;

//    check which categoryBitMask is bigger by comparing enemy = 0010 to projectile 0000
    if ( contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask ) {
        firstBody = contact.bodyA;
        secondBody = contact.bodyB;
    } else {
        firstBody = contact.bodyA;
        secondBody = contact.bodyB;
    }


    if ( firstBody.categoryBitMask == collisionCategoryEnemy && secondBody.categoryBitMask == collisionCategoryProjectile ) {
        NSLog(@"BAM!");

        SpaceDogNode *spaceDog = (SpaceDogNode *)firstBody.node;
        ProjectileNode *projectile = (ProjectileNode *)secondBody.node;

//        SFX - Explode
        [self runAction:self.explodeSFX];

//      if the projectile and spaceDog collide, remove them from the scene
        [spaceDog removeFromParent];
        [projectile removeFromParent];


    } else if (firstBody.categoryBitMask == collisionCategoryEnemy && secondBody.categoryBitMask == collisionCategoryGround ) {
        NSLog(@"Hit Ground");

//        SFX - Damage
        [self runAction:self.damageSFX];

        //        remove spaceDog if it hits the ground
        SpaceDogNode *spaceDog = (SpaceDogNode *)firstBody.node;
        [spaceDog removeFromParent];

    }

    [self createDebrisAtPosition:contact.contactPoint];

}

I haven't gotten to the collision stuff in this app yet but I had the same ground not appearing thing and the only way i made it appear was that I set the zPosition property to 1.0. I'm not sure how this would impact the collision detection (you probably have to make all the dogNodes on zPosition 1.0 as well

i have same problem !! any one can help us ?

The problem because of BackGround try to comment

 SKSpriteNode * background =[SKSpriteNode spriteNodeWithImageNamed:@"background_1"];
    background.position=CGPointMake(self.size.width/2, self.size.height/2);
        [self addChild:background]; 

and look what happen

I had the same issue and tweaked the zPosition for both background and ground, as Brittany Stubbs suggested though I had to use the opposite settings... on THGamePlayScene.m I set background.zPosition = 1;

and then I set ground.zPosition = 15;

Good luck!