So currently I am trying to make an app where when the player collides with the enemy, the enemy disappears. I have achieved this by writing this code;
func didBegin(_ contact: SKPhysicsContact) {
var firstBody = SKPhysicsBody()
var secondBody = SKPhysicsBody()
if contact.bodyA.node?.name == "Player" {
firstBody = contact.bodyA
secondBody = contact.bodyB
}else {
firstBody = contact.bodyB
secondBody = contact.bodyA
}
if firstBody.node?.name == "Player" && secondBody.node?.name == "Enemy" {
}
if contact.bodyA.categoryBitMask == 1 && contact.bodyB.categoryBitMask == 2 {
self.enumerateChildNodes(withName: "Enemy") { (node:SKNode, nil) in
if node.position.y < 550 || node.position.y > self.size.height + 550 {
node.removeFromParent()
}
}
}
}
However, because I'm enumeratingChildNodes with the name "Enemy", every enemy disappears on screen. I only want the one I hit to disappear. Any help? Thanks!
You'll want to replace this:
self.enumerateChildNodes(withName: "Enemy") { (node:SKNode, nil) in
if node.position.y < 550 || node.position.y >
self.size.height + 550 {
node.removeFromParent()
}
}
}
With something like this:
if contact.bodyA.node?.name == "Enemy" {
contact.bodyA.node?.removeFromParent()
} else if contact.bodyB.node?.name == "Enemy" {
contact.bodyB.node?.removeFromParent()
}
Contact bodyA and bodyB are the 2 nodes which have made contact with each other. The IF statement just checks to see which one is the enemy, then removes it.
JohnL has posted the correct answer, but you might find it helpful to structure your didBegin like this:
func didBegin(_ contact: SKPhysicsContact) {
print("didBeginContact entered for \(String(describing: contact.bodyA.node!.name)) and \(String(describing: contact.bodyB.node!.name))")
let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
switch contactMask {
case playerCategory | enemyCategory:
print("Player and enemy have contacted.")
let enemyNode = contact.bodyA.categoryBitMask == enemyCategory ? contact.bodyA.node : contact.bodyB.node
enemyNode.removeFromParent
default:
print("Some other contact occurred")
}
}
(The print statements are for debugging and can be removed)
This code doesn't bother assigning the bodies in the collision until required and logically ANDs the 2 category bit masks in order to ascertain what has hit what, and then using the 'switch' to process each collision. You could add extra switch cases for other collisions. We then use the ternary operator to get the 'enemy' node (Functionally the same as JohnL's 'if...then...) and remove it.
Related
This is my code:
func didBegin(_ 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 & CollisionTypes.enemy.rawValue != 0) && (secondBody.categoryBitMask & CollisionTypes.missile.rawValue != 0)) {
if let enemy = firstBody.node as? SKSpriteNode,
let missile = secondBody.node as? SKSpriteNode {
missileCollidedWithEnemy(missileNode: missile, enemyNode: enemy)
let gameOverScene = GamerOverScene(size: self.size)
self.view?.presentScene(gameOverScene)
}
}
if ((firstBody.categoryBitMask & CollisionTypes.enemy.rawValue != 0) && (secondBody.categoryBitMask & CollisionTypes.player.rawValue != 0)) {
if let enemy = firstBody.node as? SKSpriteNode,
let player = secondBody.node as? SKSpriteNode {
playerCollidedWithEnemy(enemyNode: enemy, playerNode: player)
}
}
if (contact.bodyA.categoryBitMask == playerCategory) {
contact.bodyA.node?.physicsBody?.collisionBitMask = 0
contact.bodyA.node?.physicsBody?.categoryBitMask = 0
} else if (contact.bodyB.categoryBitMask == playerCategory) {
contact.bodyB.node?.physicsBody?.collisionBitMask = 0
contact.bodyB.node?.physicsBody?.categoryBitMask = 0
}
if contact.bodyA.categoryBitMask == enemyCategory {
contact.bodyB.node?.removeFromParent()
contact.bodyB.node?.removeAllActions()
} else if contact.bodyB.categoryBitMask == enemyCategory {
contact.bodyA.node?.removeFromParent()
contact.bodyA.node?.removeAllActions()
}
So my game over does work but only when enemy and missile collide. When I take these lines of code:
let gameOverScene = GamerOverScene(size: self.size)
self.view?.presentScene(gameOverScene)
and place it into the second if statement so game over can show when player and enemy collide, it doesn't work. What's even more confusing is that when I delete the whole first if statement and place the line of code into the second one, the same thing happens, the enemy collides with the missile, and game over pops up.
You are getting expected behavior. You placed the code to show the Game Over scene when the missile and enemy collide.
Move the code to show the Game Over scene to the if statement where the player and enemy collide. You have to do that to get the scene to show when the player and enemy collide.
If the Game Over scene still only appears when the missile and enemy collide, you most likely have a problem with your category bit masks or the raw values of your collision types.
Here is my code. I am trying to create a space shooter game and when the alien hits the bottom of the screen, I want the player to disappear. I'm not exactly sure what is wrong with this code.
func gameOver() {
let borderBody = SKPhysicsBody(edgeLoopFrom: self.frame)
borderBody.categoryBitMask = borderCategory
func didBegin(_ 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 & alienCategory) != 0 && (secondBody.categoryBitMask & borderCategory) != 0 {
player.removeFromParent()
}
I'm working on a little game where I have to match some colours. I'm working only with SKShapeNodes; but I cannot seem to get this code to work. I'm only getting the "Different color" print.
func didBegin(_ contact: SKPhysicsContact) {
let firstBody = contact.bodyA
let secondBody = contact.bodyB
if firstBody.categoryBitMask == PhysicsCategory.colorNode && secondBody.categoryBitMask == PhysicsCategory.colorBox || firstBody.categoryBitMask == PhysicsCategory.colorBox && secondBody.categoryBitMask == PhysicsCategory.colorNode {
let firstNode = contact.bodyA.node as? SKShapeNode
let secondNode = contact.bodyB.node as? SKShapeNode
if firstNode?.fillColor == secondNode?.fillColor {
/* TODO: Update score label */
print("Same Color")
point = +1
}
else {
print("Differnet color!")
/* TODO: Game over */
}
}
}
Any help appreciated :-)
I found the solution. One of the SKShapeNodes was made into a child of a SKNode containing the PhysicsBody.
Code works perfectly now.
This is my first attempt with SpriteKit and I'm having trouble getting my collision right with bitmasks.
I have three categories, If player hits lit, I want to increase the score and move the lit node off screen, else, I want to call my gameover() function. I've tried a lot of variations and can't see to get anything but general collision to be recognized. I've defined the category and contact bitmasks for each node as well.
let playerCategory: UInt32 = 1
let razzCategory: UInt32 = 2
let litCategory: UInt32 = 4
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 & playerCategory) == 0 && (secondBody.categoryBitMask & litCategory) == 1)
{
lit.position.x = 400
score += 1
}
else {
gameOver()
}
}
if ((firstBody.categoryBitMask & playerCategory) == 0 && (secondBody.categoryBitMask & litCategory) == 1) Translates to the following in english.
If Firstbody AND playerCategory = 0 AND SecondBody And litCategory = 1
If Firstbody AND 1 = 0 AND SecondBody And 4 = 1
Now let's define Firstbody as playerCategory and SecondBody as litCategory
If playerCategory AND playerCategory = 0 AND litCategory and litCategory = 1
If 1 AND 1 = 0 AND 4 AND 4 = 1
If 1 = 0 AND 4 = 1
As you can see, this fails, and this method is always going to fail because if the second half of your test (SecondBody AND litCategory) can only have a value of 0 or 4, those 2 values will never be 1.
To correct the issue, you want to make sure that whatever body you are checking is equal to the category you are looking for
if ((firstBody.categoryBitMask & playerCategory) == playerCategory && (secondBody.categoryBitMask & litCategory) == litCategory)
What this says is if the firstBody is a member of the category playerCategory and the secondBody is a member of litCategory, then perform the following operatiion.
Below is the complete fix for your function:
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 & playerCategory) == playerCategory && (secondBody.categoryBitMask & litCategory) == litCategory)
{
lit.position.x = 400
score += 1
}
else {
gameOver()
}
}
I have a dynamic yellow SpriteNode in motion with a categoryBitMask and a contactTestBitMask and it's intended to collide with the static red rectangle SpriteNodes with their own categoryBitMasks and contactTestBitMasks.
I would like to call a function only when both the rectangles have been hit by the yellow sprite? Does anyone know how to do that? I currently have the following code below that I use to check for the collusion with one rectangle.
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 == kYellowCatergory && secondBody.categoryBitMask == kRectangleTarget {
//self.functionToCall()
}
}