Removing an SKSpriteNode from parent is not working - swift

In SpriteKit in xcode, I'm building a game where a player controls a ball(sprite node) and when the ball touches a "ColorOrbChanger" (sprite node) the player takes on that colour and the orb gets removed, but I'm having trouble figuring out why removeFromParent isn't working for this. thanks in advance :)
class GameScene: SKScene, SKPhysicsContactDelegate {
var RedOrb = SKSpriteNode()
var OrangeOrb = SKSpriteNode()
var YellowOrb = SKSpriteNode()
var GreenOrb = SKSpriteNode()
var BlueOrb = SKSpriteNode()
var PurpleOrb = SKSpriteNode()
func OrbColorPicker() -> SKSpriteNode {
let RedOrb = SKSpriteNode(imageNamed: "ColourNodeRed")
RedOrb.color = SKColor.redColor()
RedOrb.size = CGSize(width: 40, height: 60)
RedOrb.position = CGPoint(x: self.frame.width - 140, y: self.frame.height / 2)
RedOrb.physicsBody = SKPhysicsBody(rectangleOfSize: RedOrb.size)
RedOrb.physicsBody?.categoryBitMask = PhysicsCatagory.RedOrb
RedOrb.physicsBody?.contactTestBitMask = PhysicsCatagory.Player
RedOrb.physicsBody?.affectedByGravity = false
RedOrb.physicsBody?.dynamic = false
RedOrb.runAction(moveAndRemove)
RedOrb.zPosition = 2
/* ^ plus all the other colours Orange, Yellow, Green, Blue, Purple, then i added all those into an array that spat out a random colour orb) */
var ColorOrbArray = [RedOrb, OrangeOrb, YellowOrb, GreenOrb, BlueOrb, PurpleOrb]
let countArray = UInt32(ColorOrbArray.count)
let pickOneNode = arc4random_uniform(countArray)
let randomElement = Int(pickOneNode)
if randomElement == 0{
self.addChild(RedOrb)
}
if randomElement == 1{
self.addChild(OrangeOrb)
}
if randomElement == 2{
self.addChild(YellowOrb)
}
if randomElement == 3{
self.addChild(GreenOrb)
}
if randomElement == 4{
self.addChild(BlueOrb)
}
if randomElement == 5{
self.addChild(PurpleOrb)
}
return ColorOrbArray[randomElement]
func didBeginContact(contact: SKPhysicsContact) {
if (contact.bodyA.categoryBitMask == PhysicsCatagory.Player) && (contact.bodyB.categoryBitMask == PhysicsCatagory.RedOrb) || (contact.bodyA.categoryBitMask == PhysicsCatagory.RedOrb) && (contact.bodyB.categoryBitMask == PhysicsCatagory.Player){
let changeColorActionRed = SKAction.colorizeWithColor(SKColor.redColor(), colorBlendFactor: 1.0, duration: 0)
Player.runAction(changeColorActionRed)
RedOrb.removeFromParent()
}
}

Related

Swift programming. Possible problem with SKPhysicscontact

I'm working on a project / game and I'm stuck at adding new enemies to the game and to program them. This is how my code looked before I started changing and only had one type of "alien". I want to create two new types, one that you lose 5 points from hitting and one 10. In the code now, there is only one of three and it gives +5 points. How would you guys add the two new? I think I'm overthinking it and that's why I messed up.
import SpriteKit
import GameplayKit
import CoreMotion
class GameScene: SKScene, SKPhysicsContactDelegate {
var starfield:SKEmitterNode!
var player:SKSpriteNode!
var scoreLabel:SKLabelNode!
var score:Int = 0 {
didSet {
scoreLabel.text = "Score: \(score)"
}
}
var gameTimer:Timer!
var possibleAliens = ["alien"]
//This is the "alien you earn +5 point from shooting. Im trying to get 2 new aliens to the scene, one that you lose -5 points from shooting and one you lose -10 on.
//I have tried to rewrite the codes to the two new, but they still earn +5 points from shooting all 3 types. What would you guys do and how?
let alienCategory:UInt32 = 0x1 << 1
let photonTorpedoCategory:UInt32 = 0x1 << 0
let motionManger = CMMotionManager()
var xAcceleration:CGFloat = 0
override func didMove(to view: SKView) {
starfield = SKEmitterNode(fileNamed: "Starfield")
starfield.position = CGPoint(x: 0, y: 1472)
starfield.advanceSimulationTime(10)
self.addChild(starfield)
starfield.zPosition = -1
player = SKSpriteNode(imageNamed: "shuttle")
player.position = CGPoint(x: self.frame.size.width / 2, y: player.size.height / 2 + 20)
self.addChild(player)
self.physicsWorld.gravity = CGVector(dx: 0, dy: 0)
self.physicsWorld.contactDelegate = self
scoreLabel = SKLabelNode(text: "Score: 0")
scoreLabel.position = CGPoint(x: 100, y: self.frame.size.height - 60)
scoreLabel.fontName = "AmericanTypewriter-Bold"
scoreLabel.fontSize = 36
scoreLabel.fontColor = UIColor.white
score = 0
self.addChild(scoreLabel)
gameTimer = Timer.scheduledTimer(timeInterval: 0.75, target: self, selector: #selector(addAlien), userInfo: nil, repeats: true)
motionManger.accelerometerUpdateInterval = 0.2
motionManger.startAccelerometerUpdates(to: OperationQueue.current!) { (data:CMAccelerometerData?, error:Error?) in
if let accelerometerData = data {
let acceleration = accelerometerData.acceleration
self.xAcceleration = CGFloat(acceleration.x) * 0.75 + self.xAcceleration * 0.25
}
}
}
func addAlien () {
possibleAliens = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: possibleAliens) as! [String]
let alien = SKSpriteNode(imageNamed: possibleAliens[0])
let randomAlienPosition = GKRandomDistribution(lowestValue: 0, highestValue: 414)
let position = CGFloat(randomAlienPosition.nextInt())
alien.position = CGPoint(x: position, y: self.frame.size.height + alien.size.height)
alien.physicsBody = SKPhysicsBody(rectangleOf: alien.size)
alien.physicsBody?.isDynamic = true
alien.physicsBody?.categoryBitMask = alienCategory
alien.physicsBody?.contactTestBitMask = photonTorpedoCategory
alien.physicsBody?.collisionBitMask = 0
self.addChild(alien)
let animationDuration:TimeInterval = 6
var actionArray = [SKAction]()
actionArray.append(SKAction.move(to: CGPoint(x: position, y: -alien.size.height), duration: animationDuration))
actionArray.append(SKAction.removeFromParent())
alien.run(SKAction.sequence(actionArray))
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
fireTorpedo()
}
func fireTorpedo() {
self.run(SKAction.playSoundFileNamed("torpedo.mp3", waitForCompletion: false))
let torpedoNode = SKSpriteNode(imageNamed: "torpedo")
torpedoNode.position = player.position
torpedoNode.position.y += 5
torpedoNode.physicsBody = SKPhysicsBody(circleOfRadius: torpedoNode.size.width / 2)
torpedoNode.physicsBody?.isDynamic = true
torpedoNode.physicsBody?.categoryBitMask = photonTorpedoCategory
torpedoNode.physicsBody?.contactTestBitMask = alienCategory
torpedoNode.physicsBody?.collisionBitMask = 0
torpedoNode.physicsBody?.usesPreciseCollisionDetection = true
self.addChild(torpedoNode)
let animationDuration:TimeInterval = 0.3
var actionArray = [SKAction]()
actionArray.append(SKAction.move(to: CGPoint(x: player.position.x, y: self.frame.size.height + 10), duration: animationDuration))
actionArray.append(SKAction.removeFromParent())
torpedoNode.run(SKAction.sequence(actionArray))
}
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 & photonTorpedoCategory) != 0 && (secondBody.categoryBitMask & alienCategory) != 0 {
torpedoDidCollideWithAlien(torpedoNode: firstBody.node as! SKSpriteNode, alienNode: secondBody.node as! SKSpriteNode)
}
}
func torpedoDidCollideWithAlien (torpedoNode:SKSpriteNode, alienNode:SKSpriteNode) {
let explosion = SKEmitterNode(fileNamed: "Explosion")!
explosion.position = alienNode.position
self.addChild(explosion)
self.run(SKAction.playSoundFileNamed("explosion.mp3", waitForCompletion: false))
torpedoNode.removeFromParent()
alienNode.removeFromParent()
self.run(SKAction.wait(forDuration: 2)) {
explosion.removeFromParent()
}
score += 5
}
override func didSimulatePhysics() {
player.position.x += xAcceleration * 50
if player.position.x < -20 {
player.position = CGPoint(x: self.size.width + 20, y: player.position.y)
}else if player.position.x > self.size.width + 20 {
player.position = CGPoint(x: -20, y: player.position.y)
}
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}
I wouldn't recommend following this answer if your game becomes more complex. Normally I would say put the aliens in subclasses initialized with their own categorybitmasks and other properties, but to avoid modifying your code too much I just changed the addAlien() method and didBeginContact method. Each alien has its own categoryBitMask and textureName.
let alienCategories:[UInt32] = [(0x1 << 1),(0x1 << 2),(0x1 << 3)]
let alienTextureNames:[String] = ["alien1","alien2","alien3"]
This will make a random Alien using the values in the collections above
func addAlien () {
//Random index between 0 and 2 changing the texture and category bitmask
let index = Int.random(in: 0...2)
let textureName = alienTextureNames[index]
let alien = SKSpriteNode(imageNamed: textureName)
//Use Int.random() if you don't want a CGFLoat
let xPos = CGFloat.random(in: 0...414)
let yPos = frame.size.height + alien.size.height
alien.position = CGPoint(x: xPos, y: yPos)
alien.physicsBody = SKPhysicsBody(rectangleOf: alien.size)
alien.physicsBody?.isDynamic = true
alien.physicsBody?.categoryBitMask = alienCategories[index]
alien.physicsBody?.contactTestBitMask = photonTorpedoCategory
alien.physicsBody?.collisionBitMask = 0
self.addChild(alien)
let moveAction = SKAction.moveTo(y: -alien.size.height, duration: 6)
alien.run(moveAction, completion: { alien.removeFromParent() })
}
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 == photonTorpedoCategory && secondBody.categoryBitMask == alienCategories[0] {
torpedoDidCollideWithAlien(torpedoNode: firstBody.node as! SKSpriteNode, alienNode: secondBody.node as! SKSpriteNode)
score += 5
}else if firstBody.categoryBitMask == photonTorpedoCategory && secondBody.categoryBitMask == alienCategories[1] {
torpedoDidCollideWithAlien(torpedoNode: firstBody.node as! SKSpriteNode, alienNode: secondBody.node as! SKSpriteNode)
score -= 5
}else if firstBody.categoryBitMask == photonTorpedoCategory && secondBody.categoryBitMask == alienCategories[2] {
torpedoDidCollideWithAlien(torpedoNode: firstBody.node as! SKSpriteNode, alienNode: secondBody.node as! SKSpriteNode)
score -= 10
}
}
Change the torpedoNode.physicsBody?.contactTestBitMask = alienCategory line in the fireTorpedo method to alien.physicsBody?.contactTestBitMask = (alienCategories[0] | alienCategories[1] | alienCategories[2])

