1

I want to reproduce Super-Mario for iOS using Swift and SpriteKit. I use SKPhysics-bodies to simulate collisions between the player and the environment. The player and all objects have their own SKPhysicsBody (rectangle of their size). But when the player jumps against a brick (from top left or top right) like this, the player stucks in the air.

My assumption is that when the two SKSpriteNodes collide, they overlap a tiny bit. And the SKPhysics-Engine thinks that the player is on top of the middle brick because the player is a tiny bit inside the brick above and falls down on the middle brick.

So my question is: How can i prevent SKSPriteNodes from overlapping? Or how can i fix this?

P.S. If you need more information to answer, please tell me!

EDIT: Some Code of the Brick Class:

import SpriteKit

class Brick: SKSpriteNode {

    let imgBrick = SKTexture(imageNamed: "Brick")

    init(let xCoor: Float, let yCoor: Float) {
        // position is a global variable wich is 1334/3840
        super.init(texture: imgBrick, color: UIColor.clearColor(), size: CGSize(width: 80*proportion, height: 80*proportion))
        position = CGPoint(x:Int(xCoor*667)+Int(40*proportion), y:Int(375-(yCoor*375))-Int(40*proportion))
        name = "Brick"
        physicsBody = SKPhysicsBody(rectangleOfSize: self.size)
        physicsBody?.affectedByGravity = false
        physicsBody?.dynamic = false
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

and my Player Class

import SpriteKit

class Mario: SKSpriteNode {

    let atlas = SKTextureAtlas(named: "Mario")
    var imgMario = [SKTexture]()
    var action = SKAction()

    init(let xCoor: Float, let yCoor: Float) {
        for(var i = 1; i < 24; i++) {
           imgMario.append(atlas.textureNamed("X\(i)"))
        }

        super.init(texture: imgMario[0], color: UIColor.clearColor(), size: CGSize(width: 120*proportion, height: 160*proportion))
        position = CGPoint(x:Int(xCoor)+Int(60*proportion), y:Int(yCoor)-Int(90*proportion))
        name = "Mario"
        physicsBody = SKPhysicsBody(rectangleOfSize: self.size)
        physicsBody?.allowsRotation = false

        action = SKAction.repeatActionForever(SKAction.animateWithTextures(imgMario, timePerFrame: 0.03, resize: false, restore: true))
        runAction(action, withKey: "walking")
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

and SOME PART of my Level Class

class Level: SKScene, SKPhysicsContactDelegate {

    var motionManager = CMMotionManager()

    var mario = Mario(xCoor: 100, yCoor: 90)
    var world = SKSpriteNode()

    let MarioCategory: UInt32 = 0x1 << 0
    let BrickCategory: UInt32 = 0x1 << 1

    /*
    Some more code...
    */

    func setUpPhysics() {
        physicsWorld.contactDelegate = self
        mario.physicsBody!.categoryBitMask = marioCategory
        mario.physicsBody?.collisionBitMask = brickCategory
        mario.physicsBody?.contactTestBitMask = brickCategory

        for(var i = 0; i < world.children.count; i++) {
            if(world.children[i].name == "Brick") {
                world.children[i].physicsBody?.categoryBitMask = brickCategory
                world.children[i].physicsBody?.collisionBitMask = marioCategory
                world.children[i].physicsBody?.contactTestBitMask = marioCategory
                world.children[i].physicsBody?.friction = 0
            }
        }
    }
}
Yakuhzi
  • 969
  • 6
  • 20
  • are the bricks all tiles.. are they multiple physics bodies or one large physics body? – hamobi Dec 29 '15 at 23:38
  • Check if the physics bodies are arranged how they are supposed to, if you don't know how to do it, read [this answer](http://stackoverflow.com/a/21295318/3462308). If the bodies are ok, try setting `block.physicsBody!.friction = 0` to the blocks – alephao Dec 29 '15 at 23:46
  • Please provide code so the issue can be properly assessed. – Jonathan H. Dec 30 '15 at 00:03
  • all bricks have their own (ever brick has one) physics body. I checked the arrangement before and it seems all correct see here: [link](http://i.stack.imgur.com/OEWup.png) (I removed the left brick!) Unfortunately .physicsBody!.friction = 0 didn't help. The player still stucks. – Yakuhzi Dec 30 '15 at 00:08
  • try making your players physics body a circle – hamobi Dec 30 '15 at 08:54
  • thanks for your reply, i changed the physics body of the player to an ellipse which fits better to the Mario size: physicsBody = SKPhysicsBody(polygonFromPath: CGPathCreateWithEllipseInRect(CGRectMake(-size.width/2, -size.height/2, size.width, size.height), nil)). This solves the problem but it lead to new problems: if i want to climb a stair like this: [link](http://i.stack.imgur.com/SxDyh.png), the player slides down to the ground. – Yakuhzi Dec 30 '15 at 12:50
  • no need to make them a circle, use edgeloop when creating your wall / floor physics objects, not rectangle – Knight0fDragon Dec 31 '15 at 02:22
  • to handle stairs. always have mario walk in a tiny direction upwards, gravity should keep him visually down – Knight0fDragon Dec 31 '15 at 02:22
  • ....nevermind, wrong kind of stairs. the edgeloop should fix that problem – Knight0fDragon Dec 31 '15 at 02:31

1 Answers1

0

Try setting UsePreciseCollisionDetection to true on your physicsbody.

mario.physicsBody?.usesPreciseCollisionDetection = true

https://developer.apple.com/reference/spritekit/skphysicsbody has some examples.

By setting this property to true could cause it to detect collisions more accurately so only when you mean to. I'd also rethink giving each block its own physics body. You only really care about the edge of a group of blocks rather than the edges of each individual block.

Another suggestion is that you can create a physics body based on a texture. So if your SKSpriteNode has a complex texture with transparent bits you can create another texture that is just a simple black block or similar this will help the engine detect more accurate collisions.

MattjeS
  • 1,367
  • 18
  • 35