lisovaccaro
lisovaccaro

Reputation: 33956

Check if player is on top of another node?

I'm trying to allow my player to jump. However I cannot let him jump in mid-air so I need to check if it's standing on top of another node before applying an impulse.

How do other sprite-kit games normally do this?


Right now I'm trying two approaches, the first one is this one. It's great however I don't know how to tell if the node the player is touching is really below or to the side of the player.

override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
    var allowJump = false
    let bodies = player.physicsBody.allContactedBodies()
    for body : AnyObject in bodies {
        if let node = body.node {
            allowJump = true // Player is touching another node but we don't know where (the bottom of the player or it's sides)
        }
    }
}

This is the other approach, it's effective to tell if a contact started and also whether the contact is really with the ground (below the player) and not a wall or such. However I don't know how to tell when the player stops being on top of any nodes, because even if a contact ends he might still be contacting another node.

func didBeginContact(contact: SKPhysicsContact!){
    if(contact.bodyA.node.position.y < contact.contactPoint.y) {
        println("BodyA is on top of another node")
    }

    if(contact.bodyB.node.position.y < contact.contactPoint.y) {
        println("BodyB is on top of another node")
    }
}

func didEndContact(contact: SKPhysicsContact!){
     if(contact.bodyA.node.position.y < contact.contactPoint.y) {
        println("BodyA is on no longer on top of BodyB")
    }

    if(contact.bodyB.node.position.y < contact.contactPoint.y) {
        println("BodyB is no longer on top of BodyA")
    }
}

Upvotes: 7

Views: 2145

Answers (5)

Most Wanted
Most Wanted

Reputation: 7019

Update for Swift 5

guard let bodies = player.physicsBody?.allContactedBodies() else { return }
for body: SKPhysicsBody in bodies {
    if let node = body.node {
        if node.name == "ground" {
            isJumping = false
        }
    }
}

Upvotes: 1

jverrijt
jverrijt

Reputation: 696

Add another node at the bottom of your player node (feet if you will) and use that for detecting collisions. Make this 'feet node' slightly narrower than your player so it won't trigger side collisions. When the feet have contacting bodies other than perhaps the player itself, you know it is standing on something.

Upvotes: 0

Ahmet Hayrullahoglu
Ahmet Hayrullahoglu

Reputation: 960

typedef  NS_OPTIONS(uint32_t, CNPhysicsCategory)
{
    CNPhysicsCategoryChick = 1,
    CNPhysicsCategoryEdge = 2,
    CNPhysicsCategoryStrawberry = 3,
};

@interface MyScene() <SKPhysicsContactDelegate>

@end


@implementation MyScene
{
    SKSpriteNode *strawberry;
    SKSpriteNode *chick;

}

-(id)initWithSize:(CGSize)size {    
    if (self = [super initWithSize:size]) {
        /* Setup your scene here */

        self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
        self.physicsBody.categoryBitMask = CNPhysicsCategoryEdge;
        self.physicsWorld.contactDelegate = self;
        self.physicsBody.restitution = 0.0;

        chick = [SKSpriteNode spriteNodeWithImageNamed:@"chick"];
        [chick setScale:0.3];
        chick.position = CGPointMake(self.size.width/2, chick.size.height/2);
        chick.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:chick.size];
        chick.physicsBody.categoryBitMask = CNPhysicsCategoryChick;
        chick.physicsBody.collisionBitMask = CNPhysicsCategoryEdge;
        chick.physicsBody.restitution = 0.0;
        [self addChild:chick];


        strawberry = [SKSpriteNode spriteNodeWithImageNamed:@"strawberry"];
        strawberry.position = CGPointMake(chick.position.x, chick.position.y - chick.size.height/2 + strawberry.size.height/2);

        //You should make this hidden
        //strawberry.hidden = YES;

        strawberry.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize: CGSizeMake(strawberry.size.width, strawberry.size.height)];
        strawberry.physicsBody.categoryBitMask = CNPhysicsCategoryStrawberry;
        strawberry.physicsBody.collisionBitMask = kNilOptions;
        strawberry.physicsBody.contactTestBitMask = CNPhysicsCategoryEdge;
        strawberry.physicsBody.density = 0.001;
        strawberry.physicsBody.restitution = 0.0;
        [self addChild:strawberry];

        SKPhysicsJointFixed *point = [SKPhysicsJointFixed jointWithBodyA:strawberry.physicsBody bodyB:chick.physicsBody anchor:CGPointZero];
        [self.physicsWorld addJoint:point];
    }
    return self;
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

    [chick.physicsBody applyImpulse:CGVectorMake(0, 60)];
}

-(void)didBeginContact:(SKPhysicsContact *)contact
{
    uint32_t collision = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask);

    if (collision == (CNPhysicsCategoryEdge | CNPhysicsCategoryStrawberry)) {
        NSLog(@"In Contact");
    }
}

-(void)didEndContact:(SKPhysicsContact *)contact
{
    uint32_t collision = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask);

    if (collision == (CNPhysicsCategoryEdge | CNPhysicsCategoryStrawberry)) {
        NSLog(@"Contact Is Lost");
    }
}


@end

enter image description here enter image description here

Upvotes: 2

sps
sps

Reputation: 148

Use states to accomplish this.

Lets say ur player can be in two states - 1. touchingTheGround 2. inTheAir

  • Apply upward impulse on the player only if its state is touchingTheGround and change state to inTheAir
  • When the player touches the ground set its state to touchingTheGround. Also at this point you can check that the angle made by the line passing through centre of player node and point of contact makes a -90 degree(or around -90) angle with +x axis.(player landed on its feet)
  • If the player does not lands on its feet you can send the player to some other state and write different logic for that state.

To implement states I'll recommend using NS_ENUM

Upvotes: 0

lisovaccaro
lisovaccaro

Reputation: 33956

This is what I'm doing now however it doesn't let me know if the node it's touching is below/left/right. I could simply compare its position to my node however some nodes are hollow like edgeLoopFromPath or have different shapes.

override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
    var allowJump = false
    let bodies = player.physicsBody.allContactedBodies()
    for body : AnyObject in bodies {
        if let node = body.node { // Additionally check contactBitMask
            allowJump = true
        }
    }
}

Upvotes: 3

Related Questions