Gameover on any collision - Swift Spritekit - swift

In my game I have planes moving around. In my game, I want so that if any plane collides with another, it prints "game over". I have added SKPhysicsContactDelegate to my gamescene. I added a physics bodies to my planes. Then, I added this function to my didMoveToView function:
func didBegin(_ contact: SKPhysicsContact){
print("Game over")
}
Now, when I run my game, and the planes collide, nothing prints to the console. How can I change my code so if any plane collides with another (there are more than 2) it prints game over to the console?
Edit: I have set the physics world contact delegate to self. I have not called this function - do I have to - I thought that this function runs when there is a collision in the scene.
Here is my code:
//
// GameScene.swift
// PlaneGame
//
// Created by Lucas Farleigh on 09/04/2017.
// Copyright © 2017 Farleigh Tech. All rights reserved.
//
import SpriteKit
import GameplayKit
class GameScene: SKScene, SKPhysicsContactDelegate {
private let Background = SKSpriteNode(imageNamed: "Background")
private let PlaneRed = SKSpriteNode(imageNamed: "PlaneRed")
private let PlaneBlue = SKSpriteNode(imageNamed: "PlaneBlue")
private let PlaneRed2 = SKSpriteNode(imageNamed: "PlaneRed2")
private let PlaneBlue2 = SKSpriteNode(imageNamed: "PlaneBlue2")
private let StartButton = SKLabelNode(fontNamed: "Chalkduster")
private let Trail = SKSpriteNode(imageNamed: "BlueTrail")
private let Center = SKNode()
private let Center2 = SKNode()
var GameHasStarted = false
override func didMove(to view: SKView) {
//Defining the position,size and ZPosition for the background
Background.position = CGPoint(x:self.frame.midX / 2,y: self.frame.midY)
Background.xScale = 10.0
Background.yScale = 10.0
Background.zPosition = -10
addChild(Background)
//Defining a start button - will be used later as a button
StartButton.position = CGPoint(x:self.frame.midX,y:self.frame.minY + 100)
StartButton.text = "Tap Anywhere To Start"
StartButton.fontSize = 50.0
StartButton.name = "StartButton"
addChild(StartButton)
//Setting the planered position and size up for the plane, ready to start the game
PlaneRed.position = CGPoint(x:self.frame.midX - 250,y: self.frame.midY)
PlaneRed.xScale = 0.13
PlaneRed.yScale = 0.13
PlaneRed.zPosition = 10
//Setting the planeblue position and size up for the plane, ready to start the game
PlaneBlue.position = CGPoint(x:self.frame.midX + 250,y: self.frame.midY)
PlaneBlue.xScale = 0.13
PlaneBlue.yScale = -0.13
PlaneBlue.zPosition = 10
//Setting the planered position and size up for the plane, ready to start the game
PlaneRed2.position = CGPoint(x:self.frame.midX,y: self.frame.midY + 250)
PlaneRed2.xScale = -0.13
PlaneRed2.yScale = 0.13
PlaneRed2.zPosition = 10
//Setting the planeblue position and size up for the plane, ready to start the game
PlaneBlue2.position = CGPoint(x:self.frame.midX,y: self.frame.midY - 250)
PlaneBlue2.xScale = 0.13
PlaneBlue2.yScale = 0.13
PlaneBlue2.zPosition = 10
//Making the trail
Trail.position = CGPoint(x:self.frame.midX,y:self.frame.midY)
Trail.xScale = 1.08
Trail.yScale = 1.08
addChild(Trail)
//In order to rotate the planes around a point, we must create the point as an SKNode, and make the planes a child of the point
//The point then rotates bringing the planes with it
//Setting up the point where the plane will rotate around
Center.position = CGPoint(x:self.frame.midX,y:self.frame.midY)
addChild(Center)
Center2.position = CGPoint(x:self.frame.midX,y:self.frame.midY)
addChild(Center2)
Center.addChild(PlaneRed)
Center.addChild(PlaneBlue)
Center2.addChild(PlaneRed2)
Center2.addChild(PlaneBlue2)
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//ADDING PHYSICS TO PLANES
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Defining the red planes physics body
PlaneRed.physicsBody = SKPhysicsBody(rectangleOf: PlaneRed.size)
PlaneRed2.physicsBody = SKPhysicsBody(rectangleOf: PlaneRed2.size)
physicsWorld.contactDelegate = self
physicsWorld.gravity = CGVector.zero
func didBegin(contact: SKPhysicsContact){
print("Game over")
}
}
func Start(){
//Defining an SKAction for the plane to orbit the center
let OrbitCenter = SKAction.rotate(byAngle: CGFloat(-2), duration: 3.8)
let Orbit = SKAction.repeatForever(OrbitCenter)
//Creating the action for the center 2 to rotate anti clockwise
let AntiOrbitCenter = SKAction.rotate(byAngle: CGFloat(2), duration: 3.8)
let AntiOrbit = SKAction.repeatForever(AntiOrbitCenter)
//Running the SKAction on the plane
Center.run(Orbit)
Center2.run(AntiOrbit)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?){
for touch in touches{
//Setting up the touch settings - these two variables store the nodes that the user has touched by first defining the location and then checking for nodes at this location
let location = touch.location(in: self)
let node = self.atPoint(location)
if GameHasStarted == false{
Start()
GameHasStarted = true
}
}
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}

First
Your didBegin func needs to be outside of didMove func
override func didMove(to view: SKView) {
}
func didBegin(_ contact: SKPhysicsContact) {
print("Game over")
}
second
you need to setup some physics categories for your objects so they
know what they can collide with, what they can pass through and what
collisions don't matter. You can put this outside your class
declaration
//Game Physics
struct PhysicsCategory {
static let plane: UInt32 = 0x1 << 0
static let plane2: UInt32 = 0x1 << 1
static let obstacle: UInt32 = 0x1 << 2
}
third
You need to add those physics decorations to your objects
//Defining the red planes physics body
PlaneRed.physicsBody = SKPhysicsBody(rectangleOf: PlaneRed.size)
PlaneRed.physicsBody?.categoryBitMask = PhysicsCategory.plane
PlaneRed.physicsBody?.collisionBitMask = PhysicsCategory.plane
PlaneRed.physicsBody?.contactTestBitMask = PhysicsCategory.plane
PlaneRed.physicsBody?.isDynamic = true
PlaneRed2.physicsBody = SKPhysicsBody(rectangleOf: PlaneRed2.size)
PlaneRed2.physicsBody?.categoryBitMask = PhysicsCategory.plane
PlaneRed2.physicsBody?.collisionBitMask = PhysicsCategory.plane
PlaneRed2.physicsBody?.contactTestBitMask = PhysicsCategory.plane
PlaneRed2.physicsBody?.isDynamic = true
PlaneBlue.physicsBody = SKPhysicsBody(rectangleOf: PlaneBlue.size)
PlaneBlue.physicsBody?.categoryBitMask = PhysicsCategory.plane
PlaneBlue.physicsBody?.collisionBitMask = PhysicsCategory.plane
PlaneBlue.physicsBody?.contactTestBitMask = PhysicsCategory.plane
PlaneBlue.physicsBody?.isDynamic = true
PlaneBlue2.physicsBody = SKPhysicsBody(rectangleOf: PlaneBlue2.size)
PlaneBlue2.physicsBody?.categoryBitMask = PhysicsCategory.plane
PlaneBlue2.physicsBody?.collisionBitMask = PhysicsCategory.plane
PlaneBlue2.physicsBody?.contactTestBitMask = PhysicsCategory.plane
PlaneBlue2.physicsBody?.isDynamic = true
self.physicsWorld.contactDelegate = self
self.physicsWorld.gravity = CGVector.zero

Related

Swift / SpriteKit Collision producing varying results

I have a small test project (my first using Swift / XCode) which is designed to move me away from HTML5 and Canvas for game production.
The code compiles and runs fine. I use my iPhone as the test device rather than the built in simulator.
The symptoms of the problem are
that the lasers being repeatedly fired from the player's ship appear to occasionally bend around the aliens
the names being pulled out from the nodes are being shown as their default names not the names I assigned to them at creation
In some cases the collision works fine and the alien explosion is generated and the alien sprite node is removed from the scene.
I have named the alien nodes "alien" and the laser nodes "laser".
Both have their contactTestBitMask set to the same value.
Here is my GameScene.swift code:
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var lastUpdateTime: TimeInterval = 0
var delta: TimeInterval = 0
var sp_player: SKSpriteNode!
var stars: SKSpriteNode!
var deeperstars: SKSpriteNode!
var laser: SKSpriteNode!
var alien: SKSpriteNode!
var explosionSplat1: SKSpriteNode!
var playerScore: UInt32!
struct PhysicsCategory {
static let base:UInt32 = 0x1 << 0
static let alien:UInt32 = 0x1 << 1
static let laser:UInt32 = 0x1 << 2
static let player:UInt32 = 0x1 << 3
}
override func didMove(to view: SKView) { // called when the scene is presented into view (happens only once)
playerScore = 0
physicsWorld.contactDelegate = self
physicsWorld.gravity = .zero
// BACKGROUND
backgroundColor = UIColor(red: 0/255, green: 0/255, blue: 48/255, alpha: 1.0)
print("Background color is set")
// WRAP THE STARFIELDS
// Front most layer of stars
let starsTexture = SKTexture(imageNamed: "stars.png")
let bgAnimation = SKAction.move(by: CGVector(dx: 0, dy: -starsTexture.size().height), duration: 5)
let bgReset = SKAction.move(by: CGVector(dx: 0, dy: starsTexture.size().height), duration: 0)
let bgConstantMotion = SKAction.repeatForever(SKAction.sequence([bgAnimation,bgReset]))
// Back layer of slower stars
let deeperStarsTexture = SKTexture(imageNamed: "stars-deeper.png")
let deeperStarsbgAnimation = SKAction.move(by: CGVector(dx: 0, dy: -deeperStarsTexture.size().height), duration: 8)
let deeperStarsbgReset = SKAction.move(by: CGVector(dx: 0, dy: deeperStarsTexture.size().height), duration: 0)
let deeperStarsbgConstantMotion = SKAction.repeatForever(SKAction.sequence([deeperStarsbgAnimation,deeperStarsbgReset]))
var i: CGFloat = 0
while i < 3
{
stars = SKSpriteNode(texture: starsTexture)
stars.position = CGPoint(x: frame.midX, y: starsTexture.size().height * i)
stars.size.height = frame.height
stars.run(bgConstantMotion)
stars.zPosition = -1
addChild(stars)
deeperstars = SKSpriteNode(texture: deeperStarsTexture)
deeperstars.position = CGPoint(x: frame.midX, y: deeperStarsTexture.size().height * i)
deeperstars.size.height = frame.height
deeperstars.run(deeperStarsbgConstantMotion)
deeperstars.zPosition = -1
addChild(deeperstars)
i += 1
}
// PLAYER
let playerTexture1 = SKTexture(imageNamed: "player-1.png")
let playerTexture2 = SKTexture(imageNamed: "player-2.png")
let playerAnimation = SKAction.animate(with: [playerTexture1, playerTexture2], timePerFrame: 0.2)
let constantAnimation = SKAction.repeatForever(playerAnimation)
sp_player = SKSpriteNode(texture: playerTexture1)
sp_player.position = CGPoint(x: frame.midX, y: (sp_player.size.height * 2))
sp_player.physicsBody = SKPhysicsBody(rectangleOf: sp_player.size)
sp_player.physicsBody!.isDynamic = false
sp_player.name = "player"
sp_player.run(constantAnimation)
addChild(sp_player)
// PLACE ALIENS
let alienTexture1 = SKTexture(imageNamed: "alien-1a.png")
let alienTexture2 = SKTexture(imageNamed: "alien-1b.png")
let alienAnimation = SKAction.animate(with: [alienTexture1, alienTexture2], timePerFrame: 0.4)
let constantAlienAnimation = SKAction.repeatForever(alienAnimation)
var x: CGFloat = 0, y: CGFloat = 0
while y < 6
{
while x < 6
{
alien = SKSpriteNode(texture: alienTexture1)
alien.position = CGPoint(x: 32 + (x * alien.size.width), y: (frame.size.height - (alien.size.height * 1.5) - (alien.size.height * y)))
print("Setting y to \(frame.size.height - (alien.size.height * y))")
alien.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: alien.size.width, height: alien.size.height))
alien.physicsBody!.isDynamic = false
alien.name = "alien"
alien.physicsBody!.contactTestBitMask = PhysicsCategory.laser
alien.run(constantAlienAnimation)
addChild(alien)
x += 1
}
y += 1
x = 0
}
print("Sprites added to scene")
spawnLasers()
}
func spawnLasers()
{
let delay1 = SKAction.wait(forDuration: 0.5)
let spawn = SKAction.run {
let laserTexture = SKTexture(imageNamed: "laser-1.png")
self.laser = SKSpriteNode(texture: laserTexture)
self.laser.position = CGPoint(x: self.sp_player.position.x, y: self.sp_player.position.y + self.sp_player.size.height)
self.laser.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: self.laser.size.width, height: self.laser.size.height))
self.laser.physicsBody!.isDynamic = true
self.laser.physicsBody!.linearDamping = 0
self.laser.physicsBody!.allowsRotation = false
self.laser.physicsBody!.contactTestBitMask = PhysicsCategory.laser
self.laser.name = "laser"
self.addChild(self.laser)
let shoot = SKAction.moveTo(y: self.frame.size.height, duration: 1)
let killLaser = SKAction.removeFromParent()
let handleLaser = SKAction.sequence([shoot,killLaser])
self.laser.run(handleLaser)
}
let action = SKAction.sequence([delay1,spawn])
let constantLasers = SKAction.repeatForever(action)
self.run(constantLasers)
}
func didBegin(_ contact: SKPhysicsContact) {
var check: UInt32 = 0
if contact.bodyA.node != nil
{
check += 1
}
if contact.bodyB.node != nil
{
check += 1
}
if check == 2
{
if contact.bodyA.node!.name == "alien" && contact.bodyB.node!.name == "laser"
{
// EXPLOSION
let explosionSplatTexture1 = SKTexture(imageNamed: "explosion-1a.png")
let explosionSplatTexture2 = SKTexture(imageNamed: "explosion-1b.png")
let explosionSplatTexture3 = SKTexture(imageNamed: "explosion-1c.png")
let explosionSplatTexture4 = SKTexture(imageNamed: "explosion-1d.png")
let explosionSplatAnimation = SKAction.animate(with: [explosionSplatTexture1, explosionSplatTexture2, explosionSplatTexture3, explosionSplatTexture4], timePerFrame: 0.1)
let killExplosion = SKAction.removeFromParent()
let explosionSequence = SKAction.sequence([explosionSplatAnimation,killExplosion])
explosionSplat1 = SKSpriteNode(texture: explosionSplatTexture1)
explosionSplat1.name = "explosion"
explosionSplat1.position = CGPoint(x: contact.bodyA.node!.position.x, y: contact.bodyA.node!.position.y)
addChild(explosionSplat1)
explosionSplat1.run(explosionSequence)
self.playerScore += 1
print("Score: \(self.playerScore!)")
contact.bodyA.node?.removeFromParent()
print("Alien named \(contact.bodyA.node?.name ?? "defaultAlienName") from scene")
contact.bodyB.node?.removeFromParent()
print("Laser named \(contact.bodyB.node?.name ?? "defaultLaserName") from scene")
}
}
}
func didEnd(_ contact: SKPhysicsContact) {
//print("Contact ended between \(contact.bodyA) and \(contact.bodyB)")
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
{
// if let touch = touches.first {
// let position = touch.location(in: view)
// storedTouch = position
// }
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?)
{
if let touch = touches.first {
let position = touch.location(in: view)
var playerpos: CGPoint!
playerpos = sp_player.position
let pl_move = SKAction.move(to: CGPoint(x: position.x, y: playerpos.y), duration: 0.1)
sp_player.run(pl_move)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?)
{
/*
Need to figure out how to use storedTouch properly
to move player relative to the screen touch co-ordinates
*/
// if let touch = touches.first {
// let position = touch.location(in: view)
// }
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
if (lastUpdateTime > 0)
{
delta = currentTime - lastUpdateTime
} else {
delta = 0
}
lastUpdateTime = currentTime
}
}
When the game runs it produces this screen:
You can see the unexpected laser behavior here:
In my diags I get the following output from the collision function:
Score: 1
Alien named defaultAlienName removed from scene
Laser named laser removed from scene
Score: 2
Alien named defaultAlienName removed from scene
Laser named laser removed from scene
Score: 3
Alien named defaultAlienName removed from scene
Laser named laser removed from scene
Score: 4
Alien named defaultAlienName removed from scene
Laser named defaultLaserName removed from scene
Score: 5
Alien named defaultAlienName removed from scene
Laser named defaultLaserName removed from scene
Score: 6
Alien named defaultAlienName removed from scene
Laser named defaultLaserName removed from scene
This is most likely my complete lack of understanding for optionals and how collision actually works. I'd be super grateful for any insights.
In your alien loop, as well as spawnLasers(), you are not giving the sprite nodes an actual PhysicsBody category. For sprites to be able to detect contact between one another, they need a category name and a contact name.
So in your while loop (building the aliens), you need to have this:
alien.physicsBody!.categoryBitMask = PhysicsCategory.alien
alien.physicsBody!.contactTestBitMask = PhysicsCategory.laser
And in spawnLasers(), you want this added:
self.laser.physicsBody!.categoryBitMask = PhysicsCategory.laser
and change the contactTestBitMask to alien, not laser:
self.laser.physicsBody!.contactTestBitMask = PhysicsCategory.alien
Hopefully you can see that the alien category wants to know when lasers touch, and the laser category wants to know when the aliens touch.
To visually help you, turn on the show physics option, this way you can see the actual physics bodies you are dealing with.
To do this, in your GameViewController (or similar), find:
showsFPS = true
showsNodeCount = true
You want to add the following:
showsPhysics = true
This will help with seeing the actual physics bodies on screen.
In:
func didBegin(_ contact: SKPhysicsContact)
you are only testing for BodyA being alien and BodyB being laser.
This is the contact test:
if contact.bodyA.node!.name == "alien" && contact.bodyB.node!.name == "laser"
I believe BodyA could be laser and BodyB be alien. Basically the physics engine contact events could be "A hitting B", or "B hitting A".
Therefore, a quick and dirty solution is to create another if statement below the current one, but changing the body names, so:
if contact.bodyA.node!.name == "laser" && contact.bodyB.node!.name == "alien" {
and duplicate the code from your existing if statement, and changing the two print statements.
This isn't the ideal way to do it, but hopefully when you tidy it up you'll get an understanding of what the physics contact is doing.
I am hoping once you have implmented the above, you will be in a much better shape.

How to make a node visible before any user interaction

I took and modified a piece of code which allows me to shoot bullets in every direction. It works perfectly. But it is generating a random bullet in the moment when player touch is ended. I have 6 different bullets and for purposes of my game I need player to see them before he will tap the screen. I tried to initialize a bullet in a global function and pass it to didMoveToView. It's not working, bullet is generating once and stays at its place.
That is my code for initializing a bullet:
class GameScene: SKScene, SKPhysicsContactDelegate {
var gameOver = false
let cannon = SKSpriteNode(imageNamed: "cannon")
let bulletInCannon = SKSpriteNode()
var endOfScreenRight = CGFloat()
var endOfScreenLeft = CGFloat()
let bmrArray = ["blBl", "magBl", "rBl"]
let cgyArray = ["cBl", "gBl", "yBl"]
let yrmArray = ["yBl", "rBl", "magBl"]
let bulletArray = ["rBullet","magBullet", "blBullet", "cBullet", "gBullet", "yBullet"]
override func didMoveToView(view: SKView) {
endOfScreenLeft = (self.size.width / 2) * CGFloat(-1)
endOfScreenRight = self.size.width / 2
cannon.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))
self.addChild(cannon)
cannon.zPosition = 0
var randomBullet = Int(arc4random_uniform(6))
bulletInCannon.texture = SKTexture(imageNamed: bulletArray[randomBullet])
bulletInCannon.position = cannon.position
addChild(bulletInCannon)
bulletInCannon.zPosition = 1000
runAction(SKAction.repeatActionForever(SKAction.sequence([SKAction.runBlock(addCgyLine),
SKAction.waitForDuration(1.0)])))
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
}
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
let bullet = SKSpriteNode(texture: bulletInCannon.texture)
bullet.position = cannon.position
addChild(bullet)
let offset = location - bullet.position
let direction = offset.normalized()
let shootAmount = direction * 1000
let realDestination = shootAmount + bullet.position
let actionMove = SKAction.moveTo(realDestination, duration: 2.0)
let actionDone = SKAction.removeFromParent()
bullet.runAction(SKAction.sequence([actionMove, actionDone]))
var randomBullet = Int(arc4random_uniform(6))
bulletInCannon.texture = SKTexture(imageNamed: bulletArray[randomBullet])
}
}
func addCgyLine () {
var cgyBlock = SKSpriteNode(imageNamed: "cyanBox")
cgyBlock.position = CGPointMake(size.width + cgyBlock.size.width/2, CGRectGetMidY(self.frame) + 60)
addChild(cgyBlock)
var randomCGY = Int(arc4random_uniform(3))
cgyBlock.texture = SKTexture(imageNamed: cgyArray[randomCGY])
let actionMove = SKAction.moveTo(CGPoint(x: -cgyBlock.size.width/2, y: CGRectGetMidY(self.frame) + 60), duration: 3)
let actionDone = SKAction.removeFromParent()
cgyBlock.runAction(SKAction.sequence([actionMove, actionDone]))
SKActionTimingMode.EaseOut
}
How can I generate a bullet and only then shoot it? I will be glad to hear any ideas, thanks!
You can generate a bullet in a cannon as a global variable, and then create a new bullet in touchesEnded which will be the bullet that actually fires from the cannon. You set that new bullet's texture to the global bullet's texture. After firing the action, give the global bullet a new texture. If you need to have a reload time, then remove the global bullet temporarily, and use a Boolean to "lock" the cannon from firing until the time has elapsed.
Here's some code that I used. If the cannon moves, then make sure to update the position of the global bullet. The subtraction of CGPoint gives me an error, but I'm using Xcode 6.4.
Edit: Quicker and easier way by giving the bulletInCannon a value
var bulletInCannon = SKSpriteNode(imageNamed: "rBullet")
override func didMoveToView(view: SKView) {
var randomBullet = Int(arc4random_uniform(6))
bulletInCannon.texture = SKTexture(imageNamed: bulletArray[randomBullet])
bulletInCannon.position = cannon.position
addChild(bulletInCannon)
}
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch in (touches as! Set<UITouch>) {
let location = touch.locationInNode(self)
// Create bullet that will fire from the cannon
let bullet = SKSpriteNode(texture: bulletInCannon.texture)
bullet.position = cannon.position
addChild(bullet)
let offset = location - bullet.position
let direction = offset.normalized()
let shootAmount = direction * 1000
let realDestination = shootAmount + bullet.position
let actionMove = SKAction.moveTo(realDestination, duration: 2.0)
let actionDone = SKAction.removeFromParent()
bullet.runAction(SKAction.sequence([actionMove, actionDone]))
// Generate a random texture for the global bullet
var randomBullet = Int(arc4random_uniform(6))
bulletInCannon.texture = SKTexture(imageNamed: bulletArray[randomBullet])
}
}

