No collisions being detected in subclassed nodes - swift

I'm writing a game. It is not detecting any collisions, even though I can see the physics bodies as I have that view turned on.
To set-up the physics world in the game scene, I've coded the following above the class declaration:
struct PhysicsCategory {
static let None: UInt32 = 0
static let Chicken: UInt32 = 0b1
static let Edge: UInt32 = 0b10
}
Then, within the actual class of the scene:
override func didMove(to view: SKView) {
setupNodes()
setupTrial()
let playableRect = CGRect(x: 0, y: 0, width: size.width/2, height: size.height/2)
self.physicsBody = SKPhysicsBody(edgeLoopFrom: playableRect)
self.physicsWorld.contactDelegate = self
self.physicsBody!.categoryBitMask = PhysicsCategory.Edge
self.physicsBody!.contactTestBitMask = PhysicsCategory.Chicken
// This is important for handling all the custom events
enumerateChildNodes(withName: "//*", using: { node, _ in
// we need to limit this to chickens only
if let customNode = node as? CustomNodeEvents {
customNode.didMoveToScene()
}
})
}
Here is the code to detect collisions:
func didBegin(_ contact: SKPhysicsContact) {
print("something happened")
let collision = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
if collision == PhysicsCategory.Chicken | PhysicsCategory.Edge {
print("it works!")
}
}
The nodes I'd like to animate are chickens. I want the game to detect when they collide with the edges of the world above.
My chicken subclass is this:
class TargetNode: SKSpriteNode, CustomNodeEvents, InteractiveNode {
func didMoveToScene() {
isUserInteractionEnabled = true
anchorPoint = CGPoint(x: 0, y: 0)
let playableRect = CGRect(x: self.anchorPoint.x, y: self.anchorPoint.y, width: self.size.width, height: self.size.height)
physicsBody = SKPhysicsBody(edgeLoopFrom: playableRect)
physicsBody!.isDynamic = true
physicsBody!.categoryBitMask = PhysicsCategory.Chicken
physicsBody!.contactTestBitMask = PhysicsCategory.Edge | PhysicsCategory.Chicken
physicsBody!.velocity = CGVector(dx: 100, dy: 0)
}
}
EDIT: The nodes are being added to the scene using this method in the game scene file.
func generateItems(targetNumber: Int, target: Bool) {
let movingItems = true
for _ in 0...(targetNumber - 1) {
if (target) {
let name = createTarget()
let targetNode = TargetNode(imageNamed: name)
targetNode.name = name
fgNode.addChild(targetNode)
targetNode.position = generateRandomLocation()
//if movingItems { animateTargets(targetNode) }
}
}

setting your chicken to a SKPhysicsBody(edgeLoopFrom :_) is a bad idea. instead try SKPhysicsBody(rectangleOf :_) to draw a rectangle shape around your chicken, the type of shape you want to draw can vary, check the docs for more information.
Moreover to check weather the chickens have made contact with the playableRect
remove self.physicsBody!.contactTestBitMask = PhysicsCategory.Chicken because we don't want to check if the rect has made contact with the chicken we want to know if the chicken has made contact with the playable Rect. So keep contactTestBitMask on the chicken.
remove : | PhysicsCategory.Chicken from your chicken subclass, unless you want to check weather the chickens collide with each other as well.
lastly: check for collisions, if the chicken makes contact with the playableRect
func didBegin(_ contact: SKPhysicsContact) {
if contact.bodyA.categoryBitMask == PhysicsCategory.Chicken && contact.bodyB.categoryBitMask == PhysicsCategory.Edge {
print("Chickens have collided with edge")
}
}

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.

sprite kit collision not working

This is a game I have been working on. In summary there is a moving block named enemy and I want it to collide with an invisible static block called invisibleGround2. I have it printing hit when they supposedly collide but they are not colliding. Ive read every swift collision documentation by apply and others out there and I dont know whats wrong. Any help would be much appreciated!
import SpriteKit
import GameplayKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var orb = SKSpriteNode(imageNamed: "orb")
var scrollingG:scrollingGround?
var invisibleGround = SKSpriteNode(imageNamed: "invisible")
var invisibleGround2 = SKSpriteNode(imageNamed: "invisible")
let enemies = [SKSpriteNode(imageNamed: "blueE.png"), SKSpriteNode(imageNamed: "redE.png")]
let enemyCategory : UInt32 = 1
let jumperCategory : UInt32 = 1
let rotateDuration = 2
let enemyMoveSpeed = 5
let groundScrollingSpeed = 5
func createOrb(){
let orbConst = frame.size.width/2
let xConstraint = SKConstraint.positionX(SKRange(constantValue: orbConst))
orb.position = CGPoint(x: frame.size.width/2, y: 480)
orb.physicsBody = SKPhysicsBody(texture: orb.texture!, size: orb.texture!.size())
orb.constraints = [xConstraint]
self.addChild(orb)
}
func createScrollingGround () {
scrollingG = scrollingGround.scrollingNodeWithImage(imageName: "ground", containerWidth: self.size.width)
scrollingG?.scrollingSpeed = CGFloat(groundScrollingSpeed)
scrollingG?.anchorPoint = .zero
self.addChild(scrollingG!)
}
func createGround(){
invisibleGround2.size.width = 1
invisibleGround2.size.height = 1
invisibleGround2.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width:1, height: 100))
invisibleGround2.physicsBody?.isDynamic = false
invisibleGround2.position = CGPoint(x: 530, y: 191)
invisibleGround2.physicsBody?.categoryBitMask = jumperCategory
invisibleGround2.physicsBody?.collisionBitMask = enemyCategory
invisibleGround2.physicsBody?.contactTestBitMask = enemyCategory
invisibleGround2.name = "jumper"
invisibleGround.position = CGPoint(x: 0, y: 190)
invisibleGround.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: self.size.width * 3, height: 10))
invisibleGround.physicsBody?.isDynamic = false
self.addChild(invisibleGround2)
self.addChild(invisibleGround)
}
func getRandomEnemy(fromArray array:[SKSpriteNode])->SKSpriteNode{
return array[Int(arc4random_uniform(UInt32(array.count)))]
}
func spawnEnemy() {
if let enemy = getRandomEnemy(fromArray: enemies).copy() as? SKSpriteNode {
enemy.position = CGPoint(x: frame.size.width + frame.size.width/3, y: 440)
enemy.physicsBody = SKPhysicsBody(texture: enemy.texture!, size: enemy.texture!.size())
if enemy.size.width < 95 {
enemy.physicsBody?.categoryBitMask = enemyCategory
enemy.physicsBody?.collisionBitMask = jumperCategory
enemy.physicsBody?.contactTestBitMask = jumperCategory
}
enemy.name = "enemy"
self.addChild(enemy)
let moveLeft = SKAction.moveBy(x: -1500, y: 0, duration: TimeInterval(enemyMoveSpeed))
enemy.run(moveLeft)
}
}
func addEnemies () {
self.run(SKAction.repeatForever(SKAction.sequence([SKAction.run {
self.spawnEnemy()
}, SKAction.wait(forDuration: 4)])))
}
func jump() {
orb.physicsBody?.applyImpulse(CGVector(dx: 0, dy: 335))
}
func rotate() {
let rotate = SKAction.rotate(byAngle: CGFloat(M_PI * -2.55), duration: TimeInterval(rotateDuration))
let repeatAction = SKAction.repeatForever(rotate)
orb.run(repeatAction)
}
override func didMove(to view: SKView) {
createScrollingGround()
createOrb()
createGround()
rotate()
addEnemies()
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
jump()
}
override func update(_ currentTime: TimeInterval) {
if self.scrollingG != nil {
scrollingG?.update(currentTime: currentTime)
func didBegin(_ contact: SKPhysicsContact) {
let bodyA = contact.bodyA.categoryBitMask
let bodyB = contact.bodyB.categoryBitMask
if bodyA == jumperCategory && bodyB == enemyCategory {
print("hit")
} else if bodyA == enemyCategory && bodyB == jumperCategory {
print("hit 2")
}
}
}
}
}
Not an swer, but some things to check:
Have you set the scene’s physicsworld delegate property set to
self?
Move didBegin outside of update
Is the 'enemy' sprite's width < 95?
Add a print("Contact detected") as the first line of your relocated didBegin so you at least know that some contact has been detected.
See my answer here https://stackoverflow.com/a/43605825/1430420 for a simple SK collision demo which might help - I think it needs updates to Swift 4 which I'll try and do.

