RemoveFromParent one of multiple node with same name after contact - ios

I got GameScene with mass similiar nodes. After my hero contact with one of that nodes (with same name and parameters), i need to remove node that he contact.
func didBegin(_ contact: SKPhysicsContact) {
var firstBody = SKPhysicsBody()
var secondBody = SKPhysicsBody()
if contact.bodyA.node?.name == "redCircle" {
firstBody = contact.bodyA
secondBody = contact.bodyB}
else {
firstBody = contact.bodyB
secondBody = contact.bodyA}
if firstBody.node?.name == "redCircle" && secondBody.node?.name == "indicatorBody" {
candlesCounter = candlesCounter + 1
centerBody.removeFromParent()
}}
This how i do it, but after contact, any random node is removed from the game scene. It is necessary to remove the one that contact the hero.

Related

How to detect different types of collisions in SpriteKit

I have two different types of collisions I want to detect in SpriteKit:
When ballOne hits goalOne and when ballTwo hits goalTwo:
So far I've added my different physics categories:
struct physicsCategory {
static let ballOne :UInt32 = 0x1 << 0
static let goalOne :UInt32 = 0x1 << 1
static let ballTwo :UInt32 = 0x1 << 2
static let goalTwo :UInt32 = 0x1 << 3
}
And here's the relevant parts of my class:
class gameScene: SKScene, SKPhysicsContactDelegate {
var borderBody:SKPhysicsBody!
var myscore:Int = 0
var opponentScore:Int = 10
override func sceneDidLoad() {
super.sceneDidLoad()
physicsWorld.contactDelegate = self
}
func didBegin(_ contact: SKPhysicsContact) {
let firstBody = contact.bodyA.node as! SKSpriteNode
let secondBody = contact.bodyB.node as! SKSpriteNode
if ((firstBody.name == "ballOne") && (secondBody.name == "goalOne")) {
collisionGoalOne(BallOne: firstBody, GoalOne: secondBody)
}
else {
}
}
func collisionGoalOne(BallOne: SKSpriteNode, GoalOne: SKSpriteNode) {
addScore()
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
let touchLocation = touch!.location(in: self)
let ballOne = childNode(withName: "ballOne") as! SKSpriteNode
ballOne.physicsBody?.categoryBitMask = physicsCategory.ballOne
ballOne.physicsBody?.collisionBitMask = physicsCategory.goalOne
ballOne.physicsBody?.contactTestBitMask = physicsCategory.goalOne
ballOne.physicsBody?.isDynamic = true
let goalOne = childNode(withName: "goalOne") as! SKSpriteNode
goalOne.physicsBody?.categoryBitMask = physicsCategory.goalOne
goalOne.physicsBody?.collisionBitMask = physicsCategory.ballOne
goalOne.physicsBody?.contactTestBitMask = physicsCategory.ballOne
goalOne.physicsBody?.isDynamic = false
}
}
Right now the app crashes when the ball collides with the wall on
let firstBody = contact.bodyA.node as! SKSpriteNode
with "Could not cast value of type 'appName.gameScene' (0x105320) to 'SKSpriteNode' (0x1877b5c)."
Would love to understand why this crash is happening and if this is even a good approach to collision handling. Thank you.
FirstBody might not be able to be cast to SKSpriteNode, so only create that variable after you check which nodes collide.
Note that ballOne could be either bodyA or bodyB so change your code to check for both possibilities.
I changed your code a bit. Hope this helps
if ((contact.bodyA.node?.name == "ballOne") && (contact.bodyB.node?.name == "goalOne")) {
let firstBody = contact.bodyA.node as! SKSpriteNode
let secondBody = contact.bodyB.node as! SKSpriteNode
collisionGoalOne(BallOne: firstBody, GoalOne: secondBody)
} else if ((contact.bodyA.node?.name == "goalOne") && (contact.bodyB.node?.name == "ballOne")) {
let firstBody = contact.bodyA.node as! SKSpriteNode
let secondBody = contact.bodyB.node as! SKSpriteNode
collisionGoalOne(BallOne: secondBody, GoalOne: firstBody)
}
I think there are a couple of changes you might want to make.
When your ball node contacts the wall, I believe your didBegin(_ contact: SKPhysicsContact) method is invoked with one body as the ball (SKNode) and the other body as the scene border (SKScene).
Forcing a cast of SKScene to SKSpriteNode with let firstBody = contact.bodyA.node as! SKSpriteNode crashes, as RT5754 points out.
To fix this you might consider the following:
Add a physics category for the wall/border. Something like:
static let sceneBorder :UInt32 = 0x1 << 4
Update the border and the ball nodes to have collision physics:
ballNode.physicsBody?.collisionBitMask = physicsCategory.sceneBorder
borderNode.physicsBody?.collisionBitMask = physicsCategory.ballOne + physicsCategory.ballTwo
When handling other contact logic, my pattern for
didBegin(_ contact: SKPhysicsContact)
is like this:
func didBegin(_ contact: SKPhysicsContact) {
var firstBody: SKPhysicsBody = contact.bodyA
var secondBody: SKPhysicsBody = contact.bodyB
//in did begin contact, order the bodies by physics category
if firstBody.categoryBitMask > secondBody.categoryBitMask {
let temp = secondBody
secondBody = firstBody
firstBody = temp
}
//logic to handle contact is based on physicsCategory
if secondBody.categoryBitMask == physicsCategory.whatever {
methodToHandleWhatever(thingOne: firstBody, thingTwo: secondBody)
}

Simplifying collision and player death in SpriteKit

I want to add a bunch of different rocks and other dangerous objects that the player can collide with and die. How would I do this effectively? Now if I would copy paste these functions, it would surely work. But it seems like a huge amount of unnecessary code.
Sidenote: I'm very new to xcode, swift 2 & Sprite-kit.
func didBeginContact(contact: SKPhysicsContact) {
let firstBody : SKPhysicsBody = contact.bodyA
let secondBody : SKPhysicsBody = contact.bodyB
if ((firstBody.categoryBitMask == PhysicsCategory.Rock) && (secondBody.categoryBitMask == PhysicsCategory.Bullet) || (firstBody.categoryBitMask == PhysicsCategory.Bullet) && (secondBody.categoryBitMask == PhysicsCategory.Rock)) {
CollisionWithBullet(firstBody.node as! SKSpriteNode, Bullet: secondBody.node as! SKSpriteNode)
}
if ((firstBody.categoryBitMask == PhysicsCategory.Rock) && (secondBody.categoryBitMask == PhysicsCategory.Player) || (firstBody.categoryBitMask == PhysicsCategory.Player) && (secondBody.categoryBitMask == PhysicsCategory.Rock)) {
CollisionWithPlayer(firstBody.node as! SKSpriteNode, Player: secondBody.node as! SKSpriteNode)
}
}
func CollisionWithPlayer(Rock: SKSpriteNode, Player: SKSpriteNode){
let ScoreDefault = NSUserDefaults.standardUserDefaults()
ScoreDefault.setValue(Score, forKey: "Score")
ScoreDefault.synchronize()
let HighscoreDefault = NSUserDefaults.standardUserDefaults()
if (HighscoreDefault.valueForKey("Highscore") != nil){
Highscore = HighscoreDefault.valueForKey("Highscore") as! NSInteger
} else {
Highscore = 0
}
if (Score > Highscore){
let HighscoreDefault = NSUserDefaults.standardUserDefaults()
HighscoreDefault.setValue(Score, forKey: "Highscore")
}
self.view?.presentScene(EndScene())
ScoreLabel.removeFromSuperview()
}
So what you're likely looking for here is the bitwise OR operator and the bitwise AND operator.
With SpriteKit, physicsBodies can have multiple categories assigned to them. For instance, if we have the following categories set up:
enum CollisionCategories : UInt32 {
case Player = 1
case Enemy = 2
case Rock = 4
case Bullet = 8
}
we can set up the player category and contact bitmasks as follows:
let player = SKSpriteNode(color: UIColor.blackColor(), size: CGSize(width: 50, height: 50))
player.physicsBody = SKPhysicsBody(rectangleOfSize: player.size)
player.physicsBody?.categoryBitMask = CollisionCategories.Player.rawValue
player.physicsBody?.contactTestBitMask = CollisionCategories.Enemy.rawValue
and two enemy category and contact bitmasks as follows:
let rockEnemy = SKSpriteNode(color: UIColor.greenColor(), size: CGSize(width: 25, height: 25))
rockEnemy.physicsBody = SKPhysicsBody(rectangleOfSize: rockEnemy.size)
rockEnemy.physicsBody?.categoryBitMask = CollisionCategories.Enemy.rawValue | CollisionCategories.Rock.rawValue
rockEnemy.physicsBody?.contactTestBitMask = CollisionCategories.Player.rawValue
let bulletEnemy = SKSpriteNode(color: UIColor.redColor(), size: CGSize(width: 25, height: 25))
bulletEnemy.physicsBody = SKPhysicsBody(rectangleOfSize: bulletEnemy.size)
bulletEnemy.physicsBody?.categoryBitMask = CollisionCategories.Enemy.rawValue | CollisionCategories.Bullet.rawValue
bulletEnemy.physicsBody?.contactTestBitMask = CollisionCategories.Player.rawValue
Notice that on setting the category bitmasks, I am using the bitwise OR operator ('|'). This is a nice way of setting the bitmasks to two different categories at the same time. This way, a sprite can be both a rock and an enemy or a bullet and an enemy, etc.
See the image below for an idea of what is happening bitwise under the hood.
In our contact detection function, we can use the bitwise AND operator ('&') to see if our contact possesses a certain category.
func didBeginContact(contact: SKPhysicsContact) {
let firstBody : SKPhysicsBody = contact.bodyA
let secondBody : SKPhysicsBody = contact.bodyB
if (firstBody.categoryBitMask & CollisionCategories.Player.rawValue == CollisionCategories.Player.rawValue &&
secondBody.categoryBitMask & CollisionCategories.Enemy.rawValue == CollisionCategories.Enemy.rawValue) {
print("The collision was between the Player and an Enemy")
}
else if (firstBody.categoryBitMask & CollisionCategories.Enemy.rawValue == CollisionCategories.Enemy.rawValue &&
secondBody.categoryBitMask & CollisionCategories.Player.rawValue == CollisionCategories.Player.rawValue) {
print("The collision was between the Player and an Enemy")
}
}
This way, you can have enemies with multiple categories, but you can always do one check to see if a node is in fact an enemy, regardless of what other categories they might possess.

Three sprite node collision

I have a shooter app that is crashing when the bullet hits two overlapping nodes. I've tried everything, I tried checking if the bodies were nil but it wouldnt allow me, I'm not sure how to make this work anymore. here's the code:
func didBeginContact(contact: SKPhysicsContact) {
var firstBody:SKPhysicsBody
var secondBody:SKPhysicsBody
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask) {
firstBody = contact.bodyA
secondBody = contact.bodyB
}
else {
firstBody = contact.bodyB
secondBody = contact.bodyA
}
if ((firstBody.categoryBitMask & photonCategory) != 0 && (secondBody.categoryBitMask & alientCategory) != 0){
aliensCollideWithBullets(firstBody.node as! SKSpriteNode, alien: secondBody.node as! SKSpriteNode)
// firstBody.node?.removeFromParent()
// secondBody.node?.removeFromParent()
}
}
func aliensCollideWithBullets(torpedo:SKSpriteNode, alien:SKSpriteNode) {
print("hit")
torpedo.removeFromParent()
alien.removeFromParent()
aliensDestroyed++
trumpsDestroyedLabel.text = "\(aliensDestroyed) Trumps"
if (aliensDestroyed > 10) {
}
}
the line crashing is:
if ((firstBody.categoryBitMask & photonCategory) != 0 && (secondBody.categoryBitMask & alientCategory) != 0){
aliensCollideWithBullets(firstBody.node as! SKSpriteNode, alien: secondBody.node as! SKSpriteNode)
// firstBody.node?.removeFromParent()
// secondBody.node?.removeFromParent()
}
Any help is appreciated.
Check bodyA and bodyB for nil. I had the same issue and solved it with this line of code:
if contact.bodyA.node != nil && contact.bodyB.node != nil