No collision detection between SpriteNodes

I have 2 categories
enum ColliderType:UInt32 {
case bulletCategory = 1
case bossCategory = 2
}
I made my class use the SKPhysicsContactDelegate and created a node
class PlayScene: SKScene, SKPhysicsContactDelegate {
var charBullets :[SKSpriteNode] = [SKSpriteNode]()
var charBulletTexture = SKTexture(imageNamed: "Bullet1.png")
var boss = SKSpriteNode()
var bossTexture = SKTexture(imageNamed: "BossImage.png")
And set the physicsWorld.contactDelegate and created a node in the didMove(to view: SKView) function
override func didMove(to view: SKView) {
physicsWorld.contactDelegate = self
boss.name = "boss"
boss = SKSpriteNode(texture: bossTexture)
boss.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 300, height: 300))
boss.physicsBody?.usesPreciseCollisionDetection = true
boss.physicsBody!.isDynamic = true
boss.physicsBody!.affectedByGravity = false
boss.size = CGSize(width: 300, height: 300)
boss.physicsBody!.categoryBitMask = ColliderType.bossCategory.rawValue
boss.physicsBody!.collisionBitMask = 0
boss.physicsBody!.contactTestBitMask = ColliderType.bulletCategory.rawValue
boss.position = CGPoint(x: 0, y: self.size.height/4)
addChild(boss)
}
I created another node in the touchesBegan function
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
charBullet.name = "bullet"
charBullet.physicsBody = SKPhysicsBody(circleOfRadius: charBullet.size.width/64)
charBullet.physicsBody!.isDynamic = true
charBullet.physicsBody!.usesPreciseCollisionDetection = true
charBullet.physicsBody!.affectedByGravity = false
charBullet.physicsBody!.velocity = CGVector.init(dx: 0, dy: 450)
charBullet.physicsBody!.categoryBitMask = ColliderType.bulletCategory.rawValue
charBullet.physicsBody!.collisionBitMask = 0
charBullet.physicsBody!.contactTestBitMask = ColliderType.bossCategory.rawValue
charBullet.position = CGPoint(x: character.position.x, y: character.position.y + 100)
addChild(charBullet)
charBullets.append(charBullet)
}
Then I have a didBegin(_ contact: SKPhysicsContact) but it isn't called when the bullet and boss collide
func didBegin(_ contact: SKPhysicsContact) {
//not printed
print("contact!")
if(charBullets.count > 0) {
var firstBody = SKPhysicsBody()
var secondBody = SKPhysicsBody()
if(contact.bodyA.node?.name == "bullet") {
print("test1")
firstBody = contact.bodyA
secondBody = contact.bodyB
} else {
print("test2")
firstBody = contact.bodyB
secondBody = contact.bodyA
print(secondBody)
}
if(firstBody.node?.name == "bullet") {
print("bulletname")
}
if(firstBody.node?.name == "boss") {
print("bossname")
}
if(secondBody.node?.name == "bullet") {
print("bulletname2")
}
if(secondBody.node?.name == "boss") {
print("bossname2")
}
if(firstBody.node?.name == "bullet" && secondBody.node?.name == "boss") {
print("hit the boss!")
charBullets[i].removeFromParent()
bossHealth -= 1
bossHealthLabel.text = "Boss Health \(bossHealth)"
}
}
}
}
Update: I fixed it by changing their physicsBodys' to use the image texture size as opposed to a set value and by setting their names under the creation of the node by using
boss = SKSpriteNode(texture: bossTexture)
boss.name = "boss"
boss.physicsBody = SKPhysicsBody(texture: bossTexture, size: bossTexture.size())
and
let charBullet = SKSpriteNode(texture: charBulletTexture)
charBullet.name = "bullet"
charBullet.physicsBody = SKPhysicsBody(texture: charBulletTexture, size: charBulletTexture.size())