SpriteKit frame incorrect

I'm creating a simple game. I'm trying to set the frame as an physicsBody so the plane, never falls below the screen. The function below stops it from falling all the way off, but the SKSpriteNode falls just out of view before stopping.
func setBoundry(view: SKView) {
// Create ground
var boundry = SKNode()
boundry.physicsBody = SKPhysicsBody(edgeLoopFromRect: view.frame)
boundry.physicsBody?.dynamic = false
boundry.physicsBody?.contactTestBitMask = PhysicsCategory.Plane
boundry.physicsBody?.categoryBitMask = PhysicsCategory.Boundry | PhysicsCategory.Collidable
self.addChild(boundry)
}
Plane is being created like this...
func createPlane(sceneView: SKView) {
let planeTexture = SKTexture(imageNamed: "planeRed1")
let planeTexture1 = SKTexture(imageNamed: "planeRed2")
let planeTexture2 = SKTexture(imageNamed: "planeRed3")
// Animate plans propeller
let animation = SKAction.animateWithTextures([planeTexture, planeTexture1, planeTexture2], timePerFrame: 0.05)
let makePropellerSpin = SKAction.repeatActionForever(animation)
// Set planes position
plane = SKSpriteNode(texture: planeTexture)
plane.position = CGPointMake(size.width/4, size.height/2)
plane.runAction(makePropellerSpin)
plane.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(plane.size.width, plane.size.height))
plane.physicsBody?.dynamic = true
plane.physicsBody?.allowsRotation = false
plane.physicsBody?.categoryBitMask = PhysicsCategory.Plane
plane.physicsBody?.collisionBitMask = PhysicsCategory.Collidable | PhysicsCategory.Boundry
plane.physicsBody?.contactTestBitMask = PhysicsCategory.Collidable | PhysicsCategory.Boundry
// Set elevation
plane.zPosition = 5
self.addChild(plane)
}
You need to set the contactTestBitMask for your boundry node and also set the physics for your plane-node:
boundry.physicsBody?.contactTestBitMask = PhysicsCategory.YourPlayerEnum
Also check out the collisionBitMask property of the physicsbody.

