Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set up SceneKit collision detection

Tags:

swift

scenekit

Hello I have pored over the documentation and can not figure out how to set up collision detection in scene kit. Can some one please show an example. Please help I am very desperate to figure this out. Thank you!

Edit: Hello Thank you very much, I'm sorry I forgot to mention my project is in swift. No big deal I can translate my self for the most part.

I have the BitMasks working correctly as the objects collide and bounce with each other. However I can not seem to get the function to work

func physicsWorld(world: SCNPhysicsWorld, didBeginContact contact: SCNPhysicsContact){
    let contactMask = contact.nodeA.physicsBody!.categoryBitMask | contact.nodeB.physicsBody!.categoryBitMask
    if (contactMask == (CollisionBallCategory | CollisionTerminatorCategory)) {
        println("Collided")
    }
}

Looking at the documentation it appears i need to assign the scenes physics world delegate to this method somehow. I am not sure how to do that.

like image 359
T Neate Avatar asked Dec 09 '14 05:12

T Neate


1 Answers

The main things to get about collision detection in SceneKit:

  • it's based on bit masks, which together form a table.
  • a contact delegate is how you respond to collisions.

Making objects collide

For example, you might state a bit of game design in plain English like this:

Asteroids hit each other (and make smaller asteroids). Missiles should pass through each other, but destroy rockets and asteroids. Rockets shouldn't do anything to missiles (only the other way around), but if one gets too close to another or to an asteroid you are having a bad problem and you will not go to space today.

The first step to realizing that with collision detection is to codify that design in terms of which pairs interact. You can do this with a table:

         | Missile | Rocket | Asteroid
--------------------------------------
Missile  | No      | Yes    | Yes
Rocket   | No      | Yes    | Yes
Asteroid | No      | No     | Yes

Then you can turn the headers of the table into a set of category constants for use in your code.

typedef NS_OPTIONS(NSUInteger, CollisionCategory) {
    CollisionCategoryMissile    = 1 << 0,
    CollisionCategoryRocket     = 1 << 1,
    CollisionCategoryAsteroid   = 1 << 2,
};

missile.physicsBody.categoryBitMask = CollisionCategoryMissile;
rocket.physicsBody.categoryBitMask = CollisionCategoryRocket;
asteroid.physicsBody.categoryBitMask = CollisionCategoryAsteroid;

Use bitwise OR on these constants to create collisionBitMask values that fill in the table.

missile.physicsBody.collisionBitMask =
    CollisionCategoryRocket | CollisionCategoryAsteroid;
rocket.physicsBody.collisionBitMask =
    CollisionCategoryRocket | CollisionCategoryAsteroid;
asteroid.physicsBody.collisionBitMask = CollisionCategoryAsteroid;

That's all you need to make SceneKit resolve collisions for you (that is, bounce objects off each other).

Responding to collisions

If you also want to be notified of collisions (so you can make missiles blow stuff up, and running your ship into an asteroid end the game), you'll need to set a contact delegate on your scene's physics world and implement one or more of the contact delegate methods that get called when a contact happens.

In your contact delegate method (say, physicsWorld:didBeginContact:), you'll need to find out which categories of bodies were involved in the contact, and which was which, so you can get to your code that does whatever your game does for the collision:

- (void)physicsWorld:(SCNPhysicsWorld *)world didBeginContact:(SCNPhysicsContact *)contact
{
    CollisionCategory contactMask =
        contact.nodeA.physicsBody.categoryBitMask | contact.nodeB.physicsBody.categoryBitMask;

    // first, sort out what kind of collision
    if (contactMask == (CollisionCategoryMissile | CollisionCategoryRocket)) {
        // next, sort out which body is the missile and which is the rocket
        // and do something about it
        if (contact.nodeA.physicsBody.categoryBitMask == CollisionCategoryMissile) {
            [self hitRocket:contact.nodeB withMissile:contact.nodeA];
        } else {
            [self hitRocket:contact.nodeA withMissile:contact.nodeB];
        }
    } else if (contactMask == (CollisionCategoryMissile | CollisionCategoryAsteroid)) {
        // ... and so on ...
    }
}

Put this code in one of your classes (a view controller, maybe — wherever you keep your game logic is good), and make that class declare conformance to the SCNPhysicsContactDelegate protocol.

@interface ViewController: UIViewController <SCNPhysicsContactDelegate>

Then assign that object to your scene's physics world as the contact delegate:

// in initial setup, where presumably you already have a reference to your scene
scene.physicsWorld.contactDelegate = self

Learning more

There's a little bit about collision resolution in the SCNPhysicsBody reference documentation. And Apple has some sample code that uses collision detection — it's part of the smorgasbord of demos in the WWDC slides and demo sample apps, and in the vehicle physics demo, too.

Beyond that, SceneKit's collision handling model is almost exactly the same as SpriteKit's, so almost everything in the SpriteKit programming guide is also useful for understanding the same stuff in SceneKit.

like image 122
rickster Avatar answered Oct 04 '22 05:10

rickster