I created a function that continuously spawns coins forever.
However, I'm having trouble removing the coin that is touched by the ball that hits them. By the way, I have a variable called coinBoolean set to false in the class. Here is my code:
func didBeginContact(contact: SKPhysicsContact) {
if ((contact.bodyA.categoryBitMask == groundGroup && contact.bodyB.categoryBitMask == ballGroup) ||
(contact.bodyA.categoryBitMask == ballGroup && contact.bodyB.categoryBitMask == groundGroup)) {
}
if ((contact.bodyA.categoryBitMask == ballGroup && contact.bodyB.categoryBitMask == coinGroup) ||
(contact.bodyA.categoryBitMask == coinGroup && contact.bodyB.categoryBitMask == ballGroup)) {
print("TRUE")
coinBoolean = true
}
else {
print("false")
coinBoolean = false
}
}
func addAndMoveCoins() {
let coin = SKSpriteNode(imageNamed: "Coin")
coin.zPosition = 55
coin.position = CGPoint(x: self.frame.size.width*0.9, y: randomY())
coin.physicsBody = SKPhysicsBody(rectangleOfSize: coin.size)
coin.physicsBody?.categoryBitMask = coinGroup
coin.physicsBody?.contactTestBitMask = ballGroup
coin.physicsBody?.affectedByGravity = false
coin.physicsBody?.dynamic = false
let moveCoinLeft = SKAction.moveByX(-self.size.height, y: 0, duration: 5)
let repeatAction1 = SKAction.repeatActionForever(moveCoinLeft)
let removeObstacle1 = SKAction.removeFromParent()
let moveAndRemove1 = SKAction.sequence([repeatAction1, removeObstacle1])
coin.runAction(moveAndRemove1)
self.addChild(coin)
if coinBoolean == true {
coin.removeFromParent()
}
else if coinBoolean == false {
}
}
func repeatCoins() {
let generateCoins = SKAction.sequence([SKAction.runBlock(self.addAndMoveCoins), SKAction.waitForDuration(1.3)])
let endlessAction = SKAction.repeatActionForever(generateCoins)
runAction(endlessAction)
}
Your coinBoolean variable is probably changing too fast. I suggest getting the node you want to remove from the collision (bodyA or bodyB) and removing it from its parent
Just set an action on the coin object during the contact phase instead of doing booleans.
contact.bodyA.node.runAction(SKAction.removeFromParent())
You would need to check if bodyA is the coin object of course
Related
I try to make objects moving down the screen with 2 categories. On the bottom I have 2 cars (left and right), and if the objects hit the car 1 is a "collision" and the other is a "spinner" that will spinn the car around....
Object setup have a random of "collision" and "spinner" object....
Only "spinner" objects should be detected now as I not added "collision" yet.
My problem is that all are treated the same, so spinner and collision objects get "Hit right car! / Hit left car!" in my console.
"Hit left tree! / "Hit right tree!" never triggers.
Code:
func didBegin(_ contact: SKPhysicsContact) {
if enableContact == true {
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 & PC.L_CAR) != 0 && (secondBody.categoryBitMask & PC.L_COLLIDER) != 0 {
print ("Hit left car!")
}
else if (firstBody.categoryBitMask & PC.R_CAR) != 0 && (secondBody.categoryBitMask & PC.R_COLLIDER) != 0 {
print ("Hit right car!")
}
else if (firstBody.categoryBitMask & PC.L_CAR) != 0 && (secondBody.categoryBitMask & PC.L_SPINNER) != 0 {
print("Hit left tree!")
}
else if (firstBody.categoryBitMask & PC.R_CAR) != 0 && (secondBody.categoryBitMask & PC.R_SPINNER) != 0 {
print("Hit right tree!")
}
}}
Car setup:
func setUp() {
canMove = true
leftCar = self.childNode(withName: "leftCar") as! SKSpriteNode
rightCar = self.childNode(withName: "rightCar") as! SKSpriteNode
centerPoint = self.frame.size.width / self.frame.size.height
leftCar.physicsBody?.isDynamic = true
leftCar.physicsBody?.categoryBitMask = PC.L_CAR
leftCar.physicsBody?.contactTestBitMask = PC.L_SPINNER
leftCar.physicsBody?.collisionBitMask = 0
rightCar.physicsBody?.isDynamic = true
rightCar.physicsBody?.categoryBitMask = PC.R_CAR
rightCar.physicsBody?.contactTestBitMask = PC.R_SPINNER
rightCar.physicsBody?.collisionBitMask = 0
Object setup:
#objc func leftTraffic() {
if !stopEverything {
let leftTrafficItem : SKSpriteNode!
let randonNumber = Helper().randomBetweenTwoNumbers(firstNumber: 1, secondNumber: 13)
switch Int(randonNumber) {
case 1...4:
leftTrafficItem = SKSpriteNode(imageNamed: "orangeCar")
leftTrafficItem.name = "orangeCar"
leftTrafficItem.physicsBody?.categoryBitMask = PC.L_COLLIDER
break
case 5...8:
leftTrafficItem = SKSpriteNode(imageNamed: "greenCar")
leftTrafficItem.name = "greenCar"
leftTrafficItem.physicsBody?.categoryBitMask = PC.L_COLLIDER
break
case 9...11:
leftTrafficItem = SKSpriteNode(imageNamed: "rock")
leftTrafficItem.name = "rock"
leftTrafficItem.physicsBody?.categoryBitMask = PC.L_COLLIDER
break
case 12...13:
leftTrafficItem = SKSpriteNode(imageNamed: "tree")
leftTrafficItem.name = "tree"
leftTrafficItem.physicsBody?.categoryBitMask = PC.L_SPINNER
break
default:
leftTrafficItem = SKSpriteNode(imageNamed: "orangCar")
leftTrafficItem.name = "orangeCar"
leftTrafficItem.physicsBody?.categoryBitMask = PC.L_COLLIDER
}
leftTrafficItem.anchorPoint = CGPoint(x: 0.5, y: 0.5)
leftTrafficItem.zPosition = 10
let randomNum = Helper().randomBetweenTwoNumbers(firstNumber: 1, secondNumber: 10)
switch Int(randomNum) {
case 1...4:
leftTrafficItem.position.x = -280
break
case 5...10:
leftTrafficItem.position.x = -100
break
default:
leftTrafficItem.position.x = -280
}
leftTrafficItem.position.y = 700
leftTrafficItem.physicsBody = SKPhysicsBody(texture: leftTrafficItem.texture!, size: (leftTrafficItem.texture?.size())!)
leftTrafficItem.physicsBody?.isDynamic = true
leftTrafficItem.physicsBody?.contactTestBitMask = 0
leftTrafficItem.physicsBody?.collisionBitMask = 0
leftTrafficItem.physicsBody?.affectedByGravity = false
addChild(leftTrafficItem)
}
}
So I added this recent code (to keep enemy nodes in the borders of the screen) with help from #Fluidity from my last question, now my issue is that with this code, TOO many enemies are coming down, and they're not colliding with either the bullets the Player shoots, or the Player nodes themselves, like it was previously. Here's some code:
func createEnemy() {
let wait:SKAction = SKAction.wait(forDuration: 1.25)
//1.0 = 1.35
let block:SKAction = SKAction.run(launchEnemy)
let seq:SKAction = SKAction.sequence( [ wait, block ])
let repeated:SKAction = SKAction.repeatForever(seq)
self.run(repeated, withKey:"enemyLaunchAction")
self.run(seq, withKey:"EnemySpawnAction")
}
func launchEnemy() {
let EnemyMissile:EnemyClass = EnemyClass()
EnemyMissile.createEnemy("Enemy")
addChild(EnemyMissile)
let enemy = EnemyMissile.EnemyNode
// let randomX = arc4random_uniform( UInt32(screenWidth))
var randomX = CGFloat(arc4random_uniform(UInt32(self.size.width))) // This is not a perfect rando algo.
// Because our random x was from 0 through screenwidth, but .position works
// on -halfWidth through +halfWidth
randomX -= (self.frame.size.width / 2)
// Because spawning on the border will mean that the enemy is HALF out of view:
/*
right border: |
enemy # frame.maxX: x
- offset: x|
- size.w/2 x |
*/
enemy.position = CGPoint( x: CGFloat(randomX) - (screenWidth / 2), y: screenHeight + 50)
let action = SKAction.move(by: CGVector(dx: 0, dy: -400 + speedScore), duration: 5.0)
enemy.run(SKAction.repeatForever(action))
increaseSpeed()
print("enemy spawned!")
self.run(action, withKey:"LaunchEnemyAction")
let offset = CGFloat(5) // pixels, so enemy will have some spacing between borders.
if randomX > self.frame.maxX - offset - (enemy.size.width / 2) {
randomX = self.frame.maxX - offset - (enemy.size.width / 2)
}
else if randomX < self.frame.minX + offset + (enemy.size.width / 2) {
randomX = self.frame.minX + offset + (enemy.size.width / 2)
}
enemy.position.x = randomX
}
func keepEnemyInBounds() {
// A better way to do this would be to have a dictionary of enemies:
for node in self.children {
guard node.name == "enemy" else { continue }
let enemy = node as! SKSpriteNode
if enemy.position.x > frame.maxX - (enemy.size.width / 2) {
enemy.position.x = frame.maxX - (enemy.size.width / 2)
}
else if enemy.position.x < frame.minX + (enemy.size.width / 2) {
enemy.position.x = frame.minX + (enemy.size.width / 2)
}
}
}
override func update(_ currentTime: TimeInterval) {
launchEnemy()
}
override func didFinishUpdate() {
keepEnemyInBounds()
}
CONTACT LISTENER CODE:
func didBegin(_ contact: SKPhysicsContact) {
_ = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
if (contact.bodyA.categoryBitMask == BodyType.enemy.rawValue && contact.bodyB.categoryBitMask == BodyType.bullet.rawValue) {
if let missile = contact.bodyA.node! as? EnemyClass {
enemyMissileAndBullet(missile)
}
contact.bodyB.node?.name = "removeNode"
} else if ( contact.bodyA.categoryBitMask == BodyType.bullet.rawValue && contact.bodyB.categoryBitMask == BodyType.enemy.rawValue) {
if let missile = contact.bodyB.node! as? EnemyClass {
enemyMissileAndBullet(missile)
}
contact.bodyA.node?.name = "removeNode"
}
if ( contact.bodyA.categoryBitMask == BodyType.player.rawValue && contact.bodyB.categoryBitMask == BodyType.enemy.rawValue) {
createExplosion(contact.bodyB.node!.position , image:"explosion2")
contact.bodyB.node?.name = "removeNode"
updateScore(1)
subtractHealth()
playSound("shipExplosion")
} else if (contact.bodyA.categoryBitMask == BodyType.enemy.rawValue && contact.bodyB.categoryBitMask == BodyType.player.rawValue) {
createExplosion(contact.bodyA.node!.position , image:"explosion2")
contact.bodyA.node?.name = "removeNode"
updateScore(1)
subtractHealth()
playSound("shipExplosion")
}
}
EDIT:
Contact listener cleaned up: Requires player < missle < enemy
func didBegin(_ contact: SKPhysicsContact) {
let bodyA = contact.bodyA.categoryBitMask >= contact.bodyA.categoryBitMask ? contact.bodyA : contact.bodyB
let bodyB = contact.bodyA.categoryBitMask < contact.bodyA.categoryBitMask ? contact.bodyA : contact.bodyB
if contact.bodyA.categoryBitMask == BodyType.bullet.rawValue && contact.bodyB.categoryBitMask == BodyType.enemy.rawValue {
if let missile = contact.bodyA.node! as? EnemyClass {
enemyMissileAndBullet(missile)
}
contact.bodyB.node?.name = "removeNode"
}
else if contact.bodyA.categoryBitMask == BodyType.player.rawValue && contact.bodyB.categoryBitMask == BodyType.enemy.rawValue {
createExplosion(bodyB.node!.position , image:"explosion2")
bodyB.node?.name = "removeNode"
updateScore(1)
subtractHealth()
playSound("shipExplosion")
}
}
This code colorizes my ball.
var colorize1 = SKAction.colorizeWithColor(.redColor(), colorBlendFactor: 1.0, duration: 0.3)
var colorize2 = SKAction.colorizeWithColor(.greenColor(), colorBlendFactor: 1.0, duration: 0.3)
var colorize3 = SKAction.colorizeWithColor(.blueColor(), colorBlendFactor: 1.0, duration: 0.3)
var actions = [colorize1, colorize2, colorize3]
var randomIndex = Int(arc4random_uniform(3))
var action = actions[randomIndex]
let seconds = 0.14
let delay = seconds * Double(NSEC_PER_SEC) // nanoseconds per seconds
let dispatchTime = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
dispatch_after(dispatchTime, dispatch_get_main_queue(), {
self.Ball.runAction(action)
})
}
This code colorizes my walls.
var colorBucket = [UIColor]()
func randomColor() -> UIColor {
if colorBucket.isEmpty {
fillBucket()
}
let randomIndex = Int(arc4random_uniform(UInt32(colorBucket.count)))
let randomColor = colorBucket[randomIndex]
colorBucket.removeAtIndex(randomIndex)
return randomColor
}
func fillBucket() {
colorBucket = [UIColor.redColor(), UIColor.greenColor(), UIColor.blueColor()]
}
I need to write an if statement that detects whether or not the ball and wall are the same color when I collide with an spritekitnode() located in between my walls.
I've tried these:
if Wall1.color == UIColor.redColor() && Wall2.color == UIColor.redColor() && Ball.color != UIColor.redColor {
died = true
}
My main issue is writing the if statement that determines if the ball and walls are the same color. I have the collision part down.
Thank you so much for your help.
func didBeginContact(contact: SKPhysicsContact) {
let firstBody = contact.bodyA
let secondBody = contact.bodyB
let ballWasContacted = firstBody.categoryBitMask == PhysicsCat.Ball || secondBody.categoryBitMask == PhysicsCat.Ball
var wallWasContacted = firstBody.categoryBitMask == PhysicsCat.Wall || secondBody.categoryBitMask == PhysicsCat.Wall
let scoreWasContacted = firstBody.categoryBitMask == PhysicsCat.Score || secondBody.categoryBitMask == PhysicsCat.Score
let colorWasContacted = firstBody.categoryBitMask == PhysicsCat.Color || secondBody.categoryBitMask == PhysicsCat.Ball
if ballWasContacted {
if scoreWasContacted {
score += 1
scoreLbl.text = "\(score)"
let scoreNode = firstBody.categoryBitMask == PhysicsCat.Score ? firstBody.node : secondBody.node
scoreNode!.removeFromParent()
} else if wallWasContacted {
enumerateChildNodesWithName("wallPair", usingBlock: ({
(node, error) in
node.speed = 0
self.removeAllActions()
}))
if died == false {
died = true
createBTN()
fallDie()
}
}
}
else if colorWasContacted {
}
}
The colorWasContacted isn't really being used right now, but everything else is.
Code to change color of wall.
action = SKAction.colorizeWithColor(randomColor(), colorBlendFactor: 1.0, duration: 0.3)
Wall1.runAction(action)
This is what happened when I added the breakpoints.
Breakpoints
Contacts only happen with two elements at a time, so you could do something like this in your didBeginContact (edit, based on your actual code):
...
else if wallWasContacted {
let sprite1 = firstBody.node as! SKSpriteNode
let sprite2 = secondBody.node as! SKSpriteNode
if sprite1.color.isEqual(sprite2.color) {
// Whatever your heart desires...
} else {
// Something else you'd like to have done...
}
enumerateChildNodesWithName("wallPair", usingBlock: ({
(node, error) in
node.speed = 0
self.removeAllActions()
}))
if died == false {
died = true
createBTN()
fallDie()
}
}
As this might help with this isolated issue, I belive (from looking through your other threads) that you need to rethink the whole logic when it comes to discovering passing between walls...
As you are mixing questions and re-asking them (worded slightly differently) I'll attempt dealing with your colorization problems here as well, as this is most likely where you are failing. I say most likely because the code you supply does not show us that you colorize the walls at all.
It shows us that:
You have logic in place to change color on the ball.
You have logic to pick out a random color from a colorBucket.
You have logic to refill the bucket when all the colors have been
used.
It does not show us that you actually change the color-information for the walls. Could you please supply this part of the code (if it exists).
I would like to be able to identify which exact target sprite node is hit when the user flings another sprite at a group of target sprites. Here is how I set up my sprites under the didMoveToView function (only including the relevant lines of code here)
let flingerTexture = SKTexture(imageNamed: "flinger")
flingerNode.position = CGPoint(x: 768, y: 440)
flingerNode.physicsBody = SKPhysicsBody(texture: flingerTexture, size: flingerNode.size)
flingerNode.physicsBody?.dynamic = true
flingerNode.physicsBody!.categoryBitMask = PhysicsCategory.Flinger
flingerNode.physicsBody!.collisionBitMask = PhysicsCategory.Edge | PhysicsCategory.Bubble | PhysicsCategory.Ball
flingerNode.physicsBody?.contactTestBitMask = PhysicsCategory.Ball
let rotationConstraint = SKConstraint.zRotation(SKRange(lowerLimit: -π/4, upperLimit: π/4))
flingerNode.constraints = [rotationConstraint]
addChild(flingerNode)
// -------------Setup targets---------------
let range: Range<Int> = 1...10
for numbers in range {
let ballNode: BallNode = BallNode(imageNamed: "\(numbers)a")
let positionX = CGFloat.random(min: size.width / 6, max: size.width * 5/6)
let positionY = CGFloat.random(min: size.height * 4/9, max: size.height * 8/9)
ballNode.position = CGPoint(x: positionX, y: positionY)
ballNode.name = "Ball"
ballNode.ballIndex = Int(numbers)
index = ballNode.ballIndex
ballNode.ballHit = false
addChild(ballNode)
ballNode.physicsBody = SKPhysicsBody(circleOfRadius: 100)
ballNode.physicsBody!.affectedByGravity = false
ballNode.physicsBody?.dynamic = true
ballNode.physicsBody!.restitution = 0.5
ballNode.physicsBody!.friction = 0.0
ballNode.physicsBody!.categoryBitMask = PhysicsCategory.Ball
ballNode.physicsBody!.collisionBitMask = PhysicsCategory.Ball | PhysicsCategory.Bubble | PhysicsCategory.Edge | PhysicsCategory.Flinger | PhysicsCategory.Wall
ballNode.physicsBody?.contactTestBitMask = PhysicsCategory.Flinger
ballNode.userData = NSMutableDictionary()
ballArray.append(ballNode.ballIndex)
}
I am able to detect the collision, but am unable to retrieve the additional userData that would identify which exact ballNode was struck. When I tried the following code, it only returns an output of "nil".
func didBeginContact(contact: SKPhysicsContact!) {
let collision = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
if collision == PhysicsCategory.Flinger | PhysicsCategory.Ball {
println(ballNode.userData)
}
}
I am assuming that PhysicsCategory.Flinger is less than PhysicsCategory.Ball. Then in didContactBegan you can use this code.
func didBeginContact(contact: SKPhysicsContact) {
var body1 : SKPhysicsBody!
var body2 : SKPhysicsBody!
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
body1 = contact.bodyA
body2 = contact.bodyB
}
else {
body1 = contact.bodyB
body2 = contact.bodyA
}
if body1.categoryBitMask == PhysicsCategory.Flinger &&
body2.categoryBitMask == PhysicsCategory.Ball {
if let ballNode = body2.node {
println(ballNode.userData)
}
}
}
The conditions have to be reversed if PhysicsCategory.Flinger is greater than PhysicsCategory.Ball.
if body1.categoryBitMask == PhysicsCategory.Ball &&
body2.categoryBitMask == PhysicsCategory.Flinger {
if let ballNode = body1.node {
println(ballNode.userData)
}
}
Thanks so much for pointing me in the right direction #rakeshbs! Your didBeginContact method works - the problem was I wasn't adding the userData correctly. I resolved this by adding the following line to my didMoveToView function:
ballNode.userData = ["ballNumber" : ballNode.ballIndex]
I have created a clone of the app Flappy Bird, and I’ve tried to add coins which appear randomly around the game. My problem is I try to make the coins disappear when the bird collides with them, but it has proven a difficult task. I have a function that spawns in a coin every 3 seconds; at first all the coins had the same categoryBitMask's, but then I changed it so every 3 coins had the the categoryBitMask of 1, 2, 3, recurring.
I have tried many slight variations on the code to get rid of the coins as they collide with the bird, sometimes the coins will disappear, but with other problems, like the wrong coins disappearing or no more coins spawning. Here is all the code I think you need to see for my problem:
class GameScene: SKScene, SKPhysicsContactDelegate {
var bird = SKSpriteNode()
var coin = SKSpriteNode()
let birdGroup: UInt32 = 1
let objectGroup: UInt32 = 2
let gapGroup: UInt32 = 0 << 3
let gapGroup2: UInt32 = 0 << 4
let gapGroup3: UInt32 = 0 << 5
var gameOver = 0
var movingObjects = SKNode()
var coinGroup1 = SKNode()
var coinGroup2 = SKNode()
var coinGroup3 = SKNode()
override func didMoveToView(view: SKView) {
/********| Bird physics body |********/
bird.physicsBody?.categoryBitMask = birdGroup
bird.physicsBody?.collisionBitMask = gapGroup
bird.physicsBody?.collisionBitMask = gapGroup2
bird.physicsBody?.collisionBitMask = gapGroup3
bird.physicsBody?.collisionBitMask = objectGroup
bird.physicsBody?.contactTestBitMask = objectGroup
bird.zPosition = 10
self.addChild(bird)
/********| Bird physics body |********/
/********| Spawning coins |********/
var timer = NSTimer.scheduledTimerWithTimeInterval(3, target: self, selector: Selector("spawnPipes"), userInfo: nil, repeats: true)
}
func spawnPipes(){
var coinTexture = SKTexture(imageNamed: "coin.png")
coin = SKSpriteNode(texture: coinTexture)
coin.size.height = bird.size.height
coin.size.width = bird.size.width
coin.position = CGPoint(x: CGRectGetMidX(self.frame) + self.frame.size.width * 1.33, y: CGRectGetMidY(self.frame) + movementAmount2)
coin.physicsBody = SKPhysicsBody(circleOfRadius: coin.size.height / 2)
coin.physicsBody?.dynamic = false
coin.runAction(moveAndRemove)
coin.physicsBody?.contactTestBitMask = birdGroup
if(i == 1){
coin.physicsBody?.categoryBitMask = gapGroup
coin.physicsBody?.collisionBitMask = gapGroup
coinGroup1.addChild(coin)
}else if(i == 2){
coin.physicsBody?.categoryBitMask = gapGroup2
coin.physicsBody?.collisionBitMask = gapGroup2
coinGroup2.addChild(coin)
}else if(i == 3){
coin.physicsBody?.categoryBitMask = gapGroup3
coin.physicsBody?.collisionBitMask = gapGroup3
coinGroup3.addChild(coin)
}else{
i = 1
}
}
/********| Spawning coins |********/
/********| Removing coins |********/
func didBeginContact(contact: SKPhysicsContact) {
if(gameOver == 0){
if((contact.bodyA.categoryBitMask == gapGroup || contact.bodyB.categoryBitMask == gapGroup) || (contact.bodyA.categoryBitMask == gapGroup2 || contact.bodyB.categoryBitMask == gapGroup2) || (contact.bodyA.categoryBitMask == gapGroup3 || contact.bodyB.categoryBitMask == gapGroup3)){
score++
scoreLabel.text = "\(score)"
if(contact.bodyA.categoryBitMask == gapGroup || contact.bodyB.categoryBitMask == gapGroup){
coinGroup1.removeAllChildren()
}else if(contact.bodyA.categoryBitMask == gapGroup2 || contact.bodyB.categoryBitMask == gapGroup2){
coinGroup2.removeAllChildren()
}else if(contact.bodyA.categoryBitMask == gapGroup3 || contact.bodyB.categoryBitMask == gapGroup3){
coinGroup3.removeAllChildren()
}
/********| Removing coins |********/
Here is all of my code if this ^^^ isn't enough:
http://pastebin.com/7yGh5gBn