macoscocoaswiftsprite-kitskphysicscontact

didBeginContact works absolutely incorrect swift


I have a very simple app on sprite kit for mac os. (BTW, this code for iOS is working correctly). AppDelegate code:

import Cocoa
import SpriteKit

extension SKNode {
    class func unarchiveFromFile(file : String) -> SKNode? {
        if let path = NSBundle.mainBundle().pathForResource(file, ofType: "sks") {
        var sceneData = NSData(contentsOfFile: path, options: .DataReadingMappedIfSafe, error: nil)!
        var archiver = NSKeyedUnarchiver(forReadingWithData: sceneData)

        archiver.setClass(self.classForKeyedUnarchiver(), forClassName: "SKScene")
        let scene = archiver.decodeObjectForKey(NSKeyedArchiveRootObjectKey) as! GameScene
        archiver.finishDecoding()
        return scene
    } else {
        return nil
    }
}
}

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

@IBOutlet weak var window: NSWindow!
@IBOutlet weak var skView: SKView!

func applicationDidFinishLaunching(aNotification: NSNotification) {
    /* Pick a size for the scene */
    if let scene = GameScene.unarchiveFromFile("GameScene") as? GameScene {
        /* Set the scale mode to scale to fit the window */
        scene.scaleMode = .AspectFill

        self.skView!.presentScene(scene)

        /* Sprite Kit applies additional optimizations to improve rendering performance */
        self.skView!.ignoresSiblingOrder = true

        self.skView!.showsFPS = true
        self.skView!.showsNodeCount = true
    }
}

func applicationShouldTerminateAfterLastWindowClosed(sender: NSApplication) -> Bool {
    return true
}
}

And the scene code:

import Foundation
import SpriteKit

struct Detection {
    static var no : UInt32 = 0
    static var all : UInt32 = UInt32.max
    static var monster : UInt32 = 0b1
    static var suric : UInt32 = 0b10
    static var ninja : UInt32 = 0b100
}

func random() -> CGFloat {
    return CGFloat(Float(arc4random()) / 0xFFFFFFFF)
}

func random(#min: CGFloat, max: CGFloat) -> CGFloat {
    return random() * (max - min) + min
}


func + (left: CGPoint, right: CGPoint) -> CGPoint {
    return CGPoint(x: left.x + right.x, y: left.y + right.y)
}

func - (left: CGPoint, right: CGPoint) -> CGPoint {
    return CGPoint(x: left.x - right.x, y: left.y - right.y)
}

func * (point: CGPoint, scalar: CGFloat) -> CGPoint {
    return CGPoint(x: point.x * scalar, y: point.y * scalar)
}

func / (point: CGPoint, scalar: CGFloat) -> CGPoint {
    return CGPoint(x: point.x / scalar, y: point.y / scalar)
}

#if !(arch(x86_64) || arch(arm64))
func sqrt(a: CGFloat) -> CGFloat {
return CGFloat(sqrtf(Float(a)))
}
#endif

extension CGPoint {
func length() -> CGFloat {
    return sqrt(x*x + y*y)
}

func normalized() -> CGPoint {
    return self / length()
}
}


class GameScene: SKScene, SKPhysicsContactDelegate {

let player = SKSpriteNode(imageNamed: "player.png")

override func didMoveToView(view: SKView) {

    backgroundColor = SKColor.whiteColor()

    physicsWorld.gravity = CGVectorMake(0.0, 0.0)
    physicsWorld.contactDelegate = self
    player.position = CGPoint(x: self.size.width * 0.1, y: self.size.height / 2)
    addChild(player)


    runAction(SKAction.repeatActionForever(SKAction.sequence([SKAction.runBlock(createMonster), SKAction.waitForDuration(1)])))
}

override func mouseDown(theEvent: NSEvent) {
    let location = theEvent.locationInNode(self)

    let suric = SKSpriteNode(imageNamed: "projectile.png")
    suric.position = player.position
    suric.physicsBody = SKPhysicsBody(circleOfRadius: self.size.width / 2)
    suric.physicsBody?.categoryBitMask = Detection.suric
    suric.physicsBody?.collisionBitMask = Detection.no
    suric.physicsBody?.contactTestBitMask = Detection.monster
    suric.physicsBody?.usesPreciseCollisionDetection = true
    suric.physicsBody?.dynamic = true
    suric.physicsBody?.angularVelocity = -10.0

    let offset = location - suric.position

    if offset.x < 0 {
        return
    }

    addChild(suric)

    let direc = offset.normalized()
    let shoot = direc * 1000
    let dest = shoot + suric.position

    let move = SKAction.moveTo(dest, duration: 2.0)
    let stop = SKAction.removeFromParent()
    suric.runAction(SKAction.sequence([move, stop]))

}

func suricHit(suric : SKSpriteNode?, monster : SKSpriteNode?) {
    if suric != nil && monster != nil {
        suric!.removeFromParent()
        monster!.removeFromParent()
    }
}

func didBeginContact(contact: SKPhysicsContact) {
    var first : SKPhysicsBody
    var second : SKPhysicsBody
    if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
        first = contact.bodyA
        second = contact.bodyB
    }
    else {
        first = contact.bodyB
        second = contact.bodyA
    }

    if (first.categoryBitMask & Detection.monster != 0) && (second.categoryBitMask & Detection.suric != 0) {
        suricHit(first.node as? SKSpriteNode, monster: second.node as? SKSpriteNode)
    }
}

func createMonster() {
    let monster = SKSpriteNode(imageNamed: "monster.png")

    let y = random(min: monster.size.height / 2, size.height - monster.size.height)
    monster.position = CGPoint(x: self.size.width + monster.size.width / 2, y: y)
    monster.physicsBody = SKPhysicsBody(rectangleOfSize: monster.size, center: CGPoint(x: monster.position.x / 2, y: monster.position.y))
    monster.physicsBody?.usesPreciseCollisionDetection = true
    monster.physicsBody?.categoryBitMask = Detection.monster
    monster.physicsBody?.contactTestBitMask = Detection.suric
    monster.physicsBody?.collisionBitMask = Detection.no
    monster.physicsBody?.dynamic = true
    addChild(monster)

    let duration = random(min: 2.0, 4.0)
    let move = SKAction.moveTo(CGPoint(x: -monster.size.width / 2, y: y), duration: NSTimeInterval(duration))
    let done = SKAction.removeFromParent()

    monster.runAction(SKAction.sequence([move, done]))
}
}

And the contact function works really strange. It runs even without actual contact between suric and monster. I have no idea why this happens. Is it just my fault or just a Xcode bug?


Solution

  • Your physics body of projectile is too big. Nodes contacts through physics bodies not their actual sizes.

    suric.physicsBody = SKPhysicsBody(circleOfRadius: self.size.width / 2)
    

    change size of physics body to something smaller

    self.size.width / 2
    

    something like this

    suric.physicsBody = SKPhysicsBody(circleOfRadius: suric.size);