In SpriteKit SKPhysicsContactDelegate contact is not detected - swift

I am trying to program a game with Spritekit in Swift. The aim is to escape with his character oncoming rectangles. Now I've made a mistake with the SKPhysicsContactDelegate (didBegin ()) method, so the figure's contact with one of the rectangles is not recognized. Can someone help me find the mistake?
import SpriteKit
enum bodyType: UInt32 {
case rechteckRechts = 1
case rechteckLinks = 2
case figur = 4
}
class PlayScene: SKScene, SKPhysicsContactDelegate {
let figur = SKSpriteNode(imageNamed: "Punkt.jpg")
#objc func addRechteck(){
let rechteckRechts = SKSpriteNode(imageNamed: "Rechteck.gif")
rechteckRechts.physicsBody = SKPhysicsBody()
rechteckRechts.physicsBody?.isDynamic = false
rechteckRechts.physicsBody?.categoryBitMask = bodyType.rechteckRechts.rawValue
let rechteckLinks = SKSpriteNode(imageNamed: "Rechteck.gif")
rechteckLinks.physicsBody = SKPhysicsBody()
rechteckLinks.physicsBody?.isDynamic = false
rechteckLinks.physicsBody?.categoryBitMask = bodyType.rechteckLinks.rawValue
let groesse = arc4random_uniform(5)+1
print(groesse)
switch groesse {
case 1:
rechteckLinks.xScale = 0.5
rechteckRechts.xScale = 1.5
case 2:
rechteckLinks.xScale = 1.5
rechteckRechts.xScale = 0.5
case 3:
rechteckLinks.xScale = 1
rechteckRechts.xScale = 1
case 4:
rechteckLinks.xScale = 1.25
rechteckRechts.xScale = 0.75
case 5:
rechteckLinks.xScale = 0.75
rechteckRechts.xScale = 1.25
default:
print("Fehler in der Wahrscheinlichkeit!!!")
}
rechteckRechts.position = CGPoint(x: frame.minX + (rechteckRechts.size.width / 2), y: frame.maxY)
rechteckLinks.position = CGPoint(x: frame.maxX - (rechteckLinks.size.width / 2), y: frame.maxY)
let moveDown = SKAction.moveBy(x: 0, y: -5000, duration: 20.0)
rechteckLinks.run(moveDown)
rechteckRechts.run(moveDown)
self.addChild(rechteckRechts)
self.addChild(rechteckLinks)
}
override func didMove(to view: SKView) {
physicsWorld.gravity = .zero
physicsWorld.contactDelegate = self
figur.xScale = 0.4
figur.yScale = 0.4
figur.position = CGPoint(x: frame.midX, y: frame.maxY / 4)
figur.physicsBody = SKPhysicsBody()
figur.physicsBody?.isDynamic = false
figur.physicsBody?.categoryBitMask = bodyType.figur.rawValue
self.addChild(figur)
self.backgroundColor = SKColor.white
let wait1 = SKAction.wait(forDuration: 3)
let timer = SKAction.repeatForever(SKAction.sequence([wait1, SKAction.run {
self.addRechteck()
}]))
self.run(timer, withKey: "addRechteck")
}
func didBegin(_ contact: SKPhysicsContact) {
let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
switch contactMask {
case bodyType.figur.rawValue | bodyType.rechteckLinks.rawValue:
print("contact")
case bodyType.figur.rawValue | bodyType.rechteckRechts.rawValue:
print("contact")
default:
return
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in (touches ){
let location = touch.location(in: self)
if figur.contains(location){
figur.position = location
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in (touches ) {
let location = touch.location(in: self)
if figur.contains(location){
figur.position = location
}
}
}
}

You don’t appear to have set the collision and contactTest bit masks. These are properties of the physics body. The collisionBitMask controls which objects bounce off which other objects. By default, everything bounces off everything else.
The contactTestBitMask is the important one in this siutuation, as it controls which objects invoke ‘didBegin‘ when contacting other objects. By default, this is 0 which means that no contacts are registered. See my answer here : https://stackoverflow.com/a/40596890/1430420 for a detailed explanation.
In your specific example, you appear to want to know when figur makes contact with either rechteckLinks or rechteckRechts, so you would want to following contactTestBitMasks:
figur.physicsBody!.cagegoryBitMask = bodyType.figur
figur.physicsBody!.contactTestBitMask = bodyType.rechteckRechts | bodyType.rechteckLinks
Edit: correcting the definition of figurs physics body:
We change the ? to ! so that we get a crash should the physics body not exist. We also change the isDynamic property to true as physics bodies that are not dynamic cannot register contacts (I think). It’s ok for the other 2 to not be dynamic. We assign the collisionTestBitMask to the value of all the categories we want to be notified of collision with logically ‘OR’ed together:
figur.physicsBody!.isDynamic = true
figur.physicsBody!.categoryBitMask = bodyType.figur.rawValue
figur.physicsBody!.contactTestBitMask = bodyType.rechteckRechts.rawValue | bodyType.rechteckLinks.rawValue

Related

Swift / SpriteKit Collision producing varying results

I have a small test project (my first using Swift / XCode) which is designed to move me away from HTML5 and Canvas for game production.
The code compiles and runs fine. I use my iPhone as the test device rather than the built in simulator.
The symptoms of the problem are
that the lasers being repeatedly fired from the player's ship appear to occasionally bend around the aliens
the names being pulled out from the nodes are being shown as their default names not the names I assigned to them at creation
In some cases the collision works fine and the alien explosion is generated and the alien sprite node is removed from the scene.
I have named the alien nodes "alien" and the laser nodes "laser".
Both have their contactTestBitMask set to the same value.
Here is my GameScene.swift code:
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var lastUpdateTime: TimeInterval = 0
var delta: TimeInterval = 0
var sp_player: SKSpriteNode!
var stars: SKSpriteNode!
var deeperstars: SKSpriteNode!
var laser: SKSpriteNode!
var alien: SKSpriteNode!
var explosionSplat1: SKSpriteNode!
var playerScore: UInt32!
struct PhysicsCategory {
static let base:UInt32 = 0x1 << 0
static let alien:UInt32 = 0x1 << 1
static let laser:UInt32 = 0x1 << 2
static let player:UInt32 = 0x1 << 3
}
override func didMove(to view: SKView) { // called when the scene is presented into view (happens only once)
playerScore = 0
physicsWorld.contactDelegate = self
physicsWorld.gravity = .zero
// BACKGROUND
backgroundColor = UIColor(red: 0/255, green: 0/255, blue: 48/255, alpha: 1.0)
print("Background color is set")
// WRAP THE STARFIELDS
// Front most layer of stars
let starsTexture = SKTexture(imageNamed: "stars.png")
let bgAnimation = SKAction.move(by: CGVector(dx: 0, dy: -starsTexture.size().height), duration: 5)
let bgReset = SKAction.move(by: CGVector(dx: 0, dy: starsTexture.size().height), duration: 0)
let bgConstantMotion = SKAction.repeatForever(SKAction.sequence([bgAnimation,bgReset]))
// Back layer of slower stars
let deeperStarsTexture = SKTexture(imageNamed: "stars-deeper.png")
let deeperStarsbgAnimation = SKAction.move(by: CGVector(dx: 0, dy: -deeperStarsTexture.size().height), duration: 8)
let deeperStarsbgReset = SKAction.move(by: CGVector(dx: 0, dy: deeperStarsTexture.size().height), duration: 0)
let deeperStarsbgConstantMotion = SKAction.repeatForever(SKAction.sequence([deeperStarsbgAnimation,deeperStarsbgReset]))
var i: CGFloat = 0
while i < 3
{
stars = SKSpriteNode(texture: starsTexture)
stars.position = CGPoint(x: frame.midX, y: starsTexture.size().height * i)
stars.size.height = frame.height
stars.run(bgConstantMotion)
stars.zPosition = -1
addChild(stars)
deeperstars = SKSpriteNode(texture: deeperStarsTexture)
deeperstars.position = CGPoint(x: frame.midX, y: deeperStarsTexture.size().height * i)
deeperstars.size.height = frame.height
deeperstars.run(deeperStarsbgConstantMotion)
deeperstars.zPosition = -1
addChild(deeperstars)
i += 1
}
// PLAYER
let playerTexture1 = SKTexture(imageNamed: "player-1.png")
let playerTexture2 = SKTexture(imageNamed: "player-2.png")
let playerAnimation = SKAction.animate(with: [playerTexture1, playerTexture2], timePerFrame: 0.2)
let constantAnimation = SKAction.repeatForever(playerAnimation)
sp_player = SKSpriteNode(texture: playerTexture1)
sp_player.position = CGPoint(x: frame.midX, y: (sp_player.size.height * 2))
sp_player.physicsBody = SKPhysicsBody(rectangleOf: sp_player.size)
sp_player.physicsBody!.isDynamic = false
sp_player.name = "player"
sp_player.run(constantAnimation)
addChild(sp_player)
// PLACE ALIENS
let alienTexture1 = SKTexture(imageNamed: "alien-1a.png")
let alienTexture2 = SKTexture(imageNamed: "alien-1b.png")
let alienAnimation = SKAction.animate(with: [alienTexture1, alienTexture2], timePerFrame: 0.4)
let constantAlienAnimation = SKAction.repeatForever(alienAnimation)
var x: CGFloat = 0, y: CGFloat = 0
while y < 6
{
while x < 6
{
alien = SKSpriteNode(texture: alienTexture1)
alien.position = CGPoint(x: 32 + (x * alien.size.width), y: (frame.size.height - (alien.size.height * 1.5) - (alien.size.height * y)))
print("Setting y to \(frame.size.height - (alien.size.height * y))")
alien.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: alien.size.width, height: alien.size.height))
alien.physicsBody!.isDynamic = false
alien.name = "alien"
alien.physicsBody!.contactTestBitMask = PhysicsCategory.laser
alien.run(constantAlienAnimation)
addChild(alien)
x += 1
}
y += 1
x = 0
}
print("Sprites added to scene")
spawnLasers()
}
func spawnLasers()
{
let delay1 = SKAction.wait(forDuration: 0.5)
let spawn = SKAction.run {
let laserTexture = SKTexture(imageNamed: "laser-1.png")
self.laser = SKSpriteNode(texture: laserTexture)
self.laser.position = CGPoint(x: self.sp_player.position.x, y: self.sp_player.position.y + self.sp_player.size.height)
self.laser.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: self.laser.size.width, height: self.laser.size.height))
self.laser.physicsBody!.isDynamic = true
self.laser.physicsBody!.linearDamping = 0
self.laser.physicsBody!.allowsRotation = false
self.laser.physicsBody!.contactTestBitMask = PhysicsCategory.laser
self.laser.name = "laser"
self.addChild(self.laser)
let shoot = SKAction.moveTo(y: self.frame.size.height, duration: 1)
let killLaser = SKAction.removeFromParent()
let handleLaser = SKAction.sequence([shoot,killLaser])
self.laser.run(handleLaser)
}
let action = SKAction.sequence([delay1,spawn])
let constantLasers = SKAction.repeatForever(action)
self.run(constantLasers)
}
func didBegin(_ contact: SKPhysicsContact) {
var check: UInt32 = 0
if contact.bodyA.node != nil
{
check += 1
}
if contact.bodyB.node != nil
{
check += 1
}
if check == 2
{
if contact.bodyA.node!.name == "alien" && contact.bodyB.node!.name == "laser"
{
// EXPLOSION
let explosionSplatTexture1 = SKTexture(imageNamed: "explosion-1a.png")
let explosionSplatTexture2 = SKTexture(imageNamed: "explosion-1b.png")
let explosionSplatTexture3 = SKTexture(imageNamed: "explosion-1c.png")
let explosionSplatTexture4 = SKTexture(imageNamed: "explosion-1d.png")
let explosionSplatAnimation = SKAction.animate(with: [explosionSplatTexture1, explosionSplatTexture2, explosionSplatTexture3, explosionSplatTexture4], timePerFrame: 0.1)
let killExplosion = SKAction.removeFromParent()
let explosionSequence = SKAction.sequence([explosionSplatAnimation,killExplosion])
explosionSplat1 = SKSpriteNode(texture: explosionSplatTexture1)
explosionSplat1.name = "explosion"
explosionSplat1.position = CGPoint(x: contact.bodyA.node!.position.x, y: contact.bodyA.node!.position.y)
addChild(explosionSplat1)
explosionSplat1.run(explosionSequence)
self.playerScore += 1
print("Score: \(self.playerScore!)")
contact.bodyA.node?.removeFromParent()
print("Alien named \(contact.bodyA.node?.name ?? "defaultAlienName") from scene")
contact.bodyB.node?.removeFromParent()
print("Laser named \(contact.bodyB.node?.name ?? "defaultLaserName") from scene")
}
}
}
func didEnd(_ contact: SKPhysicsContact) {
//print("Contact ended between \(contact.bodyA) and \(contact.bodyB)")
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
{
// if let touch = touches.first {
// let position = touch.location(in: view)
// storedTouch = position
// }
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?)
{
if let touch = touches.first {
let position = touch.location(in: view)
var playerpos: CGPoint!
playerpos = sp_player.position
let pl_move = SKAction.move(to: CGPoint(x: position.x, y: playerpos.y), duration: 0.1)
sp_player.run(pl_move)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?)
{
/*
Need to figure out how to use storedTouch properly
to move player relative to the screen touch co-ordinates
*/
// if let touch = touches.first {
// let position = touch.location(in: view)
// }
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
if (lastUpdateTime > 0)
{
delta = currentTime - lastUpdateTime
} else {
delta = 0
}
lastUpdateTime = currentTime
}
}
When the game runs it produces this screen:
You can see the unexpected laser behavior here:
In my diags I get the following output from the collision function:
Score: 1
Alien named defaultAlienName removed from scene
Laser named laser removed from scene
Score: 2
Alien named defaultAlienName removed from scene
Laser named laser removed from scene
Score: 3
Alien named defaultAlienName removed from scene
Laser named laser removed from scene
Score: 4
Alien named defaultAlienName removed from scene
Laser named defaultLaserName removed from scene
Score: 5
Alien named defaultAlienName removed from scene
Laser named defaultLaserName removed from scene
Score: 6
Alien named defaultAlienName removed from scene
Laser named defaultLaserName removed from scene
This is most likely my complete lack of understanding for optionals and how collision actually works. I'd be super grateful for any insights.
In your alien loop, as well as spawnLasers(), you are not giving the sprite nodes an actual PhysicsBody category. For sprites to be able to detect contact between one another, they need a category name and a contact name.
So in your while loop (building the aliens), you need to have this:
alien.physicsBody!.categoryBitMask = PhysicsCategory.alien
alien.physicsBody!.contactTestBitMask = PhysicsCategory.laser
And in spawnLasers(), you want this added:
self.laser.physicsBody!.categoryBitMask = PhysicsCategory.laser
and change the contactTestBitMask to alien, not laser:
self.laser.physicsBody!.contactTestBitMask = PhysicsCategory.alien
Hopefully you can see that the alien category wants to know when lasers touch, and the laser category wants to know when the aliens touch.
To visually help you, turn on the show physics option, this way you can see the actual physics bodies you are dealing with.
To do this, in your GameViewController (or similar), find:
showsFPS = true
showsNodeCount = true
You want to add the following:
showsPhysics = true
This will help with seeing the actual physics bodies on screen.
In:
func didBegin(_ contact: SKPhysicsContact)
you are only testing for BodyA being alien and BodyB being laser.
This is the contact test:
if contact.bodyA.node!.name == "alien" && contact.bodyB.node!.name == "laser"
I believe BodyA could be laser and BodyB be alien. Basically the physics engine contact events could be "A hitting B", or "B hitting A".
Therefore, a quick and dirty solution is to create another if statement below the current one, but changing the body names, so:
if contact.bodyA.node!.name == "laser" && contact.bodyB.node!.name == "alien" {
and duplicate the code from your existing if statement, and changing the two print statements.
This isn't the ideal way to do it, but hopefully when you tidy it up you'll get an understanding of what the physics contact is doing.
I am hoping once you have implmented the above, you will be in a much better shape.

Error with SKPhysicsContactDelegate?

I am trying to program a game with Spritekit in Swift. The aim is to escape with his character oncoming rectangles. Now I've made a mistake with the SKPhysicsContactDelegate (didBegin ()) method, so the figure's contact with one of the rectangles is not recognized. Can someone help me find the mistake? This isn't a duplicate because this time I used a different code!
import SpriteKit
struct PhysicsCategory {
static let none : UInt32 = 0
static let all : UInt32 = UInt32.max
static let rechteck : UInt32 = 0b1 // 1
static let figur : UInt32 = 0b10 // 2
}
class PlayScene: SKScene, SKPhysicsContactDelegate{
let figur = SKSpriteNode(imageNamed: "Punkt.jpg")
#objc func addRechteck(){
let rechteckRechts = SKSpriteNode(imageNamed: "Rechteck.gif")
rechteckRechts.physicsBody = SKPhysicsBody(rectangleOf: rechteckRechts.size) // 1
rechteckRechts.physicsBody?.isDynamic = true // 2
rechteckRechts.physicsBody?.categoryBitMask = PhysicsCategory.rechteck // 3
rechteckRechts.physicsBody?.contactTestBitMask = PhysicsCategory.rechteck // 4
rechteckRechts.physicsBody?.collisionBitMask = PhysicsCategory.none // 5
let rechteckLinks = SKSpriteNode(imageNamed: "Rechteck.gif")
rechteckLinks.physicsBody = SKPhysicsBody(rectangleOf: rechteckLinks.size) // 1
rechteckLinks.physicsBody?.isDynamic = true // 2
rechteckLinks.physicsBody?.categoryBitMask = PhysicsCategory.rechteck // 3
rechteckLinks.physicsBody?.contactTestBitMask = PhysicsCategory.rechteck // 4
rechteckLinks.physicsBody?.collisionBitMask = PhysicsCategory.none // 5
let groesse = arc4random_uniform(5)+1
print(groesse)
switch groesse {
case 1:
rechteckLinks.xScale = 0.5
rechteckRechts.xScale = 1.5
case 2:
rechteckLinks.xScale = 1.5
rechteckRechts.xScale = 0.5
case 3:
rechteckLinks.xScale = 1
rechteckRechts.xScale = 1
case 4:
rechteckLinks.xScale = 1.25
rechteckRechts.xScale = 0.75
case 5:
rechteckLinks.xScale = 0.75
rechteckRechts.xScale = 1.25
default:
print("Fehler in der Wahrscheinlichkeit!!!")
}
rechteckRechts.position = CGPoint(x: frame.minX + (rechteckRechts.size.width / 2), y: frame.maxY)
rechteckLinks.position = CGPoint(x: frame.maxX - (rechteckLinks.size.width / 2), y: frame.maxY)
let moveDown = SKAction.moveBy(x: 0, y: -5000, duration: 20.0)
rechteckLinks.run(moveDown)
rechteckRechts.run(moveDown)
self.addChild(rechteckRechts)
self.addChild(rechteckLinks)
}
override func didMove(to view: SKView) {
physicsWorld.gravity = .zero
physicsWorld.contactDelegate = self
figur.xScale = 0.4
figur.yScale = 0.4
figur.position = CGPoint(x: frame.midX, y: frame.maxY / 4)
figur.physicsBody = SKPhysicsBody(rectangleOf: figur.size)
figur.physicsBody?.isDynamic = true
figur.physicsBody?.categoryBitMask = PhysicsCategory.figur
figur.physicsBody?.contactTestBitMask = PhysicsCategory.rechteck
figur.physicsBody?.collisionBitMask = PhysicsCategory.none
figur.physicsBody?.usesPreciseCollisionDetection = true
self.addChild(figur)
self.backgroundColor = SKColor.white
let wait1 = SKAction.wait(forDuration: 3)
let timer = SKAction.repeatForever(SKAction.sequence([wait1, SKAction.run {
self.addRechteck()
}]))
self.run(timer, withKey: "addRechteck")
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in (touches ){
let location = touch.location(in: self)
if figur.contains(location){
figur.position = location
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in (touches ) {
let location = touch.location(in: self)
if figur.contains(location){
figur.position = location
}
}
}
}
func figurDidColissionWithRectangle(figur: SKSpriteNode, rechteck: SKSpriteNode) {
print("Hit")
figur.removeFromParent()
rechteck.removeFromParent()
}
extension GameScene: SKPhysicsContactDelegate {
func didBegin(_ contact: SKPhysicsContact) {
// 1
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
}
// 2
if ((firstBody.categoryBitMask & PhysicsCategory.rechteck != 0) &&
(secondBody.categoryBitMask & PhysicsCategory.figur != 0)) {
if let rechteck = firstBody.node as? SKSpriteNode,
let figur = secondBody.node as? SKSpriteNode {
figurDidColissionWithRectangle(figur: figur, rechteck: rechteck)
}
}
}
}
In the method addRecktech(), you've told rechteckRechts' physics body to only test for contact with itself:
rechteckRechts.physicsBody?.contactTestBitMask = PhysicsCategory.rechteck //4
I suggest changing this line of code to the following, to tell it to test for contact with figur:
rechteckRechts.physicsBody?.contactTestBitMask = PhysicsCategory.figur //4
I believe because there's only a 'one-way' test for contact between rechTeck and figur in your current code, some contact events are being missed.

Program not detecting collision with Physics Body SpriteKit

I have a simple game where meteors are spawned and the player must dodge these meteors. I am trying to detect if the player (rocketship) has collided with the meteor (enemy) with PhysicsBody. However, nothing happens when they collide. The console should print "Crash detected"
Here's my code:
import SpriteKit
import GameplayKit
import UIKit
class GameScene: SKScene, SKPhysicsContactDelegate{
let player = SKSpriteNode(imageNamed: "spaceship")
let stars = SKSpriteNode(imageNamed: "stars")
let meteor = SKSpriteNode(imageNamed: "meteor")
override func didMove(to view: SKView) {
print(frame.size.width)
print(frame.size.height)
stars.position = CGPoint(x:0, y:0)
stars.zPosition = 1
player.physicsBody = SKPhysicsBody(circleOfRadius: player.size.width / 2)
player.position = CGPoint(x:0, y:-320)
player.zPosition = 4
player.physicsBody = SKPhysicsBody()
player.physicsBody?.affectedByGravity = false
player.physicsBody?.isDynamic = false
player.physicsBody?.categoryBitMask = 0
player.physicsBody?.collisionBitMask = 1
player.physicsBody?.contactTestBitMask = 1
self.addChild(player)
self.addChild(stars)
addMeteor()
}
func addMeteor() {
meteor.physicsBody = SKPhysicsBody(circleOfRadius: meteor.size.width / 2)
meteor.physicsBody?.affectedByGravity = false
meteor.setScale(0.50)
meteor.position = CGPoint(x:Int(arc4random()%300),y:Int(arc4random()%600))
//meteor.position = CGPoint(x:0 , y:0)
meteor.zPosition = 4
meteor.physicsBody?.categoryBitMask = 1
meteor.physicsBody?.collisionBitMask = 0
self.addChild(meteor)
}
func fireBullet() {
let bullet = SKSpriteNode(imageNamed: "bullet")
bullet.position = player.position
bullet.setScale(0.5)
bullet.zPosition = 3
self.addChild(bullet)
let moveBullet = SKAction.moveTo(y: self.size.height + bullet.size.height, duration: 1)
let deleteBullet = SKAction.removeFromParent()
let bulletSequence = SKAction.sequence([moveBullet, deleteBullet])
bullet.run(bulletSequence)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
fireBullet()
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches {
let pointOfTouch = touch.location(in: self)
let previousPointOfTouch = touch.previousLocation(in: self)
let amountDragged = pointOfTouch.x - previousPointOfTouch.x
player.position.x += amountDragged
}
}
func didBegin(_ contact: SKPhysicsContact) {
print("Crash detected")
}
override func update(_ currentTime: TimeInterval) {
meteor.position.y -= 6
if meteor.position.y < player.position.y - 300{
meteor.removeFromParent()
addMeteor()
}
}
}
The program should detect collision and then execute the didBegin function (which prints "Crash detected" to the console). However, when I run the program, the console doesn't have anything printed to it. Anybody know what im doing wrong? Thanks!
in didMove method add this line of code physicsWorld.contactDelegate = self. you set playerCateoryBitmask to 0. that means nothing can hit the player. use player.physicsBody?.categoryBitMask = 2 and also set your meteor a contactTestBitMask like so: meteor.physicsBody?.contactTestBitMask

Why my contact recogniser doesn't work

I made contact recogniser using physics body, category bit mask and SKPhysics contact. Unfortunately it do not work. Could You help me somehow to find mistake?
This code do not print statement Touched.
Is it because one body is SKSpriteNode and second SKShapeNode? Could we recognise contact between SpriteNode and ShapeNode?
I made all improvements like #giorashc said with SKPhysics body, but in not worked.
import Foundation
import SpriteKit
import GameplayKit
import UIKit
var TouchLevel3BadgeChecker = 0
enum BodyType:UInt32{
case TheCircle = 1
case TheEightBack = 2
}
class TouchLevel3SceneClass: SKScene, SKPhysicsContactDelegate{
var Eight = SKSpriteNode(imageNamed: "8.png")
var EightBack = SKSpriteNode(imageNamed: "8Back.png")
var counter : Int = 0;
override func didMove(to view: SKView) {
self.physicsWorld.contactDelegate = self
Eight.position = CGPoint(x: 0, y: -50)
Eight.name = "Eight"
addChild(Eight)
EightBack.position = CGPoint(x: 0, y: -50)
EightBack.name = "EightBack"
EightBack.zPosition = 2
EightBack.physicsBody? = SKPhysicsBody(texture: EightBack.texture!,
size: EightBack.texture!.size())
EightBack.physicsBody?.usesPreciseCollisionDetection = true
EightBack.physicsBody?.isDynamic = true
EightBack.physicsBody?.affectedByGravity = false
EightBack.physicsBody?.categoryBitMask = BodyType.TheEightBack.rawValue
addChild(EightBack)
Circle.position = CGPoint(x: 0, y: -25)
Circle.zPosition = 2;
Circle.fillColor = SKColor.black;
Circle.name = "Circle"
Circle.physicsBody? = SKPhysicsBody(circleOfRadius: 30)
Circle.physicsBody?.usesPreciseCollisionDetection = true
Circle.physicsBody?.isDynamic = true
Circle.physicsBody?.affectedByGravity = false
Circle.physicsBody?.categoryBitMask = BodyType.TheCircle.rawValue
Circle.physicsBody?.collisionBitMask = BodyType.TheEightBack.rawValue
Circle.physicsBody?.contactTestBitMask = BodyType.TheEightBack.rawValue
addChild(Circle)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches{
let location = t.location(in: self);
if atPoint(location).name == "Circle"{
Circle.position.x = location.x
Circle.position.y = location.y
}
}
}
func didBegin(_ contact: SKPhysicsContact) {
if contact.bodyA.categoryBitMask == BodyType.TheCircle.rawValue && contact.bodyB.categoryBitMask == BodyType.TheEightBack.rawValue{
print("Touched")
} else if contact.bodyB.categoryBitMask == BodyType.TheCircle.rawValue && contact.bodyA.categoryBitMask == BodyType.TheEightBack.rawValue{
print("Touched")
}
}
}
You are not creating the physicsBody property. Since its optional and nil setting its properties won't do anything.
create the physicsBody property with:
EightBack.physicsBody = SKPhysicsBody(...);
Circle.physicsBody = SKPhysicsBody(...);
check the example in this link: https://developer.apple.com/documentation/spritekit/skphysicsbody

Swift didBeginContact() isn't called

When I put a println() in my didBeginContact, nothing prints. Therefore, I assume that my didBeginContact doesn't work. Why? I also have an enum (Collider Type) in which rawValues of Cup and Star and listed.
This is my GameScene file.
import Foundation
import SpriteKit
class PlayScene: SKScene, SKPhysicsContactDelegate {
let cup = SKSpriteNode(imageNamed: "cup")
override func didMoveToView(view: SKView) {
/* Setup your scene here */
self.physicsWorld.contactDelegate = self
self.backgroundColor = UIColor(red: 192, green: 192, blue: 192, alpha: 1)
cup.setScale(0.5)
cup.position = CGPointMake(self.size.width + cup.size.width / 2, self.size.height/2)
self.addChild(cup)
let actualDuration = 2.0
let actualY = size.height / 2
let actionMove = SKAction.moveByX(-550, y: 0, duration: actualDuration)
let reverseActionMove = actionMove.reversedAction()
let sequence = SKAction.sequence([actionMove, reverseActionMove])
let endlessAction = SKAction.repeatActionForever(sequence)
self.cup.physicsBody = SKPhysicsBody(rectangleOfSize: self.cup.size)
self.cup.physicsBody?.dynamic = false
self.cup.physicsBody?.categoryBitMask = ColliderType.Cup.rawValue
self.cup.physicsBody?.collisionBitMask = ColliderType.Star.rawValue
//Code to keep stars in screen
//let borderBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
//borderBody.friction = 0
//self.physicsBody = borderBody
cup.runAction(endlessAction)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch: AnyObject in touches {
let star = StarNode.star(touch.locationInNode(self))
star.setScale(0.3)
self.addChild(star)
}
}
func didBeginContact(contact: SKPhysicsContact) {
println("test")
let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
switch contactMask {
case ColliderType.Star.rawValue | ColliderType.Cup.rawValue:
println("star with da cup")
default:
return
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
This is my StarNode file.
import Foundation
import UIKit
import SpriteKit
class StarNode: SKSpriteNode, SKPhysicsContactDelegate {
class func star(location: CGPoint) -> StarNode {
let sprite = StarNode(imageNamed:"star")
sprite.xScale = 0.075
sprite.yScale = 0.075
sprite.position = location
sprite.physicsBody = SKPhysicsBody(texture: SKTexture(imageNamed: "star"), size: sprite.size)
sprite.physicsBody?.dynamic = true
sprite.physicsBody?.categoryBitMask = ColliderType.Star.rawValue
sprite.physicsBody?.collisionBitMask = ColliderType.Cup.rawValue
if let physics = sprite.physicsBody {
physics.affectedByGravity = true
physics.friction = 0.12
physics.allowsRotation = true
physics.dynamic = true
physics.linearDamping = 0.75
physics.angularDamping = 0.75
sprite.name = "star"
}
return sprite
}
}
You need to set the contactTestBitMask to your SKSpriteNodes. The collisionBitMask says only which categories of bodies can collide with the node but doesn't call the didBeginContact method.
self.cup.physicsBody?.contactTestBitMask = ColliderType.Star.rawValue
//Star
sprite.physicsBody?.contactTestBitMask = ColliderType.Cup.rawValue
The contactTestBitMask documentation says it:
A mask that defines which categories of bodies cause intersection notifications with this physics body.