collision not detected between SKSpitekit nodes

183 views Asked by At

I am building a maze and I have added some SKSpritekit nodes for the walls and a dot for the player. however when the dot and the walls collide, there is no detection of collision. my code is as follows:

import UIKit
import SpriteKit
import GameplayKit
import Foundation
import GameplayKit


class level1: SKScene, SKPhysicsContactDelegate {
    var entities = [GKEntity]()
    var graphs = [String : GKGraph]()
    var dot = SKSpriteNode()

override func sceneDidLoad () {
    buildMaze()
    addDot()

    func addDot() {
    let startNum = x * (y - 1)
    startCoord = coordArray[startNum]
    dot = SKSpriteNode(imageNamed: "redDot")
    dot.physicsBody?.isDynamic = true
    dot.size = CGSize(width: 20, height: 20)
    dot.position = startCoord
    dot.physicsBody = SKPhysicsBody(circleOfRadius: 10)
    dot.physicsBody?.mass = 0
    dot.physicsBody?.usesPreciseCollisionDetection = true
    self.addChild(dot)
}

 func buildMaze() {
    let difference =  coordArray[1].x - coordArray[0].x 
    let wallDistance = difference/2
    let thickness = CGFloat(3)
    let length = difference  - CGFloat(thickness)/2



    var count = 0
    for point in coordArray {

        let northWall = SKSpriteNode(color: SKColor.black, size : CGSize (width: length, height: thickness))
        northWall.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: length, height: thickness))
        northWall.physicsBody?.mass = 200000

        let southWall = SKSpriteNode(color: SKColor.black, size : CGSize (width: length, height: thickness))
        southWall.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: length, height: thickness))
        southWall.physicsBody?.mass = 200000

        let eastWall = SKSpriteNode(color: SKColor.black, size : CGSize (width: thickness, height: length ))
        eastWall.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: thickness, height: length))
        eastWall.physicsBody?.mass = 200000

        let westWall = SKSpriteNode(color: SKColor.black, size : CGSize (width: thickness, height: length ))
        westWall.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: thickness, height: length))
        westWall.physicsBody?.mass = 200000


        if !instructions[count].contains("N")  {
            //print("added north wall")
            northWall.position = CGPoint (x: point.x , y: point.y + wallDistance)
            if nodes(at: northWall.position) == [] {

                addChild(northWall)}
            else {print("north wall already there")}

        }
        if !instructions[count].contains("S")  {
            //print("added south wall")
            southWall.position = CGPoint (x: point.x , y: point.y - wallDistance)
            if nodes(at: southWall.position) == [] {

                addChild(southWall)}
            else {//print("southwall already there")

            }

        }
        if !instructions[count].contains("E")  {
            //print("added east wall")
            eastWall.position = CGPoint (x: point.x + wallDistance , y: point.y)
            if nodes(at: eastWall.position) == [] {

                addChild(eastWall)}
            else {//print("east already there")

            }

        }
        if !instructions[count].contains("W")  {
            //print("added west wall")
            westWall.position = CGPoint (x: point.x - wallDistance , y: point.y)
            if nodes(at: westWall.position) == [] {

                addChild(westWall)}
            else {//print("west wall already there")

            }

        }
        count = count + 1

    }
}


    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        for t in touches {
            let location = t.location(in: self)
            dot.position.x = location.x
            dot.position.y = location.y
        }
    }

func didBegin(_ contact: SKPhysicsContact) {
    print("contact!")


}

The walls appear just as I wanted them, and the dot is also in the right position.

I added a masso of 20000 for each of them so that when I move the dot, the walls stay in place. however, when I move the dot with my finger, it just goest straight through the walls of the maze instead of being stopped by them.

I added a print statement to the didBegin function to see if at least it was detecting any contact between the sprites, but it does not.

Why is this?

cheers!

2

There are 2 answers

0
Andrew21111 On BEST ANSWER

First in your didMoveTo or sceneDidLoad, you need to set the physicsContactDelegate:

override func sceneDidLoad () {
    physicsWorld.contactDelegate = self
    buildMaze()
    addDot()
}

To set the contact/collision mask, you have to do it this way, because they're based on bitwise operation:

Let's suppose you want collision between dot and walls

struct PhysicsCategory {
    static let wall: UInt32 = 0x1 << 1
    static let dot: UInt32 = 0x1 << 2
}

You can put the struct above you class if you want

Then, when you assign physics body, you have to set the bitmask:

For dots:

    dot.physicsBody?.categoryBitMask = PhysicsCategory.dot
    dot.physicsBody?.contactTestBitMask = PhysicsCategory.wall
    dot.physicsBody?.collisionBitMask = PhysicsCategory.wall 

    //Collision is different from contact, so if you want to avoid collision
    //dot.physicsBody?.collisionBitMask = PhysicsCategory.dot 

Collision is different from contact, check apple documentation about it

For walls:

    northWall.physicsBody?.categoryBitMask = PhysicsCategory.wall
    northWall.physicsBody?.contactTestBitMask = PhysicsCategory.dot
    northWall.physicsBody?.collisionBitMask = PhysicsCategory.dot

    //Do the same for all walls

If you want walls to contact or collide with more than one object:

    northWall.physicsBody?.contactTestBitMask = PhysicsCategory.dot | PhysicsCategory.other
    northWall.physicsBody?.collisionBitMask = PhysicsCategory.dot | PhysicsCategory.other

For walls are valid all consideration as per dots

3
Steve Ives On

Collision and contact detection don't work very well when you set the position of your sprites directly.

In your code, the lines:

        dot.position.x = location.x
        dot.position.y = location.y

are directly setting the position of dot, overriding anything that the physics engine wants to do with the objects.

Also, you don't appear to have set up any of the necessary categories or collision/contactTest bit masks.

You allow manual movement of the dot but with contact detection with the walls, then you'd probably need to see if the touch onthe screen was inside a wall and then not move the dot if that was the case. (which would mean that you are not using physics at all).

Edit: my step-by-step guide for collisions and contacts: https://stackoverflow.com/a/51041474/1430420

And a guide to collision and contactTest bit masks: https://stackoverflow.com/a/40596890/1430420