Node not being removed from parent (spritekit)

Create enemy
touchesBegan and didBegin contact function
My enemy node is not being removed from the scene every time my sword node touches it. I'm just wondering if anyone could explain to me what I'm doing wrong?
(UPDATE BELOW)
import SpriteKit
import GameplayKit
import AVFoundation
class LevelTwo: SKScene, SKPhysicsContactDelegate{
var levelBg = SKSpriteNode(imageNamed: "level2")
var hero = SKSpriteNode()
var enemy = SKSpriteNode()
var sword = SKSpriteNode()
var health1 = SKSpriteNode(imageNamed: "playerhplv2")
var health2 = SKSpriteNode(imageNamed: "playerhplv2")
var health3 = SKSpriteNode(imageNamed: "playerhplv2")
var musicPath = URL(fileURLWithPath: Bundle.main.path(forResource: "gameMusic", ofType: "mp3")!)
var musicGamePlayer = AVAudioPlayer()
var runMonster = SKAction()
var waitMonster = SKAction()
var sequenceMonster = SKAction()
var repeatMonster = SKAction()
enum CollisionNum: UInt32{
case swordNum = 1
case enemyNum = 2
case playerNum = 4
}
override func didMove(to view: SKView) {
self.physicsWorld.contactDelegate = self
///music
do{
musicGamePlayer = try AVAudioPlayer(contentsOf: musicPath)
musicGamePlayer.prepareToPlay()
musicGamePlayer.numberOfLoops = -1
musicGamePlayer.play()
}
catch{
print(error)
}
//bg
levelBg.position = CGPoint(x: 0, y: 0)
levelBg.zPosition = 1
levelBg.size = levelBg.texture!.size()
levelBg.setScale(1.25)
self.addChild(levelBg)
//hero
let playerTexture = SKTexture(imageNamed: "main")
hero = SKSpriteNode(texture: playerTexture)
hero.position = CGPoint(x: 0, y: 0)
hero.zPosition = 2
hero.setScale(0.6)
hero.physicsBody = SKPhysicsBody(texture: playerTexture, size: CGSize(width: hero.size.width, height: hero.size.height))
hero.physicsBody!.categoryBitMask = CollisionNum.playerNum.rawValue
hero.physicsBody!.collisionBitMask = CollisionNum.enemyNum.rawValue //player is allowed to bump into rocks and skulls
hero.physicsBody!.contactTestBitMask = CollisionNum.enemyNum.rawValue // same as collisions
hero.physicsBody!.isDynamic = false
self.addChild(hero)
//health1
health1.position = CGPoint(x: 130, y: 150)
health1.zPosition = 3
health1.setScale(0.75)
self.addChild(health1)
//health2
health2.position = CGPoint(x: 230, y: 150)
health2.zPosition = 3
health2.setScale(0.75)
self.addChild(health2)
//health3
health3.position = CGPoint(x: 320, y: 150)
health3.zPosition = 3
health3.setScale(0.75)
self.addChild(health3)
runMonster = SKAction.run(addMonster)
waitMonster = SKAction.wait(forDuration: 0.3)
sequenceMonster = SKAction.sequence([runMonster,waitMonster])
repeatMonster = SKAction.repeatForever(sequenceMonster)
run(repeatMonster)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches{
let locale = touch.location(in: self)
hero.position.x = locale.x
hero.position.y = locale.y
}
}
func addMonster(){
//random position based off the bg size
let monsterHigherX = Int(levelBg.size.width)
let monsterHigherY = Int(levelBg.size.height)
let monsterLowerX = monsterHigherX * -1
let monsterLowerY = monsterHigherY * -1
let randomLocaleX = Int(arc4random_uniform(UInt32(monsterHigherX - monsterLowerX))) + monsterLowerX
let randomLocaleY = Int(arc4random_uniform(UInt32(monsterHigherY - monsterLowerY))) + monsterLowerY
let movementEnemy = SKAction.moveBy(x: -5, y: -5, duration: 0.2)
let movementForever = SKAction.repeatForever(movementEnemy)
let enemyTexture = SKTexture(imageNamed: "boss0")
enemy = SKSpriteNode(texture: enemyTexture)
enemy.zPosition = 2
enemy.setScale(0.5)
enemy.position = CGPoint(x: randomLocaleX, y: randomLocaleY)
enemy.physicsBody = SKPhysicsBody(texture: enemyTexture, size: CGSize(width: enemy.size.width, height: enemy.size.height))
enemy.physicsBody!.isDynamic = true
enemy.physicsBody!.affectedByGravity = false
enemy.physicsBody!.categoryBitMask = CollisionNum.enemyNum.rawValue
enemy.physicsBody!.collisionBitMask = CollisionNum.swordNum.rawValue
enemy.physicsBody!.contactTestBitMask = CollisionNum.swordNum.rawValue
enemy.run(movementForever)
self.addChild(enemy)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let swordTexture = SKTexture(imageNamed: "blade-0")
sword = SKSpriteNode(texture: swordTexture)
sword.setScale(0.50)
sword.zPosition = 2
sword.position = hero.position
sword.physicsBody = SKPhysicsBody(texture: swordTexture, size: CGSize(width: sword.size.width, height: sword.size.height))
sword.physicsBody!.velocity = CGVector(dx: 1200, dy:0)
sword.physicsBody!.isDynamic = true
sword.physicsBody!.affectedByGravity = true
sword.physicsBody!.usesPreciseCollisionDetection = true
sword.physicsBody!.categoryBitMask = CollisionNum.swordNum.rawValue
sword.physicsBody!.collisionBitMask = CollisionNum.enemyNum.rawValue
sword.physicsBody!.contactTestBitMask = CollisionNum.enemyNum.rawValue
self.addChild(sword)
}
func didBegin(_ contact: SKPhysicsContact) {
let collision: UInt32 = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
if collision == CollisionNum.swordNum.rawValue | CollisionNum.enemyNum.rawValue {
enemy.removeFromParent()
}
}
override func update(_ currentTime: TimeInterval) {
}
}
(Ive attached my whole level2 class) Thank you so much for the suggestion; however, when I tried implementing this I still run into the same problem (im running this on the iphone simulator) Im wondering whether the error is with my enum or my implementation of my physics with my nodes
You do not want to remove "enemy" because "enemy" is always the last monster you added. You need to check which contactBody is the enemy so you can remove it. You can do that by guaranteeing which node you want to associate as A, and which you want to associate as B by looking at the categoryBitMask value:
func didBegin(_ contact: SKPhysicsContact) {
//This guarantees the lower categoryBitMask (Providing you are only using one) is in A
let bodyA = contact.bodyA.categoryBitMask <= contact.bodyB.categoryBitMask ? contact.bodyA : contact.bodyB
let bodyB = contact.bodyA.categoryBitMask <= contact.bodyB.categoryBitMask ? contact.bodyB : contact.bodyA
if bodyA.categoryBitMask == CollisionNum.swordNum.rawValue && bodyB.categoryBitMask == CollisionNum.enemyNum.rawValue {
bodyB.node.removeFromParent()
}
}
Of course this will lead to problems with multiple collisions, so instead you may want to do:
var removeNodes = SKNode()
func didBegin(_ contact: SKPhysicsContact) {
//This guarantees the lower categoryBitMask (Providing you are only using one) is in A
let bodyA = contact.bodyA.categoryBitMask <= contact.bodyB.categoryBitMask ? contact.bodyA : contact.bodyB
let bodyB = contact.bodyA.categoryBitMask > contact.bodyB.categoryBitMask ? contact.bodyB : contact.bodyA
if bodyA.categoryBitMask == CollisionNum.swordNum.rawValue && bodyB.categoryBitMask == CollisionNum.enemyNum.rawValue {
bodyB.node.moveToParent(removeNodes)
}
}
func didFinishUpdate(){
removeNodes.removeAllChildren()
}
Try this:
func didBegin(_ contact: SKPhysicsContact) {
let collision: UInt32 = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
if collision == CollisionNum.swordNum.rawValue | CollisionNum.enemyNum.rawValue {
enemy.removeFromParent()
}
}
You were only testing if bodyA is equal to the enemy, however, bodyA may be equal to the sword instead.