When projectile hits two "monsters" the didBeginContact method crashes. I know why but i don't know how to avoid it

So I have this code from the tutorial:
func didBeginContact(contact: SKPhysicsContact) {
// 1
var firstBody: SKPhysicsBody?
var secondBody: SKPhysicsBody?
var body: SKPhysicsBody
//contact.bodyB.node!.physicsBody!.allContactedBodies().count
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
firstBody = contact.bodyA
secondBody = contact.bodyB
println("1 = A, 2 = B")
} else {
firstBody = contact.bodyB
secondBody = contact.bodyA
println("2 = A, 1 = B")
}
// 2
if ((firstBody!.categoryBitMask & PhysicsCategory.Monster != 0) &&
(secondBody!.categoryBitMask & PhysicsCategory.Projectile != 0)) {
for var c = 1; c <= contact.bodyB.node!.physicsBody!.allContactedBodies().count; c++ {
projectileDidCollideWithMonster(firstBody!.node as! SKSpriteNode, monster: secondBody!.node as! SKSpriteNode)
}
secondBody!.node?.removeFromParent()
}
}
func projectileDidCollideWithMonster(projectile:SKSpriteNode, monster:SKSpriteNode) {
println("Hit")
changeScore(1)
changeAmo(true)
projectile.removeFromParent()
monster.removeFromParent()
}
Then what is happening is that a projectile sometimes hit TWO monsters at once.
When this happens - didBeginContact method crashes saying that secondBody is nil.
After a thorough research I found out the reason:
when projectile collides with two other nodes at once - this method runs two times. After the first run - if gets bodyA as projectile, bodyB as a monster - passes them on to projectileDidCollideWithMonster and it removes them both. then it runs immediately again but at that moment projectile doesn't exist anymore and it crashes not able to assign it's node.
I have no idea how to overcome this:( any suggestions, please?
SOLUTION: Thanks to ideas below i did some changes.
added and array and a trigger at the top of the class:
var bodiesToBeRemoved = [SKSpriteNode]()
var shouldRemoveBodies = false
and did these modifications:
func projectileDidCollideWithMonster(projectile:SKSpriteNode, monster:SKSpriteNode) {
/* Called when collisions is detected and collided nodes are passed to it */
//score and stuff
println("Hit")
changeScore(1)
changeAmo(true)
//collect the nodes to be removed
bodiesToBeRemoved.append(projectile)
bodiesToBeRemoved.append(monster)
//change the trigger to remove collected nodes later on
shouldRemoveBodies=true
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
//check the trigger
if shouldRemoveBodies == true {
println("\(bodiesToBeRemoved.count)")
//remove collected nodes
for var i = 0; i<bodiesToBeRemoved.count; i++ {
bodiesToBeRemoved[i].removeFromParent()
}
//reset array
bodiesToBeRemoved.removeAll(keepCapacity: false)
//reset the trigger
shouldRemoveBodies = false
} else {
//do nothing:)
}
}
Instead of removing your projectile immediately, just mark it for removal (e.g. by setting a boolean flag, or adding it to some collection if it's not already in the collection). Then later, before the next physics check (e.g. at the end of this frame), go through and remove all projectiles marked for removal.
Just check it's nil or not better than check every nodes's mark.
func removeNodeFromPhysicsBody(ps: SKPhysicsBody){
if (ps.node != nil){
ps.node?.removeFromParent()
}
}
func wallDidCollideWithMeteor(wall:SKPhysicsBody, meteor:SKPhysicsBody) {
removeNodeFromPhysicsBody(wall)
removeNodeFromPhysicsBody(meteor)
}

Sprite Kit Collision error - Swift

I am new to IOS dev and am currently having some issues with sprite kit collisions in the didBeginContact method.
How do I break out of, or stop didBeginContact from running if one of the colliding physics bodies are removed.
eg:
1 bullet collides with 2 overlapping enemies. Because the bullet hits enemy one and is destroyed, the collision check running on the second enemy throws an exception because the bullet no longer exists.
I have tried checking for nil and NSNULL values with no luck.
The error code that I receive is "Thread 1: EXC_BAD_INSTRUCTION(code=EXC_I386_INVOIP,subcode=0x0)" and occurs when trying to check the category bit mask (Because the torpedo no longer exists).
CODE:
var bodyA:SKPhysicsBody = contact.bodyA
var bodyB:SKPhysicsBody = contact.bodyB
if(contact.bodyA.categoryBitMask == alienCategory && contact.bodyB.categoryBitMask == alienCategory){
return
}
if((bodyA.categoryBitMask == alienCategory) && (bodyB.categoryBitMask == photonTorpedoCategory)){
torpedoDidCollideWithAlien(bodyA.node as SKSpriteNode)
}
var currTorpedo:SKSpriteNode = contact.bodyB.node as SKSpriteNode
var currAlien:SKSpriteNode = contact.bodyA.node as SKSpriteNode
currTorpedo.removeFromParent()
currAlien.removeFromParent()
}
func torpedoDidCollideWithAlien(alien:SKSpriteNode){
aliensDestroyed++
label.text = ("Current Score: " + String(aliensDestroyed))
}
Perhaps you could delay removing the torpedo from the scene until the next scene 'update'. You could flag the torpedo as disabled/inactive so it doesn't impact your 2nd overlapping enemy. Something like this:
class YourScene: SkScene {
// Array of disabled torpedos
var disabledTorpedos = SKSpriteNode()[]
func didBeginContact(contact: SKPhysicsContact) {
var bodyA:SKPhysicsBody = contact.bodyA
var bodyB:SKPhysicsBody = contact.bodyB
if(contact.bodyA.categoryBitMask == alienCategory && contact.bodyB.categoryBitMask == alienCategory){
return
}
if((bodyA.categoryBitMask == alienCategory) && (bodyB.categoryBitMask == photonTorpedoCategory)){
torpedoDidCollideWithAlien(bodyA.node as SKSpriteNode)
}
var currTorpedo:SKSpriteNode = contact.bodyB.node as SKSpriteNode
var currAlien:SKSpriteNode = contact.bodyA.node as SKSpriteNode
// Created a new photonTorpedoDisabledCategory so the torpedo cannot hit a 2nd alien
currTorpedo.physicsBody.categoryBitMask = photonTorpedoDisabledCategory
disabledTorpedoes.append(currTorpedo)
// Hide torpedo from scene
currTorpedo.hidden = true
currAlien.removeFromParent()
}
override func update(currentTime: NSTimeInterval) {
// loop through disabledTorpedos array and removeFromParent
}
}

Resources