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
Related
I'm building a function in which a random variable will choose 1 in 4 SKSpritenode in an array and assign itself to it. However, that randomline, although appear as expected on the screen, does not contain any physicsbody property so it cannot be collide with other node. Below is my code:
func line() {
redLine = SKSpriteNode(imageNamed: "redline")
blueLine = SKSpriteNode(imageNamed: "blueline")
yellowLine = SKSpriteNode(imageNamed: "yellowline")
greenLine = SKSpriteNode(imageNamed: "greenline")
let lineArray = [redLine,blueLine,yellowLine,greenLine]
// Add physics
lineArray[0].physicsBody?.categoryBitMask = gamePhysics.RedLine
lineArray[1].physicsBody?.categoryBitMask = gamePhysics.BlueLine
lineArray[2].physicsBody?.categoryBitMask = gamePhysics.YellowLine
lineArray[3].physicsBody?.categoryBitMask = gamePhysics.GreenLine
for i in 0...3 {
lineArray[i].physicsBody = SKPhysicsBody(circleOfRadius: 10)
lineArray[i].physicsBody?.collisionBitMask = gamePhysics.RedBall | gamePhysics.BlueBall | gamePhysics.YellowBall | gamePhysics.GreenBall
lineArray[i].physicsBody?.contactTestBitMask = gamePhysics.RedBall | gamePhysics.BlueBall | gamePhysics.YellowBall | gamePhysics.GreenBall
lineArray[i].physicsBody?.affectedByGravity = false
lineArray[i].physicsBody?.isDynamic = true
}
let randomLine:SKSpriteNode! = lineArray.randomElement()
...
self.addChild(randomLine)
...
randomLine.run(SKAction.sequence([moveLine,delay,removeLine]))
}
So basically when the randomline collide with other node, nothing happen.
Here is my didBegin (contact) func:
func didBegin(_ contact: SKPhysicsContact) {
let firstBody = contact.bodyA
let secondBody = contact.bodyB
if firstBody.categoryBitMask == gamePhysics.RedBall && secondBody.categoryBitMask == gamePhysics.RedLine || firstBody.categoryBitMask == gamePhysics.BlueBall && secondBody.categoryBitMask == gamePhysics.BlueLine || firstBody.categoryBitMask == gamePhysics.YellowBall && secondBody.categoryBitMask == gamePhysics.YellowLine || firstBody.categoryBitMask == gamePhysics.GreenBall && secondBody.categoryBitMask == gamePhysics.GreenLine {
currentScore += 1
secondBody.node?.removeFromParent()
print("hit point")
} else if firstBody.categoryBitMask == gamePhysics.RedLine && secondBody.categoryBitMask == gamePhysics.RedBall || firstBody.categoryBitMask == gamePhysics.BlueLine && secondBody.categoryBitMask == gamePhysics.BlueBall || firstBody.categoryBitMask == gamePhysics.YellowLine && secondBody.categoryBitMask == gamePhysics.YellowBall || firstBody.categoryBitMask == gamePhysics.GreenLine && secondBody.categoryBitMask == gamePhysics.GreenBall {
currentScore += 1
firstBody.node?.removeFromParent()
print("hit point")
}
}
Any help would be appreciated!
EDIT: Here is the ball func(). Maybe somehow the SKNode contain multiple categorybitmask and therefore, won't work?
func Ball() {
ballNode = SKNode()
redBall = SKSpriteNode(imageNamed: "redball")
blueBall = SKSpriteNode(imageNamed: "blueball")
yellowBall = SKSpriteNode(imageNamed: "yellowball")
greenBall = SKSpriteNode(imageNamed: "greenball")
let ballArray = [redBall,blueBall,yellowBall,greenBall]
...
// Add physics
ballArray[0].physicsBody?.categoryBitMask = gamePhysics.RedBall
ballArray[1].physicsBody?.categoryBitMask = gamePhysics.BlueBall
ballArray[2].physicsBody?.categoryBitMask = gamePhysics.YellowBall
ballArray[3].physicsBody?.categoryBitMask = gamePhysics.GreenBall
for i in 0...3 {
ballArray[i].physicsBody = SKPhysicsBody(circleOfRadius: 20)
ballArray[i].physicsBody?.collisionBitMask = gamePhysics.RedLine | gamePhysics.BlueLine | gamePhysics.YellowLine | gamePhysics.GreenLine
ballArray[i].physicsBody?.contactTestBitMask = gamePhysics.RedLine | gamePhysics.BlueLine | gamePhysics.YellowLine | gamePhysics.GreenLine
ballArray[i].physicsBody?.affectedByGravity = false
ballArray[i].physicsBody?.isDynamic = false
}
ballNode.addChild(redBall)
ballNode.addChild(greenBall)
ballNode.addChild(yellowBall)
ballNode.addChild(blueBall)
self.addChild(ballNode)
}
UPDATE 2:
Here is a struct where i store my categorybitmask:
struct gamePhysics {
static let RedBall : UInt32 = 0x1 << 1
static let BlueBall : UInt32 = 0x1 << 2
static let GreenBall : UInt32 = 0x1 << 3
static let YellowBall : UInt32 = 0x1 << 4
static let RedLine : UInt32 = 0x1 << 5
static let BlueLine : UInt32 = 0x1 << 6
static let GreenLine : UInt32 = 0x1 << 7
static let YellowLine : UInt32 = 0x1 << 8
}
Did you remember setting the contact delegate inside your scene? Place this line of code inside didMove(to:):
self.physicsWorld.contactDelegate = self
If this don't solve the issue, I would make the if conditions inside didBegin more explicit using parentheses between the && and || statements.
Looking to the rest of the code I can not see what else could be wrong, what you did should work. Are the print("hit point") you placed inside the if statements been reached?
OMG i found the answer to my question. The reason for the value of the categorybitmask to return "nil" was because i actually assign the node to categorybitmask before specified its physicsbody. Move the line down a few line and everything is solved!
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)
}
}
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).
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()
}
}
So, I am still experimenting with Sprite Kit for my first time ever, and I would like to test for collision. So, I searched around a bit in Apple's documentation, around Stack Overflow, online tutorials, and other forums. However, I was unable to find something a tip or code that makes what I am doing work. So, here are the relevant pieces of code:
This is the code for an obstacle:
func createObstacle(){
var ball = SKShapeNode(circleOfRadius: 20)
var width = UInt32(self.frame.width)
var random_number = arc4random_uniform(width)
ball.position = CGPointMake(CGFloat(random_number), frame.height+20)
ball.strokeColor = SKColor.blackColor()
ball.glowWidth = 1.0
ball.fillColor = SKColor.darkGrayColor()
ball.physicsBody = SKPhysicsBody(circleOfRadius: 20)
ball.physicsBody!.affectedByGravity = true
ball.physicsBody?.categoryBitMask = 6
ball.physicsBody?.dynamic = true
self.addChild(ball)
}
This is relevant code for the thing that it would collide with:
let circle = SKShapeNode(circleOfRadius: 20)
circle.physicsBody = SKPhysicsBody(circleOfRadius: 20)
circle.fillColor = SKColor.blueColor()
circle.strokeColor = SKColor.blueColor()
circle.glowWidth = 1.0
circle.physicsBody?.categoryBitMask = 4
circle.physicsBody?.dynamic = true
circle.physicsBody?.affectedByGravity = false
And this is the code for contact:
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 == 4 && secondBody.categoryBitMask == 6) || (firstBody.categoryBitMask == 6 && secondBody.categoryBitMask == 4)){
println("HI")
}else{println("NO")}
}
Sadly, nothing is being printed at all, so something's wrong. Any idea why this doesn't work?
Your class should have delegate SKPhysicsContactDelegate.
class GameScene: SKScene, SKPhysicsContactDelegate {
In didMoveToView write this:
physicsWorld.contactDelegate = self
EDIT
Define CategoryBitMask like this
struct PhysicsCategory {
static let circleCategory : UInt32 = 0b1 // 1
static let ballCategory : UInt32 = 0b10 // 2
}
Give CategoryBitMask to circle and ball
circle.physicsBody?.categoryBitMask = PhysicsCategory.circleCategory
ball.physicsBody?.categoryBitMask = PhysicsCategory.ballCategory
Then check contact like this:
(func didBeginContact(contact: SKPhysicsContact) {
if ((contact.bodyA.categoryBitMask == 0b1 && contact.bodyB.categoryBitMask == 0b10 ) || ( contact.bodyA.categoryBitMask == 0b1 && contact.BodyB.categoryBitMask == 0b1 ))
println("Contact")
}
}
Sorry for typos didnt used editor