How do I create a collision with SpriteKit with an already created SKSpriteNode?

I want the pacman to restart from its original position when it collides with blinky, that is moving.
How can I make them collide considering I have already declared them?
You move the pacman, but blinky moves alone. I want it to work like the pacman game.
public class PacmanScene: SKScene {
let playerSpeed: CGFloat = 40.0
var pacman: SKSpriteNode?
var playerTextures: [SKTexture] = []
var lastTouch: CGPoint? = nil
var blinky: SKSpriteNode?
var clyde: SKSpriteNode?
var inky: SKSpriteNode?
var pinky: SKSpriteNode?
override public init(size: CGSize) {
let pacmanTexture = SKTexture(imageNamed: "pacman01.png")
pacman = SKSpriteNode(texture: pacmanTexture)
pacman?.name = "pacman"
pacman?.position = CGPoint(x:30, y:30)
pacman?.zPosition = 1.0
pacman?.physicsBody = SKPhysicsBody(texture: pacmanTexture, size: CGSize(width: (pacman?.size.width)!, height: (pacman?.size.height)!))
pacman?.physicsBody?.allowsRotation = true
pacman?.physicsBody?.affectedByGravity = false
pacman?.physicsBody?.mass = 2
let blinkyTexture = SKTexture(imageNamed: "blinky.png")
blinky = SKSpriteNode(texture: blinkyTexture)
blinky?.name = "blinky"
blinky?.position = CGPoint(x: 15, y: 60)
blinky?.zPosition = 1.0
blinky?.physicsBody = SKPhysicsBody(texture: pacmanTexture, size: CGSize(width: (blinky?.size.width)!, height: (blinky?.size.height)!))
blinky?.physicsBody?.allowsRotation = false
blinky?.physicsBody?.affectedByGravity = false
blinky?.physicsBody?.mass = 1000
super.init(size: size)
addChild(pacman!)
addChild(blinky!)
override public func didMove(to view: SKView) {
let bmoveUp = SKAction.moveBy(x: 0, y: 450, duration: 4.0)
let bmoveRight = SKAction.moveBy(x:20, y:0, duration: 1.0)
let bmoveDown = SKAction.moveBy(x:0, y: -450, duration: 4.0)
let bmoveLeft = SKAction.moveBy(x:-20, y:0, duration: 1.0)
let bsequence = SKAction.sequence([bmoveUp, bmoveRight, bmoveDown, bmoveLeft])
let bendlessAction = SKAction.repeatForever(bsequence)
blinky?.run(bendlessAction)
}
If iv got this right you want your "blinky" to follow your "pacman" to do this you would have to work out the position of the pacman then add an SKAction to your blinky to move to that position.
Try something like this
//Speed blinky moves
let blinkySpeed = 100
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
updateBlinky()
}
func updateBlinky() {
//Set the point that blinky moves to
let point = CGPoint(x: pacman.position.x, y: pacman.position.y)
//Get the distance its got to travel
let distance = distanceBetweenPoints(first: pacman.position, second: blinky.position)
//Get the time is got to take from the speed and distance
let time = distance / blinkySpeed
//Create and run the action
let action = SKAction.move(to: point, duration: TimeInterval(time))
blinky.run(action)
}
//work out the distance between the sprites
func distanceBetweenPoints(first: CGPoint, second: CGPoint) -> Int {
return Int(hypot(second.x - first.x, second.y - first.y))
}
The end result would be something like this
Edit:
Okay I think from your question you already have "blinky" moving you just want to detect collisions.
First you need to add the SKPhysicsContactDelegate to your class
public class PacmanScene: SKScene, SKPhysicsContactDelegate {
Then you need to add a category bit mask to both the sprites then handle the collisions in didBegin(_ contact: SKPhysicsContact) method.
What I would do
//Create Physics category struct
struct PhysicsCategory {
static let pacman : UInt32 = 0x1 << 1
static var blinky : UInt32 = 0x1 << 2
}
Then where you are setting up pacman and blinky set the category bitmask
//Set the category bit mask for pacman
pacman?.physicsBody?.categoryBitMask = PhysicsCategory.pacman
//Set what categories you want to test contact
pacman?.physicsBody?.contactTestBitMask = PhysicsCategory.blinky
//Set what categories you want to collide with each other
pacman?.physicsBody?.collisionBitMask = PhysicsCategory.blinky
//Set the category bit mask for blinky
blinky?.physicsBody?.categoryBitMask = PhysicsCategory.blinky
//Set what categories you want to test contact
blinky?.physicsBody?.contactTestBitMask = PhysicsCategory.pacman
//Set what categories you want to collide with each other
blinky?.physicsBody?.collisionBitMask = PhysicsCategory.pacman
Then you would need to implement the didBegin(_ contact: SKPhysicsContact) method to handle the collisions
func didBegin(_ contact: SKPhysicsContact) {
//Work out which contact was pacman
let other = contact.bodyA.categoryBitMask == PhysicsCategory.pacman ? contact.bodyB : contact.bodyA
//Test what it hit
switch other.categoryBitMask {
case PhysicsCategory.blinky:
print("pacman hit blinky")
//Move pacman
pacman?.position = CGPoint(x:30, y:30)
default:
break
}
}
Hope this helps

How can I require two collisions on one of my nodes before removing it from screen?

I have a collision based game with missiles and bombs. Everything works perfect right now with 1 collision and it removes the node from screen.
Although I want to be able to make bomb1 of my "bombs" harder to destroy so I would like to require it being hit twice. How can I do this?
Here are the bombs
func enemies() {
let bomb1 = SKSpriteNode(imageNamed: "Bomb1")
let bomb2 = SKSpriteNode(imageNamed: "bomb2")
let bomb3 = SKSpriteNode(imageNamed: "Bomb3")
let enemy = [bomb1, bomb2, bomb3]
// Enemy Physics
for bomb in enemy {
bomb1.size = CGSize(width: 55, height: 55)
bomb2.size = CGSize(width: 55, height: 70)
bomb3.size = CGSize(width: 30, height: 30)
bomb.physicsBody = SKPhysicsBody(circleOfRadius: bomb.size.width / 4)
bomb.physicsBody?.categoryBitMask = PhysicsCategory.enemy
bomb.physicsBody?.collisionBitMask = PhysicsCategory.missile | PhysicsCategory.airDefense
bomb.physicsBody?.contactTestBitMask = PhysicsCategory.missile | PhysicsCategory.airDefense
bomb.physicsBody?.affectedByGravity = false
bomb.physicsBody?.dynamic = true
bomb1.name = "enemy1"
bomb2.name = "enemy2"
bomb3.name = "enemy3"
}
Here is the function thats called when the bomb hits the missile.
func collisionWithMissile(enemy : SKSpriteNode, missile : SKSpriteNode) {
enemy.physicsBody?.dynamic = true
enemy.physicsBody?.affectedByGravity = true
missile.physicsBody?.affectedByGravity = true
enemy.physicsBody?.mass = 10.0
missile.physicsBody?.mass = 5.0
enemy.removeAllActions()
missile.removeAllActions()
enemy.removeFromParent()
missile.removeFromParent()
enemy.physicsBody?.contactTestBitMask = 0
enemy.physicsBody?.collisionBitMask = 0
enemy.name = nil
score++
scoreLbl.text = "\(score)"
}
This may help with the error Im getting
func didBeginContact(contact: SKPhysicsContact) {
if contact.bodyA.node != nil && contact.bodyB.node != nil {
let firstBody = contact.bodyA.node as! Bomb
let secondBody = contact.bodyB.node as! SKSpriteNode
// Bomb1 **********
if ((firstBody.name == "enemy1") && (secondBody.name == "missile")) {
collisionWithMissile(firstBody, missile: secondBody)
atomicExplosion(contact.bodyA.node!.position)
missileExplosion(contact.bodyB.node!.position)
}
You can subclass SKSpriteNode for your bombs and add a collision counter to this class. Inside your collisionWithMissile func you check the value of this counter and remove the bomb if a given value is reached otherwise you increase the counter.
Your subclass can look like this
class Bomb: SKSpriteNode {
var hitCount: Int
init(imageNamed: String, hitCount: Int) {
self.hitCount = hitCount
let texture = SKTexture(imageNamed: imageNamed)
super.init(texture: texture, color: UIColor.clearColor(), size: texture.size())
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
The init method has hitCount as a parameter, so you can create easily Bombs with different strength. Your collisionWithMissile method could be like this. Just fill the if-else parts with your needed code.
func collisionWithMissile(enemy : Bomb, missile : SKSpriteNode) {
if enemy.hitCount <= 0 {
enemy.removeFromParent()
} else {
enemy.hitCount -= 1
}
}

Ending a game when two nodes collide

I have two separate nodes with their own physics bodies, and when they collide, an SKScene with the high score and replay button should present itself. This is how my scene is called:
func didBeginContact(contact: SKPhysicsContact) {
gameOver()
print("gameOver")
}
And this is how my physics bodies for my nodes are set up:
func createDown(position: CGPoint) -> SKNode {
let circleNode = SKSpriteNode()
let circle = SKSpriteNode(imageNamed: "first#2x")
circleNode.position = CGPointMake(position.x, position.y)
circleNode.physicsBody = SKPhysicsBody(circleOfRadius: 30)
circleNode.physicsBody?.dynamic = false
circle.size = CGSize(width: 75, height: 75)
circleNode.addChild(circle)
circleNode.name = "circleNode"
circle.name = "CIRCLE"
let up = SKAction.moveByX(0, y: -9000, duration: 100)
physicsBody?.categoryBitMask = blackCategory
circleNode.runAction(up)
return circleNode
}
func playerPhysics() {
player.physicsBody = SKPhysicsBody(circleOfRadius: 30)
player.physicsBody?.affectedByGravity = false
player.physicsBody?.categoryBitMask = playerCategory
player.physicsBody?.contactTestBitMask = blackCategory
}
And here is my gameOver function:
func gameOver() {
gameEnd = true
let reveal = SKTransition.fadeWithDuration(1)
let scene = GameOver(size: self.scene!.size)
view!.presentScene(scene, transition: reveal)
}
Am I missing something? Will post more code if necessary.
Just use intersectsNode and call gameOver() when the two nodes collide.
if yourNode.intersectsNode(yourSecondNode) {
gameOver()
}
SKNode Class Reference: SKNode
You need to add the physics world as a contact delegate for your methods to work.
class GameScene: SKScene, SKPhysicsContactDelegate {
override func didMoveToView(view: SKView) {
physicsWorld.contactDelegate = self
And like Whirlwind said, you need to set your categoryBitMask and contactTestBitMask for the circle node.
Then everything should work. I hope I could help.