beginner swift sprite kit - node collision detection help (SKPhysicsContact)
First you should set your firstBody & secondBody to the order of their collisionBitMask:
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask
{
firstBody = contact.bodyA
secondBody = contact.bodyB
}
else {
firstBody = contact.bodyB
secondBody = contact.bodyA
}
if firstBody.categoryBitMask=0 && secondBody.categoryBitMask=1 {
secondBody.removeFromParent()
}
This will prevent your sprites from colliding with anything (including each other). Set this where you set the other BitMask properties:
alpha.physicsBody.collisionBitMask = 0
beta.physicsBody.collisionBitMask = 0
How to Detect collision in Swift, Sprite kit
- Define unique categories, ensure your class is a
SKPhysicsContactDelegate
and make yourself the physics contact delegate:
//Physics categories
let enemyCategory: UInt32 = 1 << 1
let bulletCategory: UInt32 = 1 << 2
class GameScene: SKScene, SKPhysicsContactDelegate {
physicsWorld.contactDelegate = self
Assign the categories (usually in
didMove(to view:)
:enemy.physicsBody.catgeoryBitMask = enemyCategory
bullet.physicsBody.catgeoryBitMask = bulletCategory
(Make sure you've created physics bodies for each node)
- Set up collisions:
enemy.physicsBody?.collisionBitMask = 0 // enemy collides with nothing
bullet.physicsBody?.collisionBitMask = 0 // bullet collides with nothing
or even:
for node in [enemy, bullet] {
node.physicsBody?.collisionBitMask = 0 // collides with nothing
}
Set up contacts
bullet.physicsBody?.collisionBitMask = enemyCategory // bullet contacts enemy
Make sure that at least one of the objects involved in each potential contact has the isDynamic
property on its physics body set to true
, or no contact will be generated. It is not necessary for both of the objects to be dynamic.
You should now get didBegin
called when the bullet and the enemy make contact. You could code didBegin
like this:
func didBegin(_ contact: SKPhysicsContact) {
print("didBeginContact entered for \(String(describing: contact.bodyA.node!.name)) and \(String(describing: contact.bodyB.node!.name))")
let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
switch contactMask {
case bulletCategory | enemyCategory:
print("bullet and enemy have contacted.")
let bulletNode = contact.bodyA.categoryBitMask == bulletCategory ? contact.bodyA.node : contact.bodyB.node
enemyHealth -= 10
bulletNode.removeFromParent
default:
print("Some other contact occurred")
}
}
How to detect when two objects touch in SpriteKit
Not to be a stickler but you shouldn't start of a question with "I couldn't find anything on the internet" when thats blatantly not true. There is a million tutorials on collision detection around as its one of the basics in SpriteKit.
Now to your question. You did not give your sprites an actual physics body and your physics categories are set up weird. Change your code to this
struct PhysicsCategory {
static let cat:UInt32 = 0x1 << 0
static let ground:UInt32 = 0x1 << 1
}
class GameScene: SKScene, SKPhysicsContactDelegate {
override func didMoveToView(view: SKView) {
/* Setup your scene here */
physicsWorld.contactDelegate = self
cat.physicsBody = SKPhysicsBody(rectangleOfSize: cat.size) // FORGOT THIS
cat.physicsBody?.categoryBitMask = PhysicsCategory.cat
cat.physicsBody?.contactTestBitMask = PhysicsCategory.ground
ground.physicsBody = SKPhysicsBody(rectangleOfSize: ground.size) // FORGOT THIS
ground.physicsBody?.categoryBitMask = PhysicsCategory.ground
ground.physicsBody?.contactTestBitMask = PhysicsCategory.cat // You dont really need this line as long as you have set it on the other body.
}
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 == PhysicsCategory.cat && secondBody.categoryBitMask == PhysicsCategory.ground {
print("contact")
}
}
}
If you dont want your objects to fall you have to turn gravity off, which is on by default.
cat.physicsBody?.affectedByGravity = false
ground.physicsBody?.affectedByGravity = false
Hope this helps
Avoiding collision in spriteKit on iOS while maintaining contact
You might have a different problem in your code elsewhere, because this is working for me. The bodies do not collide and the contact happens.
Sample playground code:
import PlaygroundSupport
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var stoneNode: SKShapeNode!
var birdNode: SKShapeNode!
override func didMove(to view: SKView) {
let stoneCategory : UInt32 = 0x1 << 1
let birdCategory : UInt32 = 0x1 << 2
physicsWorld.contactDelegate = self
stoneNode = SKShapeNode(circleOfRadius: 30)
stoneNode.name = "stone"
stoneNode.position = CGPoint(x: 0, y: 100)
addChild(stoneNode)
stoneNode.physicsBody = SKPhysicsBody(circleOfRadius: 30)
stoneNode.physicsBody?.categoryBitMask = stoneCategory
stoneNode.physicsBody?.contactTestBitMask = birdCategory
stoneNode.physicsBody?.collisionBitMask = 0
birdNode = SKShapeNode(circleOfRadius: 40)
birdNode.name = "bird"
birdNode.position = CGPoint(x: 0, y: -100)
addChild(birdNode)
birdNode.physicsBody = SKPhysicsBody(circleOfRadius: 40);
birdNode.physicsBody?.categoryBitMask = birdCategory
birdNode.physicsBody?.contactTestBitMask = stoneCategory
birdNode.physicsBody?.collisionBitMask = 0
birdNode.physicsBody?.isDynamic = false;
}
func didBegin(_ contact: SKPhysicsContact) {
print("Contact between "+contact.bodyA.node!.name!+" and "+contact.bodyB.node!.name!); // --> Never called
}
}
// Load the SKScene from 'GameScene.sks'
let sceneView = SKView(frame: CGRect(x:0 , y:0, width: 640, height: 480))
if let scene = GameScene(fileNamed: "GameScene") {
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
// Present the scene
sceneView.presentScene(scene)
}
PlaygroundSupport.PlaygroundPage.current.liveView = sceneView
And in the console, when the first body drops, being affected by gravity, it's printed:
Contact between stone and bird
Swift/SpriteKit - Collisions and Objects
The SKPhyicsBody class has a node property which points to the SKNode instance it is attached to.
Your collision detection code can look something like this:
func didBeginContact(contact: SKPhysicsContact) {
var item: SKSpriteNode
var character: SKSpriteNode
//Change this on the basis of the order of your categoryBitMask values
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)
{
item = contact.bodyA.node as SKSpriteNode
character = contact.bodyB.node as SKSpriteNode
}
else
{
item = contact.bodyB.node as SKSpriteNode
character = contact.bodyA.node as SKSpriteNode
}
//Perform necessary operations on item and character
}
EDIT:
In order to get access to the Item instance that declares the node, you will have to store a variable within the node which points to the Item instance. For this, you can either subclass SKSpriteNode and include a property, or use the userData
property of SKNode
Subclassing:
//New Class
class ItemNode: SKSpriteNode {
static var itemInstance
}
//In the Item class declare the itemNode using this class
let itemNode = ItemNode()
itemNode.itemInstance = self
UserData property:
item.userData = NSMutableDictionary(object: self, forKey: "ItemInstance"))
Related Topics
Private Var Is Accessible from Outside the Class
Add a Line as a Selection Indicator to a Uitabbaritem in Swift
Apple's Description of Reference and Value Types with Multiple Threads
Swift - Avaudioplayer, Sound Doesn't Play Correctly
Know When an Iteration Over Array with Async Method Is Finished
Swift Error: 'Missing Return in Function'
Swift Setting Badge Value for Uitabbaritem
Initializer for Conditional Binding Must Have Optional Type, Not 'String'
How to Check the Network Speed Using Swift
"Cannot Assign Value of Type 'String' to Type 'Anyobject'", Swift 3, Xcode 8 Beta 6
Inconsistent Scenekit Framerate
How to Convert PDF to Png Efficiently
Extending Collection with a Recursive Property/Method That Depends on the Element Type
Swift: Optional Text in Optional Value
Transform a Swift Sequence in to Adjacent Pairs