Problem with collision detection in Spritekit

Hello all,


I have some Swift source code where I want to object to bounce of eachother (see below extract). I can't manage to get the player bounce of the background, while I did set the collisionBitMask correctly I think. If I add the player to the foreGroundNode iso to the general scene, they do bounce of eachother. But for various reasons I would like to keep the player in a separate SKNode. Is there something wrong with this piece of code, making that the physicsobjects do not react correctly? Or is it not possible to make 2 different SKNodes collide with eachoter?


Kind regards


struct CollisionBitMask {
    static let playerCategory:UInt32 = 0x1 << 0
    static let foregroundCategory:UInt32 = 0x1 << 1
}


var foregroundNode: SKNode!
var foregroundNode2: SKNode!

var player: SKNode!

class GameScene: SKScene, SKPhysicsContactDelegate {
    override init(size: CGSize) {
        // Add some gravity
        physicsWorld.gravity = CGVector(dx: 0.0, dy: -5.0)
    
        // Set contact delegate
        physicsWorld.contactDelegate = self
    
        // Create foreground
        var positionForeGroundNode = CGPoint(x: 0, y:0);
        foregroundNode = createForegroundNode(position: positionForeGroundNode)
        foregroundNode.zPosition = 2
        addChild(foregroundNode)
    
        // Create player
        player = createPlayer()
        player.zPosition = 2
        addChild(player)
    
        // Set properties of physicsBody
        //player.physicsBody?.isDynamic = true
        player.physicsBody?.applyImpulse(CGVector(dx: 0.0, dy: 0.0))
    }


    func createPlayer() -> SKNode {
        let playerNode = SKNode()
        playerNode.position = CGPoint(x: self.size.width * 0.5, y: self.size.height * 0.5)
   
        let sprite = SKSpriteNode(imageNamed: "Run1.png")
        sprite.setScale(scaleFactorPlayer / 10.0)
        playerNode.addChild(sprite)
   
        // 1
        playerNode.physicsBody = SKPhysicsBody(circleOfRadius: sprite.size.width / 2)
        // 2
        playerNode.physicsBody?.isDynamic = true
        // 3
        playerNode.physicsBody?.allowsRotation = false
        // 4
        playerNode.physicsBody?.restitution = 1.0
        playerNode.physicsBody?.friction = 0.0
        playerNode.physicsBody?.angularDamping = 0.0
        playerNode.physicsBody?.linearDamping = 0.0
   
   
        playerNode.physicsBody?.categoryBitMask = CollisionBitMask.playerCategory
        playerNode.physicsBody?.collisionBitMask = CollisionBitMask.foregroundCategory
        playerNode.physicsBody?.contactTestBitMask = CollisionBitMask.foregroundCategory
   
        return playerNode
    }

    func createForegroundNode(position : CGPoint) -> SKNode {
        let foregroundNode = SKNode()
   
        let xSpacing = 32.0 * scaleFactorForeground
        for index in 0...19 {
            // 3
            let node = SKSpriteNode(imageNamed: "tile")
            // 4
            node.setScale(scaleFactorForeground / 4.0)
            node.anchorPoint = position
            node.position = CGPoint(x: xSpacing * CGFloat(index), y: 0)
            //5
            foregroundNode.addChild(node)
        }
        foregroundNode.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: self.size.width, height: self.size.height / 20.0))
        foregroundNode.physicsBody?.isDynamic = false
   
        foregroundNode.physicsBody?.categoryBitMask = CollisionBitMask.foregroundCategory
        foregroundNode.physicsBody?.collisionBitMask = CollisionBitMask.playerCategory
        foregroundNode.physicsBody?.contactTestBitMask = CollisionBitMask.playerCategory
   
   
        return foregroundNode
    }
}

Replies