SKLabel won't show up

Im making a flappy bird type game in swift using sprite kit. The game works fine but I'm trying to make a way to score the game so I set up a variable and SKLabel that adds on to itself whenever a pipe is about half way across the screen. However I cannot get the label to show up any help doing this here is my code:
// flappy rainbow sheep
//
// Created by Heather Arnold on 3/3/15.
// Copyright (c) 2015 ian arnold. All rights reserved.
//
import SpriteKit
class GameScene: SKScene {
var bird = SKSpriteNode()
var pipeUpTexture = SKTexture()
var pipeDownTexture = SKTexture()
var PipeMoveAndRemove = SKAction()
let pipeGap = 225.0
var score: Int = 0
var scoreLabel: SKLabelNode = SKLabelNode(fontNamed:"System-Bold")
override func didMoveToView(view: SKView) {
/* Setup your scene here */
score = 0
//Physics
self.physicsWorld.gravity = CGVectorMake(0.0, -10.0);
//Bird
var BirdTexture = SKTexture(imageNamed:"flappysheep")
BirdTexture.filteringMode = SKTextureFilteringMode.Nearest
bird = SKSpriteNode(texture: BirdTexture)
bird.setScale(0.5)
bird.position = CGPoint(x: self.frame.size.width * 0.35, y: self.frame.size.height * 0.6)
scoreLabel.position.x = 50
scoreLabel.position.y = view.bounds.height - 50
scoreLabel.text = "0"
scoreLabel.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.Left
scoreLabel.hidden = false
self.addChild(scoreLabel)
bird.physicsBody = SKPhysicsBody(circleOfRadius:bird.size.height/4.0);
bird.physicsBody?.dynamic = true
bird.physicsBody?.allowsRotation = false
self.addChild(bird)
//off screen
//Ground
var groundTexture = SKTexture(imageNamed:"rainbowobstacle")
var sprite = SKSpriteNode(texture: groundTexture)
sprite.setScale(2.0)
sprite.position = CGPointMake(self.size.width/2.0, sprite.size.height/2.0)
self.addChild(sprite)
var ground = SKNode()
ground.position = CGPointMake(0, groundTexture.size().height)
ground.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(self.frame.size.width, groundTexture.size().height * 2.0))
ground.physicsBody?.dynamic = false
self.addChild(ground)
//Pipes
//create the pipes
pipeUpTexture = SKTexture(imageNamed:"rainbowpipe")
pipeDownTexture = SKTexture(imageNamed:"rainbowpipe")
//move pipes
let distanceToMove = CGFloat(self.frame.size.width + 2.0 * pipeUpTexture.size().width)
let movePipes = SKAction.moveByX(-2500, y: 0.0, duration: NSTimeInterval(0.01 * distanceToMove))
let removePipes = SKAction.removeFromParent()
PipeMoveAndRemove = SKAction.sequence([movePipes, removePipes])
//spwan pipes
let spawn = SKAction.runBlock({() in self.spawnPipes()})
let delay = SKAction.waitForDuration(NSTimeInterval(0.2))
let spawnThenDelay = SKAction.sequence([spawn, delay])
let spawnThenDelayForever = SKAction.repeatActionForever(spawnThenDelay)
self.runAction(spawnThenDelayForever)
let pipeUp = SKSpriteNode(texture:pipeUpTexture)
if (pipeUp.position.x + (pipeUp.size.width / 2) < self.view!.bounds.size.width / 2)
{
score++
}
scoreLabel.hidden = false
}
func spawnPipes(){
let pipePair = SKNode()
pipePair.position = CGPointMake(self.frame.size.width + pipeUpTexture.size().width * 2, 0)
pipePair.zPosition = -10
let height = UInt32(self.frame.size.height / 4)
let y = arc4random() % height + height
let pipeDown = SKSpriteNode(texture:pipeDownTexture)
pipeDown.setScale(2.0)
pipeDown.position = CGPointMake(0.0, CGFloat(y) + pipeDown.size.height + CGFloat(pipeGap))
pipeDown.physicsBody = SKPhysicsBody(rectangleOfSize:pipeDown.size)
pipeDown.physicsBody?.dynamic = false
pipePair.addChild(pipeDown)
let pipeUp = SKSpriteNode(texture:pipeUpTexture)
pipeUp.setScale(2.0)
pipeUp.position = CGPointMake(0.0, CGFloat(y))
pipeUp.physicsBody = SKPhysicsBody(rectangleOfSize: pipeUp.size)
pipeUp.physicsBody?.dynamic = false
pipePair.addChild(pipeUp)
pipePair.runAction(PipeMoveAndRemove)
self.addChild(pipePair)
}
class GameScene: SKScene {
let bird = SKSpriteNode()
var gameOver = false
override init(size: CGSize) {
super.init(size: size)
self.bird.position = CGPoint(x: 0, y: 0)
self.addChild(self.bird)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func endGame(){
// restart the game
let gameScene = GameScene(size: self.size)
self.view!.presentScene(gameScene)
}
override func update(currentTime: NSTimeInterval) {
// our bird doesnt intersect the frame,
// we use gameOver bool so we dont call endGame more than once
if !self.frame.intersects(self.bird.frame) && !self.gameOver{
self.gameOver = true
self.endGame()
}
}
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
bird.physicsBody?.velocity = CGVectorMake(0, 0)
bird.physicsBody?.applyImpulse(CGVectorMake(0, 8))
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
Your label might be covered over by another image. Try setting the SKLabelNode zPosition property to a higher value. Perhaps something like 900.
Try setting the zPosition of the label to one.
scoreLabel.zPosition = 1
This should make the node appear even if another node passes over it.
Here are two other functions that may assist you also:
scoreLabel.color = UIColor.blackColor()//set color to black - you can use any almost color here
scoreLabel.fontSize = 32//changes font size to make label bigger/smaller

Collision Detection In Sprite Kit using Swift

The code below is from a project I am currently working on, I have been trying to teach myself Swift language and Sprite Kit for the past few days and this is my first attempt at a game, it is a Flappy Bird type game. I ran into a problem today when I was trying to write the code for the collision detection. When the bird touches one of the pipes the game is supposed to pause. However when I run the code and the bird touches the pipe, nothing happens, the bird just bounces off of it. I have read many tutorials and watched many videos on this subject to try and resolve my problem and haven't had any luck. I have written all of the collision detection code that I learned off of the last video I watched in the code below. Could anyone please tell me what I am doing wrong. Any advice would be greatly appreciated, thank you.
//
// GameScene.swift
// Bird Flappy Game
//
// Created by Brandon Ballard on 1/4/15.
// Copyright (c) 2015 Brandon Ballard. All rights reserved.
//
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var bird = SKSpriteNode()
var pipeUpTexture = SKTexture()
var pipeDownTexture = SKTexture()
var pipesMoveAndRemove = SKAction()
let pipeGap = 150.0
enum ColliderType:UInt32 {
case BIRD = 1
case PIPE = 2
}
override func didMoveToView(view: SKView) {
/* Setup your scene here */
backgroundColor = SKColor.cyanColor()
//physics
self.physicsWorld.gravity = CGVectorMake(0.0, -15.0);
self.physicsWorld.contactDelegate = self
func didBeginContact(contact: SKPhysicsContactDelegate) {
scene?.view?.paused = true
bird.setScale(12.0)
}
//Bird
var birdTexture = SKTexture(imageNamed:"Bird")
birdTexture.filteringMode = SKTextureFilteringMode.Nearest
bird = SKSpriteNode(texture: birdTexture)
bird.setScale(0.6)
bird.position = CGPoint(x: 375, y: self.frame.size.height * 0.6)
bird.physicsBody = SKPhysicsBody(circleOfRadius: bird.size.height / 2.0)
bird.physicsBody?.dynamic = true
bird.physicsBody?.allowsRotation = true
bird.physicsBody?.affectedByGravity = true
bird.physicsBody!.categoryBitMask = ColliderType.BIRD.rawValue
bird.physicsBody!.contactTestBitMask = ColliderType.PIPE.rawValue
bird.physicsBody!.collisionBitMask = ColliderType.PIPE.rawValue
self.addChild(bird)
//Ground
var groundTexture = SKTexture(imageNamed: "Ground")
var sprite = SKSpriteNode(texture: groundTexture)
sprite.setScale(2.0)
sprite.position = CGPointMake(self.size.width / 2, sprite.size.height / 2.0)
self.addChild(sprite)
var ground = SKNode()
ground.position = CGPointMake(0, groundTexture.size().height + 0)
ground.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(self.frame.size.width, groundTexture.size().height * 2.0))
ground.physicsBody?.dynamic = false
self.addChild(ground)
//Pipes
//Create the Pipes
pipeUpTexture = SKTexture(imageNamed: "PipeUp")
pipeDownTexture = SKTexture(imageNamed: "PipeDown")
//Movement of Pipes
let distanceToMove = CGFloat(self.frame.size.width + 2.0 * pipeUpTexture.size().width)
let movePipes = SKAction.moveByX(-distanceToMove, y: 0.0, duration: NSTimeInterval(0.01 * distanceToMove))
let removePipes = SKAction.removeFromParent()
pipesMoveAndRemove = SKAction.sequence([movePipes,removePipes])
//Spawn Pipes
let spawn = SKAction.runBlock({() in self.spawnPipes()})
let delay = SKAction.waitForDuration(NSTimeInterval(2.0))
let spawnThenDelay = SKAction.sequence([spawn,delay])
let spawnThenDelayForever = SKAction.repeatActionForever(spawnThenDelay)
self.runAction(spawnThenDelayForever)
}
func spawnPipes() {
let pipePair = SKNode()
pipePair.position = CGPointMake(self.frame.size.width + pipeUpTexture.size().width * 2, 0)
pipePair.zPosition = -10
let height = UInt32(self.frame.size.height / 4)
let y = arc4random() % height + height
var pipeDown = SKSpriteNode(texture: pipeDownTexture)
pipeDown.setScale(2.0)////////
pipeDown.position = CGPointMake(3.0, CGFloat(y) + pipeDown.size.height + CGFloat(pipeGap) )
pipeDown.physicsBody = SKPhysicsBody(rectangleOfSize: pipeDown.size)
pipeDown.physicsBody?.dynamic = false
pipeDown.physicsBody!.affectedByGravity = false
pipeDown.physicsBody!.categoryBitMask = ColliderType.PIPE.rawValue
pipeDown.physicsBody!.contactTestBitMask = ColliderType.BIRD.rawValue
pipeDown.physicsBody!.collisionBitMask = ColliderType.BIRD.rawValue
pipePair.addChild(pipeDown)
var pipeUp = SKSpriteNode(texture: pipeUpTexture)
pipeUp.setScale(2.0)
pipeUp.position = CGPointMake(0.0, CGFloat(y))
pipeUp.physicsBody = SKPhysicsBody(rectangleOfSize: pipeUp.size )
pipeUp.physicsBody?.dynamic = false
pipeUp.physicsBody!.affectedByGravity = false
pipeUp.physicsBody!.categoryBitMask = ColliderType.PIPE.rawValue
pipeUp.physicsBody!.contactTestBitMask = ColliderType.BIRD.rawValue
pipeUp.physicsBody!.collisionBitMask = ColliderType.BIRD.rawValue
pipePair.addChild(pipeUp)
pipePair.runAction(pipesMoveAndRemove)
self.addChild(pipePair)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
bird.physicsBody?.velocity = CGVectorMake( 0, 0 )
bird.physicsBody?.applyImpulse(CGVectorMake(0,25))
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
From what I can see, you only need to DETECT collisions, not actually simulate them. For this, you need to set only the contactTestBitMask of the physicsBodies. You can set the collisionBitMask as 0.
bird.physicsBody!.collisionBitMask = 0
pipe.physicsBody!.collisionBitMask = 0
Also, as hamobi has already said, the didBeginContact method needs to be outside the didMoveToView method with the override keyword. (This question has the exact same problem as yours)
class GameScene: SKScene, SKPhysicsContactDelegate {
// ...
override func didMoveToView(view: SKView) {
self.physicsWorld.gravity = CGVector(dx: 0, dy: 0)
// set as delegate:
self.physicsWorld.contactDelegate = self
// ..
}
// should be called now
func didBeginContact(contact: SKPhysicsContact){
scene?.view?.paused = true
bird.setScale(12.0)
}
}
You put your didBeginContact INSIDE of didMoveToView. It's not callable from there. Put it in the body of your class