didBeginContact works without contact swift - cocoa

I'm writing SpriteKit app for Mac OS. I have two objects (projectile and a monster):
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
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]))
Monster code:
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)
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]))
Detection structure:
struct Detection {
static var no : UInt32 = 0
static var all : UInt32 = UInt32.max
static var monster : UInt32 = 0b1
static var suric : UInt32 = 0b10
}
And then I try to check if there was a contact between these 2 objects:
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)
}
}
It works even if they didn't actually make a contact. E.g., the projectile is near the top of the window, the monster is at the bottom, but this function works.
EDIT:
I added this code to didBeginContact:
let nodeA = (first.node as! SKSpriteNode)
let nodeB = (second.node as! SKSpriteNode)
println("\(nodeA.position)\t\(nodeA.size)")
println("\(nodeB.position)\t\(nodeB.size)")
println((contact.contactPoint))
println()
The example of out:
(510.201293945312, 285.025665283203) (54.0, 80.0)
(532.54345703125, 378.878845214844) (20.0, 20.0)
(852.469543457031, 530.415222167969)
As you can see, the x position of nodes are 510 and 532, but the contact x point is 852. I just have no idea how and why. Does anybody have a solution?

You're missing an extra set of parenthesis in the if statement.
if ((first.categoryBitMask & Detection.monster != 0) && (second.categoryBitMask & Detection.suric != 0)) {
suricHit(first.node as? SKSpriteNode, monster: second.node as? SKSpriteNode)
}
PS I followed this tutorial and put my project on GitHub, check it out!

Related

Detect collision between the player and an object with Sprite kit