Not detecting collision

I'm getting some difficulty detecting collision in my SpriteKit Game. I simply want to detect collision between the missiles and the enemies and boats.
I have the ColliderType:
struct ColliderType {
static let Boat: UInt32 = 1
static let Enemy: UInt32 = 2
static let Wall: UInt32 = 3
static let Bullet: UInt32 = 4
}
class GameplayScene: SKScene, SKPhysicsContactDelegate {
var player = SKSpriteNode()
var enemy = SKSpriteNode()
var boat = SKSpriteNode()
var missile = SKSpriteNode()
The didBegin contact:
func didBegin(_ contact: SKPhysicsContact) {
var firstBody = SKPhysicsBody()
var secondBody = SKPhysicsBody()
if contact.bodyA.node?.name == "Missile" {
firstBody = contact.bodyA
secondBody = contact.bodyB
} else {
firstBody = contact.bodyB
secondBody = contact.bodyA
}
if firstBody.node?.name == "Missile" && secondBody.node?.name ==
"Enemy" {
incrementScore()
secondBody.node?.removeFromParent()
} else if firstBody.node?.name == "Missile" &&
secondBody.node?.name == "Boat" {
incrementScore()
}
}
I have added the "physicsWorld.contactDelegate = self" in the didMove toview
I have also added physicsBodies to all the relevant spritenodes:
func fireMissile() {
let missile = SKSpriteNode(color: .yellow, size: CGSize(width: 20,
height: 5))
missile.name = "Missile"
missile.position = CGPoint(x: player.position.x + 28, y:
player.position.y + 10)
missile.zPosition = 2
missile.physicsBody = SKPhysicsBody(rectangleOf: missile.size)
missile.physicsBody?.isDynamic = false
missile.physicsBody?.categoryBitMask = ColliderType.Bullet
missile.physicsBody?.collisionBitMask = ColliderType.Enemy |
ColliderType.Boat
missile.physicsBody?.contactTestBitMask = ColliderType.Enemy |
ColliderType.Boat
self.addChild(missile)
}
func createEnemies() {
let enemy = SKSpriteNode(imageNamed: "Enemy1")
enemy.name = "Enemy"
enemy.anchorPoint = CGPoint(x: 0.5, y: 0.5)
enemy.physicsBody = SKPhysicsBody(circleOfRadius: enemy.size.height
/ 2)
enemy.physicsBody?.categoryBitMask = ColliderType.Enemy
enemy.physicsBody?.affectedByGravity = false
enemy.physicsBody?.isDynamic = false
enemy.zPosition = 3
enemy.position.y = self.frame.height + 100
enemy.position.x = CGFloat.randomBetweenNumbers(firstNum: -347.5,
secondNum: -85)
self.addChild(enemy)
}
func createBoat() {
let boat = SKSpriteNode(imageNamed: "Boat")
boat.name = "Boat"
boat.anchorPoint = CGPoint(x: 0.5, y: 0.5)
boat.physicsBody = SKPhysicsBody(circleOfRadius: boat.size.height /
2)
boat.physicsBody?.categoryBitMask = ColliderType.Boat
boat.physicsBody?.affectedByGravity = false
boat.physicsBody?.isDynamic = false
boat.zPosition = 3
boat.position.y = self.frame.height + 100
boat.position.x = CGFloat.randomBetweenNumbers(firstNum: 0,
secondNum: 0)
self.addChild(boat)
}
Firstly, some of your categories are wrong:
static let Wall: UInt32 = 3
static let Bullet: UInt32 = 4
This effectively defines Wall as being both a Boat and an Enemy. Change them to:
static let Wall: UInt32 = 4
static let Bullet: UInt32 = 8
(Categories should always be unique powers of 2 - 1, 2, 4, 8, 16 etc).
The rest looks ok, so try that and let us know if it’s working.
Edit:
OK - just noticed that all of your physics bodies have their isDynamic property set to false - this means that, among other things, the body will not trigger contacts. so if you want missile to generate contacts with either enemy or boat, then either missile should be dynamic or both enemy and boat should be dynamic (only 1 of the 2 objects involved in a contact needs to by dynamic).

why physics is not working with SKShapes?

I am trying to detect the collision between the circle called player and the balls called enemyCircle but nothing happen
func didBeginContact(contact: SKPhysicsContact) {
if let nodeA = contact.bodyA.node as? SKShapeNode, let nodeB = contact.bodyB.node as? SKShapeNode {
if nodeA.fillColor != nodeB.fillColor {
print("not same color")
}else {
print("same color")
}
}
}
private func drawPlayer(radius: CGFloat) {
player = SKShapeNode(circleOfRadius: radius)
player.physicsBody = SKPhysicsBody(circleOfRadius: radius)
player.physicsBody?.affectedByGravity = false
player.physicsBody?.isDynamic = false
player.physicsBody?.pinned = true
player.physicsBody?.categoryBitMask = bodyType.player.rawValue
player.physicsBody?.collisionBitMask = bodyType.enemy.rawValue
player.physicsBody?.contactTestBitMask = bodyType.enemy.rawValue
player.strokeColor = SKColor.purple
player.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
player.glowWidth = 2.0
world.addChild(player)
}
private func drawEnemy(radius: CGFloat, color: SKColor) {
let enemyCircle = SKShapeNode(circleOfRadius: radius)
enemyCircle.fillColor = color
enemyCircle.glowWidth = 0.5
enemyCircle.strokeColor = color
enemyCircle.physicsBody = SKPhysicsBody(circleOfRadius: radius)
enemyCircle.physicsBody?.affectedByGravity = false
enemyCircle.physicsBody?.isDynamic = false
enemyCircle.physicsBody?.collisionBitMask = bodyType.player.rawValue
enemyCircle.physicsBody?.categoryBitMask = bodyType.enemy.rawValue
let nextEnemyPosition = determineNextEnemyPosition()
let enemyPosition = spawnAtRandomPosition(edge: nextEnemyPosition)
enemyCircle.position = enemyPosition
world.addChild(enemyCircle)
numberOfEnemies += 1
enemyCircle.name = String(numberOfEnemies)
enemy.append(enemyCircle)
runToCenter(enemy: enemyCircle)
}
enum bodyType: UInt32 {
case enemy = 1
case player = 2
}
and this
class GameScene: SKScene, SKPhysicsContactDelegate
and i put this in didMove func
self.physicsWorld.contactDelegate = self
can someone explain me the issues here
Note:: I made both not dynamic as I don't want anyone to move the other or change it, just be notified when it touches so I stop the game