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

Jared Watkins
Jared Watkins
10,756 Points

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

Brittany Stubbs
Brittany Stubbs
22,979 Points

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;
Jared Watkins
Jared Watkins
10,756 Points

This worked. Thanks Brittany!

Stone Preston
Stone Preston
42,016 Points

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

Jared Watkins
Jared Watkins
10,756 Points

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

 self.physicsWorld.gravity = CGVectorMake(0, -9.8);
 self.physicsWorld.contactDelegate = self;
Stone Preston
Stone Preston
42,016 Points

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;
Jared Watkins
Jared Watkins
10,756 Points

Thanks Stone. although, no change.

Stone Preston
Stone Preston
42,016 Points

can you post the full code you have in your gamePlayScene

Jared Watkins
Jared Watkins
10,756 Points

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
Stone Preston
Stone Preston
42,016 Points

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

Jared Watkins
Jared Watkins
10,756 Points

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?

Stone Preston
Stone Preston
42,016 Points

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;
}
Jared Watkins
Jared Watkins
10,756 Points

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;
}
Stone Preston
Stone Preston
42,016 Points

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;
}
Jared Watkins
Jared Watkins
10,756 Points

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
Stone Preston
Stone Preston
42,016 Points

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

Jared Watkins
Jared Watkins
10,756 Points

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

Stone Preston
Stone Preston
42,016 Points

can you post your groundNode header file real quick

Jared Watkins
Jared Watkins
10,756 Points

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
Stone Preston
Stone Preston
42,016 Points

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

Jared Watkins
Jared Watkins
10,756 Points

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

David Rynn
David Rynn
10,554 Points

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!