I am making a game with sprite kit in xcode and i want let the game finish when the player touch the monster, do you know how to use categorymask?
here is part of the code :
import SpriteKit
import GameplayKit
import CoreMotion
class GameScene: SKScene, SKPhysicsContactDelegate {
var player : SKSpriteNode!
var playerCategory : UInt32 = 0x1 << 0
var gameTimer2 : Timer!
var possiblealien = ["alien", "alien2", "alien3"]
let alienCategory : UInt32 = 0x1 << 1
override func didMove(to view: SKView) {
player = SKSpriteNode (imageNamed: "player")
player.position = CGPoint (x: self.frame.size.width / 2 - 500 , y:
player.size.height / 2 - 560)
player.size = CGSize(width: player.size.width * 6 ,
height: player.size.height * 6)
self.addChild(player)
player.zPosition = 2
self.physicsWorld.gravity = CGVector (dx: 0 , dy: 0 )
self.physicsWorld.contactDelegate = self
player.physicsBody?.contactTestBitMask = playerCategory
player.physicsBody? = SKPhysicsBody(rectangleOf: player.size)
player.physicsBody?.isDynamic = true
player.physicsBody?.categoryBitMask = monsterCategory
player.physicsBody?.collisionBitMask = 0
gameTimer2 = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(addalien), userInfo: nil, repeats: true)
motionManager.accelerometerUpdateInterval = 0.2
motionManager.startAccelerometerUpdates(to: OperationQueue.current!) { ( data: CMAccelerometerData?, error:Error?)in
if let accelerometerData = data {
let acceleration = accelerometerData.acceleration
self.xAcceleration = CGFloat(acceleration.x) * 0.65 + self.xAcceleration * 0.25
}
}
#objc func addalien () {
possiblealien = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: possiblealien) as! [String]
let alien = SKSpriteNode(imageNamed: possiblealien[0])
let randomalienposition = GKRandomDistribution(lowestValue: -480, highestValue: +800 )
let position = CGFloat(randomalienposition.nextInt())
alien.position = CGPoint (x: position - 200 , y: self.frame.size.height * 0.5 + alien.size.height)
alien.physicsBody = SKPhysicsBody(rectangleOf: alien.size)
alien.physicsBody?.isDynamic = true
alien.physicsBody?.categoryBitMask = alienCategory
alien.physicsBody?.contactTestBitMask = playerCategory
alien.physicsBody?.collisionBitMask = 0
alien.zPosition = 2
alien.size = CGSize(width: alien.size.width * 1 ,
height: alien.size.height * 1)
self.addChild(alien)
let animationduration:TimeInterval = 6
var actionArray = [SKAction]()
actionArray.append(SKAction.move(to: CGPoint(x: position, y: -1000 ),duration: animationduration))
actionArray.append(SKAction.removeFromParent())
alien.run(SKAction.sequence(actionArray))
}
I see your problem, you need to declare it's physics body in a Physics Category, Otherwise, there would be no body for it at all
struct PhysicsCategory {
static let None:UInt32 = 0
static let All:UInt32 = UInt32.max
static let Player:UInt32 = 0b1
Also, The contactbitmasks should be what they will collide with along with collisiontestbitmask. catergorybitmask should be the category of the sprite in the struct
ex:
let alien = SKSpritenode()
alien.physicsBody?.categoryBitMask = PhysicsCategory.Alien
alien.physicsBody?.contactBitMask = PhysicsCategory.Player
alien.physicsBody?.collisionTestBitMask = PhysicsCategory.Player
Also, you can use | to declare if they will collide with multiple things

Swift 2 PhysicsBody not working

i have a code where i two spaceships floating around the screen however they can't break through the top of the screen and the motto. however they can fly through i sides. i've look at endless about of sites and done some many different types of skphysicsbodys but none of them help.
code example.
class GameScene: SKScene, SKPhysicsContactDelegate {
var player = SKSpriteNode(imageNamed: "spaceship.png")
var player2 = SKSpriteNode(imageNamed: "spaceship.png")
var timer = NSTimer()
var tapsValid:Bool?
var playerRight:Bool?
var playerChange:Bool?
override func didMoveToView(view: SKView) {
let sceneBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
sceneBody.friction = 0
self.physicsBody = sceneBody
physicsWorld.gravity = CGVector(dx: 0.0, dy: 0.0)
tapsValid = true
playerRight = true
self.scene?.backgroundColor = UIColor.whiteColor()
player.position = CGPointMake(self.size.width / 2, self.size.height / 1.8 + 280)
player.physicsBody = SKPhysicsBody(rectangleOfSize: player.size)
player.physicsBody?.dynamic = false
player.physicsBody?.affectedByGravity = false
player.physicsBody?.restitution = 1
player.physicsBody?.friction = 0
player.physicsBody?.linearDamping = 0
player.physicsBody?.angularDamping = 0
player2.position = CGPointMake(self.size.width / 2, self.size.height / 14)
player2.physicsBody = SKPhysicsBody(rectangleOfSize: player2.size)
player2.physicsBody?.affectedByGravity = false
player2.physicsBody?.dynamic = false
player2.physicsBody?.restitution = 1
player2.physicsBody?.friction = 0
player.physicsBody?.linearDamping = 0
player.physicsBody?.angularDamping = 0
self.addChild(player)
self.addChild(player2)
can anyone help me out here. i just want to add physics to the edges of the screen. Thanks,
You should define your colliderTypes and categories for your nodes. That way you can check to see what hits what
enum ColliderType: UInt32 {
case Spaceship = 0
case CornerCategory = 1
}
self.physicsBody!.categoryBitMask = ColliderType.CornerCategory.rawValue
player.physicsBody?.contactTestBitMask = ColliderType.CornerCategory.rawValue
player2.physicsBody?.contactTestBitMask = ColliderType.CornerCategory.rawValue
func didBeginContact(contact: SKPhysicsContact) {
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
//If it is true. Means that bodyA is the spaceship. Because it's 0
firstBody = contact.bodyA
secondBody = contact.bodyB
}else{
//Either way. firstBody will always be the spaceship
firstBody = contact.bodyB
secondBody = contact.bodyA
}
if firstBody.categoryBitMask == ColliderType. Spaceship.rawValue && secondBody.categoryBitMask == ColliderType.CornerCategory.rawValue{
//means that Spaceship and wall touches
print("Spaceship hits wall")
}
}

Use of unresolved identifier. (doesn't make sense)

I am making a game using Sprite Kit in swift that has objects falling down the screen that need to be caught by the player(Labeled Person) at the bottom. I am trying do remove the falling objects from the screen when they contact the player but I cant call on the falling object (labeled Ice) inside of the function did begin contact. I am able to call on other objects like the person or the score label but not the object falling down the screen (Labeled Ice). Why is this I will post my full code below. This has really been bugging me. (ps. I am given the error "use of unresolved identifier")
import SpriteKit
struct physicsCatagory {
static let person : UInt32 = 0x1 << 1
static let Ice : UInt32 = 0x1 << 2
static let IceTwo : UInt32 = 0x1 << 3
static let IceThree : UInt32 = 0x1 << 4
static let Score : UInt32 = 0x1 << 5
}
class GameScene: SKScene, SKPhysicsContactDelegate {
var scorenumber = Int()
var person = SKSpriteNode(imageNamed: "Person")
let Score = SKSpriteNode()
var ScoreLable = SKLabelNode()
override func didMoveToView(view: SKView) {
self.scene?.backgroundColor = UIColor.purpleColor()
physicsWorld.contactDelegate = self
self.scene?.size = CGSize(width: 640, height: 1136)
Score.size = CGSize(width: 648, height: 1)
Score.position = CGPoint(x: 320, y: -90)
Score.physicsBody = SKPhysicsBody(rectangleOfSize: Score.size)
Score.physicsBody?.affectedByGravity = false
Score.physicsBody?.dynamic = false
Score.physicsBody?.categoryBitMask = physicsCatagory.Score
Score.physicsBody?.collisionBitMask = 0
Score.physicsBody?.contactTestBitMask = physicsCatagory.IceThree
Score.color = SKColor.blueColor()
self.addChild(Score)
person.position = CGPointMake(self.size.width/2, self.size.height/12)
person.setScale(0.4)
person.physicsBody = SKPhysicsBody(rectangleOfSize: person.size)
person.physicsBody?.affectedByGravity = false
person.physicsBody?.categoryBitMask = physicsCatagory.person
person.physicsBody?.contactTestBitMask = physicsCatagory.Ice
person.physicsBody?.collisionBitMask = physicsCatagory.Ice
person.physicsBody?.dynamic = false
ScoreLable.position = CGPoint(x: self.frame.width / 2, y: 1000)
ScoreLable.text = "\(scorenumber)"
ScoreLable.fontColor = UIColor.yellowColor()
ScoreLable.fontSize = 100
ScoreLable.fontName = "Zapfino "
self.addChild(ScoreLable)
var IceThreeTimer = NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: ("spawnThirdIce"), userInfo: nil, repeats: true)
self.addChild(person)
}
func didBeginContact(contact: SKPhysicsContact) {
let firstBody = contact.bodyA
let secondBody = contact.bodyB
if firstBody.categoryBitMask == physicsCatagory.person && secondBody.categoryBitMask == physicsCatagory.IceThree || firstBody.categoryBitMask == physicsCatagory.IceThree && secondBody.categoryBitMask == physicsCatagory.person{
scorenumber++
ScoreLable.text = "\(scorenumber)"
CollisionWithPerson(firstBody.node as! SKSpriteNode, Person: secondBody.node as! SKSpriteNode)
}
if firstBody.categoryBitMask == physicsCatagory.Score && secondBody.categoryBitMask == physicsCatagory.IceThree ||
firstBody.categoryBitMask == physicsCatagory.IceThree && secondBody.categoryBitMask == physicsCatagory.Score{
self.view?.presentScene(EndScene())
}
}
func CollisionWithPerson (Ice: SKSpriteNode, Person: SKSpriteNode){
Person.removeFromParent()
}
func spawnThirdIce(){
var Ice = SKSpriteNode(imageNamed: "Ice")
Ice.setScale(0.9)
Ice.physicsBody = SKPhysicsBody(rectangleOfSize: Ice.size)
Ice.physicsBody?.categoryBitMask = physicsCatagory.IceThree
Ice.physicsBody?.contactTestBitMask = physicsCatagory.person | physicsCatagory.Score
Ice.physicsBody?.affectedByGravity = false
Ice.physicsBody?.dynamic = true
let MinValue = self.size.width / 8
let MaxValue = self.size.width - 20
let SpawnPoint = UInt32(MaxValue - MinValue)
Ice.position = CGPoint(x: CGFloat(arc4random_uniform(SpawnPoint)), y: self.size.height)
self.addChild(Ice)
let action = SKAction.moveToY(-85, duration: 2.5)
let actionDone = SKAction.removeFromParent()
Ice.runAction(SKAction.sequence([action,actionDone]))
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let location = touch.locationInNode(self)
person.position.x = location.x
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let location = touch.locationInNode(self)
person.position.x = location.x
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}

physicsBody collision with another physicsBody

Okay, so I have a really specific problem and I hope I can help you. I will attach an image to clarify what I'm talking about.
redball.physicsBody is spawning above boss.physicsBody - falling (through) the boss. Colliding with whitebar.physicsBody - bouncing back up and once again colliding with boss.physicsBody. THIS time, i want to fire an event that notices when the ball is bouncing back up and hitting the boss.
Currently. Boss & Ball shares the same collisionBitMask, so that they can pass through each other. When the ball collides with the bar, I'm trying to add ball.physicsBody?.contactTestBitMask = PhysicsCategory.boss.rawValue to the ball. So that i can notice the collision on the way back up between ball & boss. However this does not seem to work.
Does anyone have a solution to this weird problem? Super thankful for some help.
Sidenote: If someone can figure out a clever title, let me know and ill edit it!
EDIT: Adding code
enum PhysicsCategory : UInt32 {
case bar = 1
case ball = 2
case boss = 4
case noCollision = 8
}
class GameScene: SKScene, SKPhysicsContactDelegate {
var score = Int()
var background = SKSpriteNode(imageNamed: "background.png")
var bar = SKSpriteNode(imageNamed: "bar.png")
var boss1 = SKSpriteNode(imageNamed: "boss1.png")
override func didMoveToView(view: SKView) {
self.scene?.size = CGSize(width: 640, height:1136)
physicsWorld.contactDelegate = self
background.position = CGPoint(x: self.frame.width / 2, y: self.frame.height / 2)
background.zPosition = -20
self.addChild(background)
bar.position = CGPoint(x: self.frame.width / 2, y: self.frame.height / 8)
bar.physicsBody = SKPhysicsBody(rectangleOfSize: CGSize(width: bar.size.width, height: bar.size.height))
bar.physicsBody?.categoryBitMask = PhysicsCategory.bar.rawValue
bar.physicsBody?.contactTestBitMask = PhysicsCategory.ball.rawValue
bar.physicsBody?.collisionBitMask = PhysicsCategory.bar.rawValue
bar.physicsBody?.affectedByGravity = false
bar.physicsBody?.dynamic = false
self.addChild(bar)
boss1.position = CGPoint(x: self.frame.width, y: self.frame.height - boss1.size.height / 2)
boss1.physicsBody = SKPhysicsBody(rectangleOfSize: CGSize(width: boss1.size.width, height: boss1.size.height))
boss1.physicsBody?.categoryBitMask = PhysicsCategory.boss.rawValue
boss1.physicsBody?.contactTestBitMask = PhysicsCategory.ball.rawValue
boss1.physicsBody?.collisionBitMask = PhysicsCategory.noCollision.rawValue
boss1.physicsBody?.affectedByGravity = false
boss1.physicsBody?.dynamic = false
boss1.zPosition = 5
self.addChild(boss1)
boss1.runAction(SKAction.moveToX(self.frame.width / 2, duration: 1))
let spawnBallsAction = SKAction.sequence([SKAction.waitForDuration(2), SKAction.runBlock(spawnBalls)])
self.runAction(SKAction.repeatActionForever(spawnBallsAction))
}
func didBeginContact(contact: SKPhysicsContact) {
let firstBody : SKPhysicsBody = contact.bodyA
let secondBody : SKPhysicsBody = contact.bodyB
if (firstBody.categoryBitMask & PhysicsCategory.bar.rawValue == PhysicsCategory.bar.rawValue &&
secondBody.categoryBitMask & PhysicsCategory.ball.rawValue == PhysicsCategory.ball.rawValue) {
CollisionWithBar(firstBody.node as! SKSpriteNode, ball: secondBody.node as! SKSpriteNode)
}
if (firstBody.categoryBitMask & PhysicsCategory.ball.rawValue == PhysicsCategory.ball.rawValue &&
secondBody.categoryBitMask & PhysicsCategory.boss.rawValue == PhysicsCategory.boss.rawValue) {
CollisionWithBoss(firstBody.node as! SKSpriteNode, boss: secondBody.node as! SKSpriteNode)
}
}
func CollisionWithBar(bar: SKSpriteNode, ball: SKSpriteNode) {
ball.physicsBody?.applyImpulse(CGVectorMake(0, 500))
ball.physicsBody?.contactTestBitMask = PhysicsCategory.boss.rawValue //Trying to solve the problem, aint working
}
func CollisionWithBoss(ball: SKSpriteNode, boss: SKSpriteNode) {
NSLog("Ball hit the boss")
}
func spawnBalls(){
let ball = SKSpriteNode(imageNamed: "ball.png")
let MinValue = self.frame.width / 8
let MaxValue = self.frame.width - 20
let SpawnPoint = UInt32(MaxValue - MinValue)
ball.position = CGPoint(x: CGFloat(arc4random_uniform(SpawnPoint)), y: self.frame.height - 128)
ball.zPosition = 50
//Physics
ball.physicsBody = SKPhysicsBody(circleOfRadius: ball.frame.height / 2)
ball.physicsBody?.categoryBitMask = PhysicsCategory.ball.rawValue
ball.physicsBody?.contactTestBitMask = PhysicsCategory.bar.rawValue
ball.physicsBody?.collisionBitMask = PhysicsCategory.noCollision.rawValue
ball.physicsBody?.affectedByGravity = true
ball.physicsBody?.dynamic = true
self.addChild(ball)
}
This is how contacts should be done:
Upon creating a ball set the contact category to this:(which you have)
ball.physicsBody?.contactTestBitMask = PhysicsCategory.bar.rawValue
Make sure the boss contacts nobody:
boss1.physicsBody?.contactTestBitMask = PhysicsCategory.noCollision.rawValue
Then on Contact, you want to do this:
func didBeginContact(contact: SKPhysicsContact) {
let firstBody : SKPhysicsBody = (contact.bodyA.categoryBitMask <= contact.bodyB.categoryBitMask) ? contact.bodyA : contact.bodyB
let secondBody : SKPhysicsBody = (contact.bodyA.categoryBitMask >= contact.bodyB.categoryBitMask) ? contact.bodyA : contact.bodyB
if (firstBody.categoryBitMask & PhysicsCategory.bar.rawValue == PhysicsCategory.bar.rawValue &&
secondBody.categoryBitMask & PhysicsCategory.ball.rawValue == PhysicsCategory.ball.rawValue) {
//This should be inside CollisionWithBar
ball.physicsBody?.contactTestBitMask = PhysicsCategory.bar.rawValue | PhysicsCategory.boss.rawValue
//the ball has hit the bar, so lets enable hitting on the boss
//normally we would only want to do this once, but since this is
//tiny, it would be more time to wrap it in ifs and putting guards on it
boss1.physicsBody?.contactTestBitMask = PhysicsCategory.ball.rawValue
CollisionWithBar(firstBody.node as! SKSpriteNode, ball: secondBody.node as! SKSpriteNode)
}
if (firstBody.categoryBitMask & PhysicsCategory.ball.rawValue == PhysicsCategory.ball.rawValue &&
secondBody.categoryBitMask & PhysicsCategory.boss.rawValue == PhysicsCategory.boss.rawValue) {
//This should be inside CollisionWithBoss
ball.physicsBody?.contactTestBitMask = PhysicsCategory.bar.rawValue
CollisionWithBoss(firstBody.node as! SKSpriteNode, boss: secondBody.node as! SKSpriteNode)
}
}
Which basically says, if the ball hits the bar, enable boss hitting, if the ball hits the boss, disable boss hitting
Edit: Upon the realization of having multiple balls, we have to change some things
Make sure the boss contacts ball upon creation again:
boss1.physicsBody?.contactTestBitMask = PhysicsCategory.ball.rawValue
Now we need to create a new category
enum PhysicsCategory : UInt32 {
case bar = 1
case ball = 2
case boss = 4
case noCollision = 8
case initialBall = 16
}
In the beginning, assign the category of ball to this:
ball.physicsBody?.categoryBitMask = initialBall
Then your CollissionWithBar will look like this:
func CollisionWithBar(bar: SKSpriteNode, ball: SKSpriteNode) {
ball.physicsBody?.applyImpulse(CGVectorMake(0, 500))
ball.physicsBody?.contactTestBitMask = PhysicsCategory.bar.rawValue | PhysicsCategory.boss.rawValue
ball.physicsBody?.categoryBitMask = PhysicsCategory.ball.rawValue
}
And your CollissionWithBolls will look like this:
func CollisionWithBoss(ball: SKSpriteNode, boss: SKSpriteNode) {
ball.physicsBody?.contactTestBitMask = PhysicsCategory.bar.rawValue
}
Finally, on Contact, you want to do this:
func didBeginContact(contact: SKPhysicsContact) {
let firstBody : SKPhysicsBody = (contact.bodyA.categoryBitMask <= contact.bodyB.categoryBitMask) ? contact.bodyA : contact.bodyB
let secondBody : SKPhysicsBody = (contact.bodyA.categoryBitMask >= contact.bodyB.categoryBitMask) ? contact.bodyA : contact.bodyB
if (firstBody.categoryBitMask & PhysicsCategory.bar.rawValue == PhysicsCategory.bar.rawValue &&
secondBody.categoryBitMask & PhysicsCategory.ball.rawValue == PhysicsCategory.ball.rawValue) {
CollisionWithBar(firstBody.node as! SKSpriteNode, ball: secondBody.node as! SKSpriteNode)
}
if (firstBody.categoryBitMask & PhysicsCategory.ball.rawValue == PhysicsCategory.ball.rawValue &&
secondBody.categoryBitMask & PhysicsCategory.boss.rawValue == PhysicsCategory.boss.rawValue) {
CollisionWithBoss(firstBody.node as! SKSpriteNode, boss: secondBody.node as! SKSpriteNode)
}
}

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?
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);

Resources