You haven't specified that player nodes can also collide with each other (lines 58-60):


        playerNode.physicsBody?.categoryBitMask = CollisionBitMask.playerCategory 
        playerNode.physicsBody?.collisionBitMask = CollisionBitMask.foregroundCategory 
        playerNode.physicsBody?.contactTestBitMask = CollisionBitMask.foregroundCategory


This says that players can only collide with the foreground.

Hello Quincey, I only have one player so this doesn't solve anything. The thing I want is the player to bounce on the foreground. Kr

If you set your GameViewController to showsPhysics to true, when you view the foreground node does it actually have the body you think it does? Always worth checking to make sure that isn't the issue.

As suspected by CartoonSmart, your physics body for the tiles was the wrong shape. This is so often the cause of this kind of issue, therefore, you should always enable showsPhysics on yoru SKView during development. There are additional problems:


1) SKSpriteNode anchorPoint is not used in the manner in which you attempted. It's valid range is [0..1] and refers to its placement with respect to *its* coordinate system, not its parent's.


2) You attempted to create one physics body for all tiles, but creating a physics body for each tile is a better practice for scalability.


the updated code:


import SpriteKit
class GameScene: SKScene {

    struct CollisionBitMask {
        static let playerCategory:UInt32 = 0x1 << 0
        static let foregroundCategory:UInt32 = 0x1 << 1
    }

    var foregroundNode: SKNode!
    var foregroundNode2: SKNode!

    var player: SKNode!
    let scaleFactorPlayer: CGFloat = 10
    let scaleFactorForeground: CGFloat = 4

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

    override init( size: CGSize ) {
        super.init(size: size)      
    }

    override func didMove(to view: SKView) {
        super.didMove(to: view)     
        physicsWorld.gravity = CGVector(dx: 0.0, dy: -5.0)          
        let positionForeGroundNode = CGPoint(x: 0, y:0);
        foregroundNode = createForegroundNode(position: positionForeGroundNode)
        foregroundNode.zPosition = 2
        addChild(foregroundNode)            
        player = createPlayer()
        player.zPosition = 2
        addChild(player)                    
        player.physicsBody?.applyImpulse(CGVector(dx: 0.0, dy: 0.0))
     
    }

    func createPlayer() -> SKNode {
        let playerNode = SKNode()
        playerNode.position = CGPoint(x: self.size.width * 0.5, y: self.size.height * 0.5)     
        let sprite = SKSpriteNode( texture: nil, color: .blue, size: CGSize( width: 50, height: 50 ) )
        sprite.setScale( scaleFactorPlayer / 10.0)
        playerNode.addChild(sprite)             
        playerNode.physicsBody = SKPhysicsBody(circleOfRadius: sprite.size.width / 2)
        playerNode.physicsBody!.isDynamic = true
        playerNode.physicsBody!.allowsRotation = false
        playerNode.physicsBody!.categoryBitMask = CollisionBitMask.playerCategory
        playerNode.physicsBody!.collisionBitMask = CollisionBitMask.foregroundCategory     
        return playerNode
    }

    func createForegroundNode( position : CGPoint ) -> SKNode {
        let foregroundNode = SKNode()
        let tilesize: CGFloat = 50
        let origin = CGPoint( x: tilesize / 2.0, y: tilesize / 2.0 )
        let tilesWide = Int( UIScreen.main.bounds.size.width / tilesize )
        for index in 0...tilesWide {          
            var color: SKColor!
            if index % 2 == 0 { color = .brown } else { color = .green }
            let node = SKSpriteNode( texture: nil, color: color, size: CGSize( width: tilesize, height: tilesize ) )                      
            node.position = CGPoint(x: origin.x + position.x + node.size.width * CGFloat(index), y: origin.y + position.y + 0) /          
            node.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: node.size.width, height: node.size.height))
            node.physicsBody!.isDynamic = false
            node.physicsBody!.categoryBitMask = CollisionBitMask.foregroundCategory
            node.physicsBody!.collisionBitMask = CollisionBitMask.playerCategory
            foregroundNode.addChild( node )
        }
        return foregroundNode
    }
}