I wanted to have a collision detection between the avatar and the obstacle, so whenever something collides, it should print "collision", but even that doesn't work, so the problem is, that it doesn't detect any collision. And if it would collide, it should differentiate between the player and the obstacle.
class GameScene: SKScene, SKPhysicsContactDelegate {
let avatar = SKShapeNode(circleOfRadius: 20)
let avatarCategory: UInt32 = 0*1 << 0
let obstacleCategory: UInt32 = 0*1 << 1
override func didMove(to view: SKView) {
physicsWorld.contactDelegate = self
createAvatar()
spawnObstacles()
}
func createAvatar() {
avatar.name = "avatarNode"
avatar.physicsBody = SKPhysicsBody()
avatar.physicsBody?.categoryBitMask = avatarCategory
avatar.physicsBody?.contactTestBitMask = avatarCategory
avatar.physicsBody?.collisionBitMask = 0
avatar.physicsBody?.usesPreciseCollisionDetection = true
avatar.physicsBody?.affectedByGravity = false
avatar.zPosition = 2
addChild(avatar)
}
func createRandomObstacle() {
let obstacle = SKShapeNode()
obstacle.name = "obstacleNode"
obstacle.physicsBody = SKPhysicsBody()
obstacle.physicsBody?.categoryBitMask = obstacleCategory
obstacle.physicsBody?.contactTestBitMask = obstacleCategory
obstacle.physicsBody?.collisionBitMask = 0
obstacle.physicsBody?.usesPreciseCollisionDetection = true
obstacle.physicsBody?.affectedByGravity = false
obstacle.zPosition = 2
addChild(obstacle)
}
func didBegin(_ contact: SKPhysicsContact) {
print("collision")
}
To start with, both avatarCategory and obstacleCategory are 0, because : UInt32 = 0*1 << 1 = 0, so let’s fix that:
let avatarCategory: UInt32 = 1 << 0
let obstacleCategory: UInt32 = 1 << 1
Now contactTestBitMask represents the object(s) you want to be notified about contacts with, so you need to change that:
avatar.physicsBody?.contactTestBitMask = obstacleCategory
and
obstacle.physicsBody?.contactTestBitMask = avatarCategor
Try that for now 😀
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
Related
So Here I have this code
if collision == PhysicsCategory.Cat | PhysicsCategory.Bed {
print("SUCCESS")
win()
if currentLevel < 10 {
currentLevel += 1
}
And basically this statement works for every single level that I have .
I want to make it like this for only one level
if collision == PhysicsCategory.Cat | PhysicsCategory.Bed| PhysicsCategory.Bottle {
print("SUCCESS")
win()
if currentLevel < 10 {
currentLevel += 1
}
But If I will use it it will mean that the collision with bottle will be needed in every single level , however I want to use it only in one level
I am using swift
thank you!
FOR #Mundi
Here is code in GameScene
import SpriteKit
Game scene
protocol EventListenerNode {
func didMoveToScene()
}
protocol InteractiveNode {
func interact()
}
struct PhysicsCategory {
static let None: UInt32 = 0
static let Cat: UInt32 = 0b1 // 1
static let Block: UInt32 = 0b10 // 2
static let Bed: UInt32 = 0b100 // 4
static let Edge: UInt32 = 0b1000 // 8
static let Label: UInt32 = 0b10000 // 16
static let Spring:UInt32 = 0b100000 //32
static let Hook: UInt32 = 0b1000000 //64
static let Bottle: UInt32 = 0b10000000 //64
}
class GameScene: SKScene, SKPhysicsContactDelegate {
var bedNode: BedNode!
var catNode: CatNode!
var lineNode: LineNode!
var bottleNode: BottleNode!
var desiredCollision = PhysicsCategory.Cat | PhysicsCategory.Bed
var playable = true
override func didMove(to view: SKView) {
// Calculate playable margin
let maxAspectRatio: CGFloat = 16.0/9.0
let maxAspectRatioHeight = size.width / maxAspectRatio
let playableMargin: CGFloat = (size.height - maxAspectRatioHeight)/2
let playableRect = CGRect(x: 0, y: playableMargin,
width: size.width, height: size.height-playableMargin*2)
physicsBody = SKPhysicsBody(edgeLoopFrom: playableRect)
physicsWorld.contactDelegate = self
physicsBody!.categoryBitMask = PhysicsCategory.Edge
enumerateChildNodes(withName: "//*", using: { node, _ in
if let eventListenerNode = node as? EventListenerNode {
eventListenerNode.didMoveToScene()
}
})
bedNode = childNode(withName: "bed") as! BedNode
catNode = childNode(withName: "//cat_body") as! CatNode
bottleNode = childNode(withName: "bottle") as! BottleNode
func didBegin(_ contact: SKPhysicsContact) {
let collision = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
if collision == PhysicsCategory.Label | PhysicsCategory.Edge {
let labelNode = contact.bodyA.categoryBitMask == PhysicsCategory.Label ?
contact.bodyA.node :
contact.bodyB.node
if let message = labelNode as? MessageNode {
message.didBounce()
}
}
if !playable {
return
}
if currentLevel == 20 { // or whatever level you want
desiredCollision |= PhysicsCategory.Bottle
}
if collision == desiredCollision {
print ("Win")
win()
} else if collision == PhysicsCategory.Cat | PhysicsCategory.Edge {
print("FAIL")
lose()
and here is the code from my BottleBode
import SpriteKit
class BottleNode: SKSpriteNode, EventListenerNode {
func didMoveToScene() {
print("bottle added to scene")
let bedBodySize = CGSize(width: 40.0, height: 30.0)
physicsBody = SKPhysicsBody(rectangleOf: bedBodySize)
physicsBody!.isDynamic = true
physicsBody!.categoryBitMask = PhysicsCategory.Bottle
physicsBody!.collisionBitMask = PhysicsCategory.Block | PhysicsCategory.Edge | PhysicsCategory.Spring | PhysicsCategory.Spring
parent!.physicsBody!.contactTestBitMask = PhysicsCategory.Bed | PhysicsCategory.Edge
}
}
I did as you told me however it still doesn't work, and when the CatNode touches the ground I have lose condition and I am receiving an error.
Here is a picture of how it doesn't work
https://i.stack.imgur.com/0JrMJ.png
and here is an error.
https://i.stack.imgur.com/8VGRd.png
Could you please tell me what wrong ?
Thank you so much
Possible without a special variable and more succinctly:
var desiredCollision = PhysicsCategory.Cat | PhysicsCategory.Bed
if currentLevel == 7 { // or whatever level you want
desiredCollision |= PhysicsCategory.Bottle
}
if collision = desiredCollision {
// ...
You could use nested if-else statements to first check if it's the special level, then decide which version to use based on the result of that. It would look something like this:
var useSpecialCollision: bool // Change this to true/false based on when the level changes
if useSpecialCollision == true {
if collision == PhysicsCategory.Cat | PhysicsCategory.Bed| PhysicsCategory.Bottle {
print("SUCCESS")
win()
if currentLevel < 10 {
currentLevel += 1
}
}
} else {
if collision == PhysicsCategory.Cat | PhysicsCategory.Bed {
print("SUCCESS")
win()
if currentLevel < 10 {
currentLevel += 1
}
}
}
This puts your if statements/code into another if statement to determine which code to run. Not very complicated... Not the neatest either...
So I want a game made, this was striped from the Xcode SpriteKit sampler, pretty simple. It will evolve greatly as I get this key issue out of the way. It has a player, Wall's, and a door. Nodes are assigned, player works fine. Wall's attempted for children in self, but crashes with my comments removed. I have a guess as multiple nodes of same name? But the door, when assigned node, for some reason no matter what slowly falls, with no gravity ticked and no gravity coded.
Those are lesser concerns. I come to you today to pick at why my collisions might not be activating my collision argument functions, to enter the house.
Yes I am aware it says contact mapped to the event. It suits my theory I am pretty sure.
//
// GameScene.swift
// Sandbox
//
// Created by M on 7/1/16.
// Copyright © 2016 M. All rights reserved.
//
import SpriteKit
import GameplayKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var entities = [GKEntity]()
var graphs = [GKGraph]()
private var lastUpdateTime : TimeInterval = 0
private var label : SKLabelNode?
var playerNode : SKSpriteNode?
var wallNode : SKSpriteNode?
var doorNode : SKSpriteNode?
private var spinnyNode : SKShapeNode?
var furnishing : SKSpriteNode?
var playerCategory = 0x1 << 0
var wallCategory = 0x1 << 1
var doorCategory = 0x1 << 2
var pathCategory = 0x1 << 3
func nextRoom() {
let sceneNode = SKScene(fileNamed: "MyScene")
sceneNode?.scaleMode = .aspectFill
// Present the scene
if let view = self.view {
view.presentScene(sceneNode)
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
func loadRoom() {
let furnishing = SKSpriteNode(color: #colorLiteral(red: 0.2464724183, green: 0.05352632701, blue: 0.03394328058, alpha: 1), size:CGSize(width:25, height:25))
doorNode?.addChild(furnishing)
}
func enterHouse() {
let newWindow = CGSize(width: 500, height: 500)
doorNode?.scale(to: newWindow)
loadRoom()
}
func exitHouse(){
let oldWindow = CGSize(width: 100, height: 100)
doorNode?.scale(to: oldWindow)
}
override func sceneDidLoad() {
self.lastUpdateTime = 0
physicsWorld.contactDelegate = self
// Get nodes from scene and store for use later
self.playerNode = self.childNode(withName: "//player") as? SKSpriteNode
playerNode?.physicsBody = SKPhysicsBody(rectangleOf: (playerNode?.frame.size)!)
playerNode?.physicsBody?.isDynamic = true
playerNode?.physicsBody?.affectedByGravity = false
playerNode?.physicsBody?.categoryBitMask = UInt32(playerCategory)
playerNode?.physicsBody?.collisionBitMask = UInt32(wallCategory)
playerNode?.physicsBody?.contactTestBitMask = UInt32(doorCategory)
for child in self.children {
/*if child.name == "wall" {
if let child = child as? SKSpriteNode {
wallNode?.physicsBody = SKPhysicsBody(rectangleOf: (wallNode?.frame.size)!)
wallNode?.physicsBody?.isDynamic = false
wallNode?.physicsBody?.categoryBitMask = UInt32(wallCategory)
wallNode?.physicsBody?.collisionBitMask = UInt32(playerCategory)
self.addChild(child)
}
}*/
}
self.doorNode = self.childNode(withName: "door") as? SKSpriteNode
doorNode?.physicsBody?.affectedByGravity = false
doorNode?.physicsBody?.isDynamic = false
doorNode?.physicsBody = SKPhysicsBody(rectangleOf: (doorNode?.frame.size)!)
doorNode?.physicsBody?.categoryBitMask = UInt32(doorCategory)
doorNode?.physicsBody?.contactTestBitMask = UInt32(playerCategory)
}
func touchDown(atPoint pos : CGPoint) {
let fromX = playerNode?.position.x
let fromY = playerNode?.position.y
let toX = pos.x
let toY = pos.y
let resultX = toX - (fromX)!
let resultY = toY - (fromY)!
let newX = (playerNode?.position.x)! + resultX / 10
let newY = (playerNode?.position.y)! + resultY / 10
playerNode?.position.x = newX
playerNode?.position.y = newY
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches { self.touchDown(atPoint: t.location(in: self)) }
}
func didBeginContact(contact: SKPhysicsContact) {
//this gets called automatically when two objects begin contact with each other
// 1. Create local variables for two physics bodies
var firstBody: SKPhysicsBody
var secondBody: SKPhysicsBody
// 2. Assign the two physics bodies so that the one with the lower category is always stored in firstBody
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
firstBody = contact.bodyA
secondBody = contact.bodyB
} else {
firstBody = contact.bodyB
secondBody = contact.bodyA
}
if secondBody.categoryBitMask == UInt32(doorCategory){
enterHouse()
}
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
// Initialize _lastUpdateTime if it has not already been
if (self.lastUpdateTime == 0) {
self.lastUpdateTime = currentTime
}
// Calculate time since last update
let dt = currentTime - self.lastUpdateTime
// Update entities
for entity in self.entities {
entity.update(withDeltaTime: dt)
}
self.lastUpdateTime = currentTime
}
}
I presume this line is causing the crash
wallNode?.physicsBody = SKPhysicsBody(rectangleOf: (wallNode?.frame.size)!)
as you are force unwrapping (!) the wallNode size however looking at your code you never assign it to anything like so
wallNode = self.childNode(withName: "wallNode") as? SKSpriteNode
or in the for loop.
Try this code in your for in loop that should avoid crashes and assigns your wall node.
for child in self.children where child.name == "wall" {
if let child = child as? SKSpriteNode {
wallNode = child // Try this
if let wallNode = wallNode { // safely unwrap wall node to avoid crashes
wallNode.physicsBody = SKPhysicsBody(rectangleOf: (wallNode.frame.size))
wallNode.physicsBody?.isDynamic = false
wallNode.physicsBody?.categoryBitMask = UInt32(wallCategory)
wallNode.physicsBody?.collisionBitMask = UInt32(playerCategory)
self.addChild(wallNode) // add wall node here instead if you are using your wallNode property
}
}
}
Hope this helps
When player body meet a monster body the function func didBeginContact(contact: SKPhysicsContact) is never called.
My class implement SKPhysicsContactDelegate
class GameScene: SKScene, SKPhysicsContactDelegate
the property collisionBitMask i commented it because i read that if we don't put it, node can collise with all node
I add my player like this :
bear.position = CGPoint(x:self.frame.width/20, y:self.frame.height/5)
bear.size = CGSize(width: self.frame.width/7, height: self.frame.height/5)
bear.physicsBody = SKPhysicsBody(rectangleOfSize: bear.frame.size)
bear.physicsBody?.dynamic = false;
bear.physicsBody?.categoryBitMask = PhysicsCategory.Player
bear.physicsBody?.contactTestBitMask = PhysicsCategory.Monster
//bear.physicsBody?.collisionBitMask = PhysicsCategory.Monster
I add monster like this:
let thePosition = CGPoint(x: positionX, y: (self.frame.height/7.4)*positionY)
let monster = SKSpriteNode(imageNamed: pattern as String)
monster.size = CGSize(width: self.frame.width/16, height: self.frame.height/11)
monster.name = name
monster.position = thePosition
monster.physicsBody = SKPhysicsBody(rectangleOfSize: monster.frame.size)
monster.physicsBody?.dynamic = false
monster.physicsBody?.categoryBitMask = PhysicsCategory.Monster // 3
monster.physicsBody?.affectedByGravity = false
monster.physicsBody?.contactTestBitMask = PhysicsCategory.Player // 4
//platformNode.physicsBody?.collisionBitMask = PhysicsCategory.Player // 5
addChild(monster)
my PhysicalCategory
struct PhysicsCategory {
static let None : UInt32 = 0
static let All : UInt32 = UInt32.max
static let Player: UInt32 = 0b1 // 1
static let Monster : UInt32 = 0b10 // 2
static let Projectile: UInt32 = 0b100 // 3
static let Plateforme: UInt32 = 0b1000 // 4
}
I have multiple Textures, for example when player walk i call this :
func walkingBear() {
bear.runAction( SKAction.repeatActionForever(SKAction.animateWithTextures(bearCoursFrames, timePerFrame: 0.1, resize: false, restore: true)), withKey:"cours")
}
I initlaise bearCoursFrames like this
var walkFrames = [SKTexture]()
var numImages : Int = bearAnimatedAtlas.textureNames.count
for var i=1; i<=numImages/2; i++ {
let bearTextureName = "cours\(i)"
walkFrames.append(bearAnimatedAtlas.textureNamed(bearTextureName))
}
bearCoursFrames = walkFrames
Where is my error please, i can't find a solution in other post
any help would be appreciated
---------------------------------EDIT------------------------------
Solution: I just add self.physicsWorld.contactDelegate = self and it works now.
The answer is simple. There is a rule which says that at least one body has to be defined as dynamic in order to register the contact.
Change either player or other body to be dynamic and contact will be detected.
As an addition, if you already haven't, enable physics visual representation ... It can be helpful because sometimes contact is not detected because the actual bodies are not intersecting each other eg. when node's anchor point is changed, so the texture and a body are misplaced (which is fixable, using appropriate initializers of SKPhysicsBody class, but this is a very common error).
I just add
self.physicsWorld.contactDelegate = self
and it works now.
I wanted to create a small animation of a car driving down the road, so I made an atlas of 9 different pictures. The car simply looks like its wheels are rotating and the car is bouncing a bit as it drives along. I already made an SKSpriteNode with an image and added a physics body on it so that it can jump and be affected by gravity.
So I was wondering how to add either a physics body to an SKAtlasTexture or create an animation through my image.xcassets folder. I tried to just change the SKSpriteNode to SKAtlasTexture, but that obviously didn't work as there are no physics bodies in SKAtlasTexture. So that's where I'm at. Any suggestions or solutions would be greatly appreciated.
Here some parts of my code:
class PlayScene: SKScene, SKPhysicsContactDelegate {
let road = SKSpriteNode(imageNamed: "road")
var origRoadPositionX = CGFloat(0)
var maxRoad = CGFloat(0)
var groundSpeed = 3
var carBaseLine = CGFloat(0)
let car = SKSpriteNode(imageNamed: "car")
enum ColliderType:UInt32{
case car = 1
case tower = 2
}
override func didMoveToView(view: SKView) {
self.backgroundColor = UIColor(hex: 0x80E8FF)
self.physicsWorld.contactDelegate = self
//Car
self.car.position = CGPointMake(CGRectGetMinX(self.frame)-20 + self.car.size.width, self.carBaseLine)
self.car.physicsBody = SKPhysicsBody (rectangleOfSize: self.car.size)
self.car.physicsBody?.allowsRotation = false
self.car.physicsBody?.affectedByGravity = false
self.car.physicsBody?.categoryBitMask = ColliderType.car.rawValue
self.car.physicsBody?.contactTestBitMask = ColliderType.tower.rawValue
self.car.physicsBody?.collisionBitMask = ColliderType.tower.rawValue
self.addChild(car)
If more code is needed in order to find a solution, let me know and i can supply more of it.
You can use atlas folder for performing animation with images.
Consider below example:
import SpriteKit
class GameScene: SKScene {
var bombFrames : [SKTexture]!
var bomb : SKSpriteNode!
let NoneCategory : UInt32 = 0x1 << 0
let ProjectileCategory : UInt32 = 0x1 << 2
let bombCategory : UInt32 = 0x1 << 7
override func didMoveToView(view: SKView) {
/* Setup your scene here */
runAction(SKAction.repeatActionForever(SKAction.sequence([SKAction.runBlock(addBomb), SKAction.waitForDuration(5.0)])))
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
func addBomb() {
let Name = "Bomb"
let AnimatedAtlas = SKTextureAtlas(named: Name)
var Framese = [SKTexture]()
let numImages = AnimatedAtlas.textureNames.count
for var i=1; i<=numImages; i++ {
let TextureName = "\(i)"
Framese.append(AnimatedAtlas.textureNamed(TextureName))
}
bombFrames = Framese
let firstFrame = bombFrames[0]
bomb = SKSpriteNode(texture: firstFrame)
let actualY = random(min: bomb.size.height/2, max: size.height - bomb.size.height/2)
bomb.position = CGPoint(x: size.width + bomb.size.width/2, y: actualY)
bomb.physicsBody = SKPhysicsBody(texture: bomb.texture, size: bomb.texture!.size())
bomb.physicsBody?.dynamic = true
bomb.physicsBody?.categoryBitMask = bombCategory
bomb.physicsBody?.contactTestBitMask = ProjectileCategory
bomb.physicsBody?.collisionBitMask = NoneCategory
bomb.physicsBody?.usesPreciseCollisionDetection = true
addChild(bomb)
let actualDuration = random(min: CGFloat(2.0), max: CGFloat(4.0))
let actionMove = SKAction.moveTo(CGPoint(x: -bomb.size.width/2, y: actualY), duration: NSTimeInterval(actualDuration))
let actionMoveDone = SKAction.removeFromParent()
bomb.runAction(SKAction.sequence([actionMove, actionMoveDone]))
playBombAnimation()
}
func random() -> CGFloat {
return CGFloat(Float(arc4random()) / 0xFFFFFFFF)
}
func random(#min: CGFloat, max: CGFloat) -> CGFloat {
return random() * (max - min) + min
}
func playBombAnimation() {
bomb.runAction(SKAction.repeatActionForever(SKAction.animateWithTextures(bombFrames, timePerFrame: 0.1, resize: false, restore: true)), withKey:"bombAnimation")
}
}
And don't forget to add atlas folder into your project navigator like this:
As you can see in code you can add physics body to your sprite. and if you want you can try this way.
Hope this will help.
I have been struggling for the past two days to get two SKSpriteNodes to register a collision and evoke didBegin#contact.
I've set their bit masks 'categoryBitMask', 'contactTestBitMask' and 'collisionTestBitMask' for both objects.
I've also set the 'dynamic' property for both to 'true'
initPhysics() seems to set up the physicsWorld okay.
All I'm expecting is that didBegin#Contact is called, but it is not
//Set up Physicsbody bit masks
let playerCarBitMask: UInt32 = 0x1 << 1
let slowCarBitMask: UInt32 = 0x1 << 2
//initPhysics
func initPhysics() {
println("(((((((((((((( Initiating Physicsbody ))))))))))))))")
self.physicsWorld.contactDelegate = self
self.physicsWorld.gravity = CGVector.zeroVector
println("self.physicsWorld.contactDelegate = \(self.physicsWorld.contactDelegate)")
}
//setupPlayer
func setupPlayer() {
car = SKSpriteNode(imageNamed: "redCarUp")
car.setScale(2.0)
car.position = CGPoint(x: 800, y: 400)
car.zPosition = 100
car.name = "car"
gameNode.addChild(car)
let carBody = SKPhysicsBody(
rectangleOfSize: car.frame.size, center: car.position)
carBody.dynamic = true
carBody.categoryBitMask = playerCarBitMask
carBody.contactTestBitMask = slowCarBitMask
carBody.mass = 5
carBody.collisionBitMask = slowCarBitMask
car.physicsBody = carBody
println("carBody = \(carBody)")
println("carBody.dynamic = \(carBody.dynamic)")
println("carBody.mass = \(carBody.mass)")
println("carBody.categoryBitMask = \(carBody.categoryBitMask)")
println("carBody.contactTestBitMask = \(carBody.contactTestBitMask)")
println("carBody.collisionBitMask = \(carBody.contactTestBitMask)")
slowCar = SKSpriteNode(imageNamed: "blueCarUp")
slowCar.setScale(2.0)
let slowCarScenePos = CGPoint(
x: 680,
y: 2048)
slowCar.position = gameNode.convertPoint(slowCarScenePos, fromNode: self)
println("slowCar.position = \(slowCar.position) ****")
slowCar.zPosition = 80
slowCar.name = "slowCar"
let slowCarBody = SKPhysicsBody(
rectangleOfSize: slowCar.frame.size, center: slowCar.position)
println("slowCar = \(slowCar) ****")
slowCarBody.dynamic = true
slowCarBody.categoryBitMask = slowCarBitMask
slowCarBody.contactTestBitMask = playerCarBitMask
slowCarBody.mass = 5
slowCarBody.collisionBitMask = playerCarBitMask
slowCar.physicsBody = slowCarBody
gameNode.addChild(slowCar)
}
func didBeginContact(contact: SKPhysicsContact!) {
println("*******************PhysicsContact********************")
}
'didBeginContact' has been changed to 'didBegin' in swift 3
func didBegin(_ contact: SKPhysicsContact) {
//stuff
}
I had a code from swift 2 and 'didBeginContact' was sitting there but wasn't being called. After quite a white I figured out that the function was changed. So, I thought my answer could help someone.
If you want make a contact between car and slowCar you have to init the categoryBitMask of both physicsBodies (I think you did). See the code below to get contact between two physicsBodies. When there is a contact it returns your display function :
//init your categoryBitMask :
let carCategory:UInt32 = 0x1 << 0
let SlowCarCategory:UInt32 = 0x1 << 1
//init car
car.physicsBody?.categoryBitMask = carCategory
car.physicsBody?.contactTestBitMask = slowCarCategory
//init slowCar
slowCar.physicsBody?.categoryBitMask = slowCarCategory
slowCar.physicsBody?.contactTestBitMask = CarCategory
// set your contact function
func didBeginContact(contact: SKPhysicsContact!)
{
var firstBody: SKPhysicsBody
var secondBody: SKPhysicsBody
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)
{
firstBody = contact.bodyA
secondBody = contact.bodyB
}
else
{
firstBody = contact.bodyB
secondBody = contact.bodyA
}
if ((firstBody.categoryBitMask & carCategory) != 0 && (secondBody.categoryBitMask & slowCarCategory) != 0)
{
displayfunction(firstBody.node as SKSpriteNode, car: secondBody.node as SKSpriteNode)
}
}
func displayFunction (slowCar : SKSpriteNode, car : SKSpriteNode)
It turned out to be a simple problem. In my original code I was setting parameters for the SKPhysicsBody detection frame like so:
let carBody = SKPhysicsBody(
rectangleOfSize: car.frame.size, center: car.position)
Similarly I was doing the same for the second node that I was testing physics collisions for.
Simply removing the 'centre:' parameters like so:
let carBody = SKPhysicsBody(rectangleOfSize: car.frame.size)
for the two sprite nodes solved the problem and the nodes now crash into each other and push themselves aside as expected.
Please Note that contact will not be detected between two static bodies
(node.physicsBody?.isDynamic = false)