3

I am planning a platforming game for iOS using SpriteKit and Swift. I did some research on how to handle collisions of the player sprite and stumbled upon this article.

http://www.learn-cocos2d.com/2013/08/physics-engine-platformer-terrible-idea/

That article advices you not to use the build-in physics engine of SpriteKit but to implement things like moving, jumping and collision handling on your own. The platform tutorial on Ray Wenderlichs site suggests a similar approach.

So far so good but let's talk about floor tiles on which the player can stand upon. A custom physics implementation would be easy as long as the tiles are rectangular and have a flat surface (like in the tutorial from Ray Wenderlich), since you would use CGRectIntersectsRect to detect a collision. But how would you check the player collision on curved or sloped tiles? From what I have read, CGRectIntersectsRect does not account for tranparent pixel inside a sprites rect.

https://i.stack.imgur.com/lRP2Q.png

Look at the above tile for example. The white area (upper left) would be transparent. Now, if the player sprite would drop on this tile, the collision would be detected at the upper border of the tiles rectangle, although there are no ground pixels there (blue area, lower right). So ultimately the player would hover in mid-air above this tile. I could push the player sprite down a few pixels but that is a bit hacky and gets harder if the curved floor tiles have different slope angles.

So the question is, how can I handle these types of collision with SpriteKit alone (no extra frameworks like Cocos2D, Kobold Kit, ...)? Or is this approach entirely wrong and collisions in platformer with SpriteKit should be done fundamentally different?

Any help is very much appreciated!

sangony
  • 11,636
  • 4
  • 39
  • 55
snorge
  • 114
  • 9

2 Answers2

4

I disagree with not using physics to handle collisions and contacts. You are really just trying to reinvent the wheel here by not using physics and implementing your own custom code.

If you are using the Tiled app then assigning a physics body is a simple task. Use the Objects in Tiled to assign various body types. In your code you can then go about creating a specific body for each object type.

For example:

enter image description here

In the above image I have created a 45 degree right side sloped floor. The object I added is called floor45R.

The next step is to parse your map objects. In case of the 45floorR, you create a physics body like this:

NSArray *arrayObjects = [group objectsNamed:@"floor45R"];
    for (NSDictionary *dicObj in arrayObjects) {
        CGFloat x = [dicObj[@"x"] floatValue];
        CGFloat y = [dicObj[@"y"] floatValue];
        CGFloat w = [dicObj[@"width"] floatValue];
        CGFloat h = [dicObj[@"height"] floatValue];

        SKSpriteNode *myNode = [SKSpriteNode spriteNodeWithColor:[SKColor clearColor] size:CGSizeMake(w, h)];
        myNode.position = CGPointMake(x, y);
        myNode.zPosition = 100;
        CGMutablePathRef trianglePath = CGPathCreateMutable();
        CGPathMoveToPoint(trianglePath, nil, -myNode.size.width/2, myNode.size.height/2);
        CGPathAddLineToPoint(trianglePath, nil, myNode.size.width/2, -myNode.size.height/2);
        CGPathAddLineToPoint(trianglePath, nil, -myNode.size.width/2, -myNode.size.height/2);
        CGPathAddLineToPoint(trianglePath, nil, -myNode.size.width/2, myNode.size.height/2);
        myNode.physicsBody = [SKPhysicsBody bodyWithPolygonFromPath:trianglePath];
        myNode.physicsBody.dynamic = NO;
        myNode.physicsBody.restitution = 0;
        myNode.physicsBody.friction = 0.0;
        myNode.physicsBody.categoryBitMask = CategoryFloor45;
        myNode.physicsBody.collisionBitMask = 0x00000000;
        CGPathRelease(trianglePath);

        [worldNode addChild:myNode];
    }

This works for any degree floor slope. Remember to set your player's and any other node's collision bit mask to collide with the floor.

In order for your player move smoothly over sloped floor, I recommend building the player's physics body in 2 pieces. A circle at the bottom and a rectangle at the top. The circle will prevent getting stuck in any potential cracks caused by 2 joining physics bodies.

SKPhysicsBody *firstBody = [SKPhysicsBody bodyWithCircleOfRadius:10 center:CGPointMake(0, 0)];
SKPhysicsBody *secondBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(5, 50) center:CGPointMake(0, 10)];
self.physicsBody = [SKPhysicsBody bodyWithBodies:@[firstBody, secondBody]];

You will have to adjust the position and center coordinates to match your sprite's image.

sangony
  • 11,636
  • 4
  • 39
  • 55
  • Ok, this is interesting. I think I'm gonna try to use the build-in physics. Just another thought: Do you add all the physics bodies for all nodes when you load your map or do you dynamically assign the bodies when the nodes are about to enter the viewport (assuming your level is larger than the viewport and it is scrolling as you move)? I imagine that if you have a really big map, simulating all bodies (even those that are outside the viewport) could have a huge impact on the frame rate. Do you have any experience about the maximum number of bodies SpriteKit can handle before slowing down? – snorge May 13 '15 at 15:08
  • @snorge - The answer to your question is sorta a grey area. It all depends on what else is going on in your scene, how many other nodes, physics bodies, actions, etc... You can removeFromParent physics bodies which are off-screen but that again depends on your game code. For example, if you have an enemy still walking around, removing the floor would make him fall into oblivion. As for the max number of nodes, again it depends on what else is going on. You will have to run several tests and see what suits your code best. – sangony May 13 '15 at 18:58
  • Alright, I will give it a try and see for myself. Thank you very much, your answer gave me some insight and actually helped me a lot. – snorge May 13 '15 at 19:48
1

You can make use of a level editor, Tiled to handle curved and sloped floor tiles along with JSTileMap which processes the TMX map.

It will be able to able to handle contact with curved and sloped floor tiles but as for adjusting the angle of objects standing on top of those tiles, you would have to utilize some math functions, a good example is found here

You can still make use of SpriteKit's collision detection which will simplify things for you but create your own jumping or movement engine.

Community
  • 1
  • 1
Wraithseeker
  • 1,884
  • 2
  • 19
  • 34
  • As a matter of fact, I want to use Tiled to create the game levels. I took a look at it already but I don't see how it will help me with the collision handling on curved and sloped floors. After all, the map will just be loaded into SpriteKit with each tile being a rectangular SKSpriteNode. Thanks for pointing out the adjustment of the angle. I might add that later on, but currently my main concern is to be able to put the player on the ground, no matter what shape the floor tile is. – snorge May 13 '15 at 11:27
  • You can use Tiled Object editor and make use of the Insert Polygon and draw a triangle and then render the object into your game with a physicsBody – Wraithseeker May 13 '15 at 11:29
  • I see. While this would work, it involves a lot of manually drawing polygons. Also, changing or adding stuff to a level would become a rather tedious task, as I would have to redraw or reposition the polygons each time. Ideally I just would want to declare certain tiles as "floor" by setting some property on them and let the game worry about correct positioning of the player within each tile. Something like pixel-perfect collision. – snorge May 13 '15 at 12:51