I am working on my first swift iOS sprite kit project but I've run into a problem that I just can't find a solution to. My goal is to have a ball land on a moving platform and the ball and platform both stop upon contact.
The issue I am running into is that there is consistently a gap between the platform and ball when the code recognizes they are in contact and makes them stop. See the attached photo:
Image Here
Ball:
//Ball Node
func createPlayer() -> SKNode {
let playerNode = SKNode()
playerNode.position = CGPoint(x: self.size.width / 2, y: self.size.height * 0.25)
let sprite = SKSpriteNode(imageNamed: "ball")
sprite.setScale(scaleFactor)
playerNode.addChild(sprite)
playerNode.physicsBody = SKPhysicsBody(circleOfRadius: sprite.size.width / 2)
playerNode.physicsBody?.dynamic = false
playerNode.physicsBody?.allowsRotation = false
playerNode.physicsBody?.restitution = 0.0
playerNode.physicsBody?.friction = 0.0
playerNode.physicsBody?.angularDamping = 0.0
playerNode.physicsBody?.linearDamping = 0.0
playerNode.physicsBody?.usesPreciseCollisionDetection = true
playerNode.physicsBody?.categoryBitMask = CollisionCategoryBitmask.Player
playerNode.physicsBody?.collisionBitMask = 0
playerNode.physicsBody?.contactTestBitMask = CollisionCategoryBitmask.Platform | CollisionCategoryBitmask.Vortex
return playerNode
Platform:
//Platform
sprite = SKSpriteNode(imageNamed: "platform")
let node = PlatformNode()
let thePosition = CGPoint(x: position.x * scaleFactor - (sprite.size.width / 1.999), y: position.y + (self.size.height * 0.50))
sprite.setScale(scaleFactor)
node.addChild(sprite)
node.physicsBody?.usesPreciseCollisionDetection = true
node.physicsBody = SKPhysicsBody(rectangleOfSize: sprite.size)
node.physicsBody?.dynamic = false
node.physicsBody?.categoryBitMask = CollisionCategoryBitmask.Platform
node.physicsBody?.collisionBitMask = 0
return node
didBeginContact:
func didBeginContact(contact: SKPhysicsContact) {
var updateHUD = true
maxPlayerY = Int(player.position.y)
let whichNode = (contact.bodyA.node != player) ? contact.bodyA.node : contact.bodyB.node
//If the ball is falling
if player.physicsBody?.velocity.dy < 0 {
let other = whichNode as! GameObjectNode
//If the player hits the Platform
if player.physicsBody?.velocity.dy < 0 && whichNode?.name == "NODE_PLATFORM" {
let joint = SKPhysicsJointFixed.jointWithBodyA(player.physicsBody, bodyB:whichNode!.physicsBody, anchor:CGPoint(x: player.position.x, y: ballBottom))
let moveScreen = SKAction.moveToY(-(player.position.y - 200), duration: 0.5)
let moveVortex = SKAction.moveToY((player.position.y - scaleFactor*300), duration: 0.5)
physicsWorld.addJoint(joint
player.physicsBody?.velocity = CGVector(dx: player.physicsBody!.velocity.dx, dy: 0)
player.physicsBody?.affectedByGravity = false
}
}
}
I suspect you can resolve this issue by waiting until after the physics simulation has completed (for the current frame) before connecting the objects. One way to do that is to a set a flag when the ball is in contact with the platform in didBeginContact and then connect the two bodies with a joint in didSimulatePhysics.
Related
I had just recently set up collision detection in my game it is doesn't seem to be working. I am very new to spritekit and i would like to know what is happening. Below i will provide you the gamscene class, didMoveToView, a function that is spawning the balls in the view, and my Did begin contact function. Please note that i am working with just the red Ball and the red block right now so they are the only two colliding. Please tell me if theres anything else in the project that you need to see and is not provided below.
import SpriteKit
import GameplayKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var greenBlock = SKSpriteNode ()
var redBlock = SKSpriteNode()
var yellowBlock = SKSpriteNode()
var blueBlock = SKSpriteNode()
var greenBall = SKSpriteNode()
var redBall = SKSpriteNode()
var yellowBall = SKSpriteNode()
var blueBall = SKSpriteNode()
var ball = SKSpriteNode()
var ballSpeedTop = CGVector(dx: 0, dy: 5)
var ballSpeedBottom = CGVector(dx: 0, dy: -5)
This is where i define the numbers for the categoryBitMask
let redBallCategory: UInt32 = 0x1 << 0
let greenBallCategory: UInt32 = 0x1 << 1
let blueBallCategory: UInt32 = 0x1 << 2
let yellowBallCategory: UInt32 = 0x1 << 3
let redBlockCategory: UInt32 = 0x1 << 4
let yellowBlockCategory: UInt32 = 0x1 << 5
let greenBlockCategory: UInt32 = 0x1 << 6
let blueBlockCategory: UInt32 = 0x1 << 7
override func didMove(to view: SKView) {
physicsWorld.contactDelegate = self
Where i am applying the physics bodies to the block.
redBlock = self.childNode(withName: "redBlock") as! SKSpriteNode
redBlock.physicsBody?.categoryBitMask = redBlockCategory
redBlock.physicsBody?.collisionBitMask = redBallCategory
redBlock.physicsBody?.contactTestBitMask = redBallCategory
yellowBlock = self.childNode(withName: "yellowBlock") as! SKSpriteNode
greenBlock = self.childNode(withName: "greenBlock") as! SKSpriteNode
blueBlock = self.childNode(withName: "blueBlock") as! SKSpriteNode
redBlock.physicsBody?.affectedByGravity = false
redBlock.physicsBody?.isDynamic = true
greenBlock.physicsBody?.affectedByGravity = false
greenBlock.physicsBody?.isDynamic = true
blueBlock.physicsBody?.affectedByGravity = false
blueBlock.physicsBody?.isDynamic = false
yellowBlock.physicsBody?.affectedByGravity = false
yellowBlock.physicsBody?.isDynamic = false
initializeGame()
print("hello")
}
func initializeGame (){
Timer.scheduledTimer(timeInterval: TimeInterval(randomBetweenNumbers(firstNum: 2, secondNum: 4)), target: self, selector: #selector(topBall), userInfo: nil, repeats: true)
Timer.scheduledTimer(timeInterval: TimeInterval(randomBetweenNumbers(firstNum: 2, secondNum: 4)), target: self, selector: #selector(bottomBall), userInfo: nil, repeats: true)
}
// top ball spawner
func topBall () -> SKSpriteNode {
let ball: SKSpriteNode?;
if Int(randomBetweenNumbers(firstNum: 0, secondNum: 2)) == 0 {
ball = SKSpriteNode(imageNamed: "redBall")
ball?.name = "redBall"
ball!.physicsBody?.categoryBitMask = redBallCategory
ball!.physicsBody?.collisionBitMask = redBlockCategory
ball!.physicsBody?.contactTestBitMask = redBlockCategory
} else {
ball = SKSpriteNode(imageNamed: "greenBall")
ball?.name = "greenBall"
}
ball!.size = CGSize(width: 30, height: 30)
ball!.anchorPoint = CGPoint(x: 0.5, y: 0.5)
ball!.position = CGPoint(x: 0, y: 0)
ball!.physicsBody = SKPhysicsBody(circleOfRadius: ball!.size.height / 2)
ball!.physicsBody?.affectedByGravity = false
ball!.physicsBody?.isDynamic = true
ball!.zPosition = 2
self.addChild(ball!)
ball!.physicsBody?.applyImpulse(ballSpeedTop)
return ball!
}
the contact function
func didBegin(_ contact: SKPhysicsContact) {
if (contact.bodyA.categoryBitMask == redBallCategory && contact.bodyB.categoryBitMask == redBlockCategory){
print("touched red block")
} else if (contact.bodyB.categoryBitMask == redBallCategory && contact.bodyA.categoryBitMask == redBlockCategory){
print("touched a red block")
}
}
UPDATE::: i found where i went wrong but my problem is not technically solved yet. i had applied the properties of the redBall outside the if statement which meant that the redballs category bit mask, etc. is applied to both balls, i need a way to apply the redBall category bit mask to the redBall when it spawns and a different categorybitmask to the green ball when it spawns.
func topBall () -> SKSpriteNode {
let ball: SKSpriteNode?;
if Int(randomBetweenNumbers(firstNum: 0, secondNum: 2)) == 0 {
ball = SKSpriteNode(imageNamed: "redBall")
ball?.name = "redBall"
} else {
ball = SKSpriteNode(imageNamed: "greenBall")
ball?.name = "greenBall"
}
ball!.size = CGSize(width: 30, height: 30)
ball!.anchorPoint = CGPoint(x: 0.5, y: 0.5)
ball!.position = CGPoint(x: 0, y: 0)
ball!.physicsBody = SKPhysicsBody(circleOfRadius: ball!.size.height / 2)
ball!.physicsBody?.affectedByGravity = false
ball!.physicsBody?.isDynamic = true
ball!.physicsBody?.categoryBitMask = redBallCategory
ball!.physicsBody?.collisionBitMask = redBlockCategory
ball!.physicsBody?.contactTestBitMask = redBlockCategory
ball!.zPosition = 2
self.addChild(ball!)
ball!.physicsBody?.applyImpulse(ballSpeedTop)
return ball!
}
this is how i got it to work but the problem is that i want to apply individual categorybitmasks to the ball depending on wether it is green or red.
Use temporary variables to store the appropriate category, collision and contactTest bitmasks in the same block of code where you set the spawned ball's image and name, and then use these temporary variables to set the actual values in the physicsbody:
var categoryBitMask: UInt32
var collisonBitMask: UInt32
var contactTestBitMask: Uint32
if Int(randomBetweenNumbers(firstNum: 0, secondNum: 2)) == 0 {
ball = SKSpriteNode(imageNamed: "redBall")
ball?.name = "redBall"
categoryBitMask = redBallCategory
collisionBitMask = redBlockCategory
contactTestBitMask = redBlockCategory
} else {
ball = SKSpriteNode(imageNamed: "greenBall")
ball?.name = "greenBall"
categoryBitMask = greenBallCategory
collisionBitMask = greenBlockCategory
contactTestBitMask = greenBlockCategory
}
ball!.size = CGSize(width: 30, height: 30)
ball!.anchorPoint = CGPoint(x: 0.5, y: 0.5)
ball!.position = CGPoint(x: 0, y: 0)
ball!.physicsBody = SKPhysicsBody(circleOfRadius: ball!.size.height / 2)
ball!.physicsBody?.affectedByGravity = false
ball!.physicsBody?.isDynamic = true
ball!.physicsBody?.categoryBitMask = categoryBitMask
ball!.physicsBody?.collisionBitMask = collisionBitMask
ball!.physicsBody?.contactTestBitMask = contactTestBitMask
I'm having a play with game programming in Swift/Xcode and am struggling getting collision to work.
I'm using Xcode 8 Beta 3 (because I couldn't use Xcode 7 on MacOS Sierra) so its using the new Swift 3.0 Syntax. The game has a little puppy (defined in a SK Scene file) that jumps and collects thrown items from the right. The thrown item is created in a function and removed when a new item is thrown.
I need contact with the thrown item comes into contact with the puppy. I've got physics turned on and the item bounces off the puppy but no contact is registered.
Heres my code. Appologies for the mess of code but its very first attempt at Swift and everything just thrown in for now. I can't see what I'm doing wrong :(
UPDATE: As cocoajoe suggested, I've tried setting the masks to 1 and have simplified the code to remove references to the gamescene.sks file and created everything programmatically but its still not working. I've updated the code below to reflect the latest version of the code...
import SpriteKit
import AVFoundation
class GameScene: SKScene, SKPhysicsContactDelegate {
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder) is not used in this app")
}
override init(size: CGSize) {
super.init(size: size)
anchorPoint = CGPoint(x: 0.0, y: 0.0)
}
var isJumping = false
var gamePaused = false
var gameRunning = false
var playerScore = 0
var lastSpawnTime = 0
var lastTick = 0
var spawnDelay = 4
let PuppyCategory : UInt32 = 0x1 << 0
let ThrowableCategory : UInt32 = 0x1 << 1
let GroundCategory : UInt32 = 0x1 << 2
override func didMove(to view: SKView) {
physicsWorld.gravity = CGVector(dx: 0.0, dy: -9.8)
physicsWorld.contactDelegate = self
let background = SKSpriteNode(imageNamed: "bg")
background.anchorPoint = CGPoint(x: 0.0, y:0.0)
background.size = size
addChild(background)
// Set up Ground Object
let Ground = SKSpriteNode()
Ground.name = "Ground"
Ground.size = CGSize(width:frame.width, height: frame.height / 10)
Ground.position = CGPoint(x: frame.midX, y: Ground.frame.height / 2 )
Ground.zPosition = -20
Ground.color = SKColor.white()
// Set up the Physics
Ground.physicsBody = SKPhysicsBody(rectangleOf: Ground.size)
Ground.physicsBody!.categoryBitMask = GroundCategory
Ground.physicsBody!.contactTestBitMask = PuppyCategory | ThrowableCategory
Ground.physicsBody!.collisionBitMask = PuppyCategory | ThrowableCategory
Ground.physicsBody?.affectedByGravity = false
Ground.physicsBody?.allowsRotation = false
Ground.physicsBody?.isDynamic = false
Ground.physicsBody?.mass = 1.99999
// Initialise the Node
addChild(Ground)
// Create a Puppy
let Puppy = SKSpriteNode(imageNamed: "puppy1")
Puppy.name = "Puppy"
Puppy.position = CGPoint(x: frame.width / 10, y: frame.height / 2)
Puppy.size = CGSize(width: frame.width / 7, height: frame.height / 5)
Puppy.zPosition = 3
// Set up the Physics
Puppy.physicsBody = SKPhysicsBody(rectangleOf: Puppy.size)
Puppy.physicsBody!.categoryBitMask = PuppyCategory
Puppy.physicsBody!.contactTestBitMask = ThrowableCategory | GroundCategory
Puppy.physicsBody!.collisionBitMask = ThrowableCategory | GroundCategory
Puppy.physicsBody?.affectedByGravity = true
Puppy.physicsBody?.allowsRotation = false
Puppy.physicsBody?.isDynamic = true
Puppy.physicsBody?.mass = 1.99999
// Set up the Textures/Animation Frames
var TextureAtlas = SKTextureAtlas()
var TextureArray = [SKTexture]()
TextureAtlas = SKTextureAtlas(named: "puppy")
for i in 1...TextureAtlas.textureNames.count {
let Name = "puppy\(i).png"
TextureArray.append(SKTexture(imageNamed: Name))
}
Puppy.run(SKAction.repeatForever(SKAction.animate(with:TextureArray, timePerFrame: 0.1)))
// Add the Sprite
addChild(Puppy)
let gameMessage = SKSpriteNode(imageNamed: "TapToPlay")
gameMessage.name = "GameMessage"
gameMessage.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
gameMessage.zPosition = 30
gameMessage.setScale(0.0)
addChild(gameMessage)
let gameLogo = SKSpriteNode(imageNamed: "pjlogo")
gameLogo.name = "GameLogo"
gameLogo.size = CGSize(width: frame.width / 3, height: frame.height / 5)
gameLogo.position = CGPoint(x: frame.midX, y: (frame.maxY / 100) * 80)
gameLogo.zPosition = 30
addChild(gameLogo)
let scale = SKAction.scale(to: 1.0, duration: 1)
scene?.childNode(withName: "GameMessage")!.run(scale)
if let musicURL = Bundle.main.urlForResource("bgmusic", withExtension: "mp3") {
var backgroundMusic: SKAudioNode!
backgroundMusic = SKAudioNode(url: musicURL)
addChild(backgroundMusic)
}
startNewGame()
}
func puppyJump() {
if gameRunning {
let Puppy = childNode(withName: "Puppy")
if !isJumping {
Puppy?.physicsBody?.applyImpulse(CGVector(dx: 0,dy: 1400))
isJumping = true
}
}
}
func generateThrowable() {
print("new enemy")
let oldItem = childNode(withName: "throwableItem")
oldItem?.removeFromParent()
let throwableItem = SKSpriteNode(imageNamed: "puppy1")
throwableItem.name = "throwableItem"
throwableItem.position = CGPoint(x: frame.maxX, y: frame.midY)
throwableItem.zPosition = 3
throwableItem.setScale(0.1)
throwableItem.physicsBody = SKPhysicsBody(rectangleOf: throwableItem.size)
throwableItem.physicsBody!.categoryBitMask = ThrowableCategory
throwableItem.physicsBody!.contactTestBitMask = PuppyCategory | GroundCategory
throwableItem.physicsBody!.collisionBitMask = PuppyCategory | GroundCategory
throwableItem.physicsBody?.friction = 0.1
throwableItem.physicsBody?.restitution = 0.1
throwableItem.physicsBody?.mass = 0.01
throwableItem.physicsBody?.affectedByGravity = true
throwableItem.physicsBody?.allowsRotation = true
throwableItem.physicsBody?.isDynamic = true
addChild(throwableItem)
throwableItem.physicsBody?.applyImpulse(CGVector(dx: -7,dy: 4))
let throwableTrail = SKEmitterNode(fileNamed: "particleTrail.sks")
throwableTrail?.name = "throwableTrail"
throwableTrail?.targetNode = scene
throwableItem.addChild(throwableTrail!)
}
func startNewGame() {
let logo = childNode(withName: "GameLogo")
let gameMessage = childNode(withName: "GameMessage")
logo?.removeFromParent()
gameMessage?.removeFromParent()
let gameScore = SKLabelNode(fontNamed: "Arial")
gameScore.name = "GameScore"
gameScore.text = "Score:0"
gameScore.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.right
gameScore.fontSize = 20
gameScore.position = CGPoint(x: frame.maxX - 20, y: frame.maxY - 26)
gameScore.zPosition = 30
addChild(gameScore)
let pauseButton = SKLabelNode(fontNamed: "Arial")
pauseButton.name = "pauseButton"
pauseButton.text = "PAUSE"
pauseButton.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.left
pauseButton.fontSize = 20
pauseButton.position = CGPoint(x: 20, y: frame.maxY - 26)
pauseButton.zPosition = 30
addChild(pauseButton)
gameRunning = true
}
func didBeginContact(contact: SKPhysicsContact) {
print("contact")
var firstBody: SKPhysicsBody
var secondBody: SKPhysicsBody
// 2
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
firstBody = contact.bodyA
secondBody = contact.bodyB
} else {
firstBody = contact.bodyB
secondBody = contact.bodyA
}
// 3
if firstBody.categoryBitMask == PuppyCategory && secondBody.categoryBitMask == GroundCategory {
print("Hit bottom. First contact has been made.")
}
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
if gameRunning {
let gameScore = childNode(withName: "GameScore") as? SKLabelNode
gameScore?.text = "Score:\(playerScore)"
let currentTick:Int = Int(ceil(currentTime))
if lastTick < currentTick {
lastTick = currentTick
}
if (lastSpawnTime + spawnDelay) < currentTick {
// Time to Spawn new Bad Guy
generateThrowable()
lastSpawnTime = currentTick
}
}
}
}
I haven't checked other parts. (Your code still needs plenty of resources...)
But this line:
func didBeginContact(contact: SKPhysicsContact) {
needs to be changed as:
func didBegin(_ contact: SKPhysicsContact) {
in Swift 3. (Which is suggested by Xcode, with just typing didBegin.)
Please try.
I'm creating a game where a player will control a falling sprite with the accelerometer X axis and an impulse is added when the user taps on the screen. In my current code when I run the game, it works okay and then once the player dies and tries again the sprite goes crazy and just flies of the screen. Can someone help please...
class PlayScene: SKScene, SKPhysicsContactDelegate {
var score = 0
var scoreLabel = SKLabelNode()
var gameOverLabel = SKLabelNode()
var labelHolder = SKSpriteNode()
var background = SKSpriteNode()
let playerGroup:UInt32 = 1
let objectGroup:UInt32 = 2
let gapGroup:UInt32 = 0 << 3
let boundary:UInt32 = 4
var movingObjects = SKNode()
var motionManager = CMMotionManager()
var destX:CGFloat = 0.0
var gameOver = 0
var currentX: CGFloat?
let greenPlayer = SKSpriteNode(imageNamed: "Image/Players/greenPlayer.png")
override func didMoveToView(view: SKView) {
self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
self.physicsWorld.contactDelegate = self
self.physicsBody!.friction = 0
self.physicsWorld.gravity = CGVectorMake(-0.0, -0.8)
self.addChild(labelHolder)
scoreLabel.fontName = "Helvetica"
scoreLabel.fontSize = 60
scoreLabel.text = "0"
scoreLabel.position = CGPointMake(CGRectGetMidX(self.frame), self.frame.size.height - 140)
scoreLabel.zPosition = 10
self.addChild(scoreLabel)
//self.physicsWorld.contactDelegate = self
self.addChild(movingObjects)
let backgroundTexture = SKTexture(imageNamed: "Image/TempBG.png")
background=SKSpriteNode(texture: backgroundTexture)
background.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame))
background.size.width = self.frame.width
background.zPosition = 0
self.addChild(background)
greenPlayer.physicsBody = SKPhysicsBody(circleOfRadius: greenPlayer.size.height/2)
greenPlayer.physicsBody?.dynamic = true
greenPlayer.physicsBody?.allowsRotation = false
greenPlayer.physicsBody?.categoryBitMask = playerGroup
greenPlayer.physicsBody?.collisionBitMask = objectGroup
greenPlayer.physicsBody?.contactTestBitMask = objectGroup
greenPlayer.physicsBody?.collisionBitMask = gapGroup
greenPlayer.physicsBody?.collisionBitMask = boundary
//self.greenPlayer.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
self.greenPlayer.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame))
greenPlayer.zPosition = 5
self.addChild(greenPlayer)
currentX = self.greenPlayer.position.x
//if motionManager.accelerometerAvailable == true {
motionManager.startAccelerometerUpdatesToQueue(NSOperationQueue.currentQueue()!, withHandler:{ data, error in
if data!.acceleration.x < 0 {
self.destX = self.currentX! + CGFloat(data!.acceleration.x * 1000)
}
else if data!.acceleration.x > 0 {
self.destX = self.currentX! + CGFloat(data!.acceleration.x * 1000)
}
})
NSTimer.scheduledTimerWithTimeInterval(2, target: self, selector: Selector("makeDodges"), userInfo: nil, repeats: true)
}
func makeDodges() {
if(gameOver == 0){
let gapWidth = greenPlayer.size.width * 3
let movementAmount = arc4random() % UInt32(self.frame.size.width / 2)
let dodgeOffSet = CGFloat(movementAmount) - (self.frame.size.width / 4)
let moveDodges = SKAction.moveByX(0, y: self.frame.size.height * 2, duration: NSTimeInterval(self.frame.size.height / 100))
let removeDodges = SKAction.removeFromParent()
let moveAndRemoveDodges = SKAction.sequence([moveDodges, removeDodges])
let dodge1Texture = SKTexture(imageNamed: "Image/Dodge.png")
let dodge1 = SKSpriteNode(texture: dodge1Texture)
dodge1.position = CGPoint(x: CGRectGetMidX(self.frame) + dodge1.size.width/2 + gapWidth/2 + dodgeOffSet, y: CGRectGetMidY(self.frame) - self.frame.size.height)
dodge1.runAction(moveDodges)
dodge1.physicsBody = SKPhysicsBody(rectangleOfSize: dodge1.size)
dodge1.physicsBody?.dynamic = false
dodge1.physicsBody?.categoryBitMask = objectGroup
dodge1.zPosition = 10
movingObjects.addChild(dodge1)
let dodge2Texture = SKTexture(imageNamed: "Image/Dodge.png")
let dodge2 = SKSpriteNode(texture: dodge2Texture)
dodge2.position = CGPoint(x: CGRectGetMidX(self.frame) - dodge2.size.width/2 - gapWidth/2 + dodgeOffSet , y: CGRectGetMidY(self.frame) - self.frame.size.height)
dodge2.runAction(moveDodges)
dodge2.physicsBody = SKPhysicsBody(rectangleOfSize: dodge2.size)
dodge2.physicsBody?.dynamic = false
dodge2.physicsBody?.categoryBitMask = objectGroup
dodge2.zPosition = 10
movingObjects.addChild(dodge2)
let gap = SKNode()
gap.position = CGPoint(x: CGRectGetMidX(self.frame) + dodgeOffSet , y: CGRectGetMidY(self.frame) - self.frame.size.height)
gap.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(dodge1.size.height, gapWidth))
gap.runAction(moveAndRemoveDodges)
gap.physicsBody?.dynamic = false
gap.physicsBody?.collisionBitMask = gapGroup
gap.physicsBody?.categoryBitMask = gapGroup
gap.physicsBody?.contactTestBitMask = playerGroup
movingObjects.addChild(gap)
}
}
func didBeginContact(contact: SKPhysicsContact) {
if contact.bodyA.categoryBitMask == gapGroup || contact.bodyB.categoryBitMask == gapGroup {
score++
print(score)
scoreLabel.text = "\(score)"
} else {
if gameOver == 0 {
gameOver = 1
movingObjects.speed = 0
let scene:SKScene = GameScene(size: self.frame.size)
scene.scaleMode = .AspectFill
let transition:SKTransition = SKTransition.fadeWithDuration(2)
self.view?.presentScene(scene, transition: transition)
}
}
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
if(gameOver == 0) {
greenPlayer.physicsBody?.velocity = CGVectorMake(0, 0)
greenPlayer.physicsBody?.applyImpulse(CGVectorMake(0, 50))
} else {
score = 0
scoreLabel.text = "0"
movingObjects.removeAllChildren()
self.greenPlayer.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame))
greenPlayer.physicsBody?.velocity = CGVectorMake(0, 0)
labelHolder.removeAllChildren()
gameOver = 0
movingObjects.speed = 1
currentX = CGRectGetMidX(self.frame)
}
}
override func update(currentTime: CFTimeInterval) {
let action = SKAction.moveToX(destX, duration: 1)
self.greenPlayer.runAction(action)
}
}
You never stop and remove the old motion manager queue, so your old scene will never die, thus causing your problems.
Add motionManager.stopAccelerometerUpdates();motionManager = nil; at the point you want to destroy your old scene, If you are not destroying your old scene, and just resetting your data, then just add motionManager.stopAccelerometerUpdates(); to the point where you are going to reset your scene.
I have been working on a Flappy Bird type game for a couple of days now in Sprite Kit. I am new to programming so I have been trying to teach myself to make the game through various online videos and tutorials. I have come to the point where I need to create a scoring system that will display the current score on the screen in real time. I have looked and looked for a good tutorial on this but have had no luck finding one. I have a score label set up already in the code below. I have included below my entire scene. All I need to know is how to update the score label text each time the bird flies through a pipe. Any advice or suggestions would be greatly appreciated, thank you.
//
// ArcheryScene.swift
// FlappyBird (swift)
//
// Created by Brandon Ballard on 1/6/15.
// Copyright (c) 2015 Brandon Ballard. All rights reserved.
//
import UIKit
import SpriteKit
class ArcheryScene: SKScene, SKPhysicsContactDelegate {
var bird = SKSpriteNode()
var pipeUpTexture = SKTexture()
var pipeDownTexture = SKTexture()
var pipesMoveAndRemove = SKAction()
var impulse = 1
var count = 0
var scoreLabel = SKLabelNode()
let scoreLabelName = "scoreLabel"
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
//Bird
var birdTexture = SKTexture(imageNamed:"Bird")
birdTexture.filteringMode = SKTextureFilteringMode.Nearest
bird = SKSpriteNode(texture: birdTexture)
bird.setScale(0.6)
bird.position = CGPoint(x: self.frame.width * 0.35 + 20, y: self.frame.size.height * 0.95)
bird.physicsBody = SKPhysicsBody(circleOfRadius: bird.size.height / 2.0)
bird.physicsBody?.dynamic = true
bird.physicsBody?.allowsRotation = true
bird.physicsBody?.affectedByGravity = true
bird.physicsBody!.collisionBitMask = ColliderType.BIRD.rawValue
bird.physicsBody!.contactTestBitMask = 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)
//Score Label
scoreLabel = SKLabelNode(fontNamed: "ScoreLabel")
scoreLabel.name = scoreLabelName
scoreLabel.fontSize = 125
scoreLabel.fontColor = SKColor.whiteColor()
scoreLabel.text = "\(count)"
println(size.height)
scoreLabel.position = CGPointMake(frame.size.width / 2, frame.size.height / 14)
self.addChild(scoreLabel)
//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!.collisionBitMask = ColliderType.PIPE.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!.collisionBitMask = ColliderType.PIPE.rawValue
pipePair.addChild(pipeUp)
pipePair.runAction(pipesMoveAndRemove)
self.addChild(pipePair)
}
func didBeginContact(contact: SKPhysicsContactDelegate) {
impulse = 0
let fadeOut = SKAction.sequence([SKAction.waitForDuration(3.0)])
let welcomeReturn = SKAction.runBlock({
let Transition = SKTransition.revealWithDirection(SKTransitionDirection.Down, duration: 1.0)
let welcomeScene = GameScene(fileNamed: "GameScene")
welcomeScene.scaleMode = .AspectFill
self.scene!.view?.presentScene(welcomeScene, transition: Transition)
})
let sequence = SKAction.sequence([fadeOut, welcomeReturn])
self.runAction(sequence)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if bird.position.y > (self.frame.size.height * 0.999){
impulse = 0
}
if impulse == 1 {
bird.physicsBody?.velocity = CGVectorMake( 0, 0 )
bird.physicsBody?.applyImpulse(CGVectorMake(0,25))
}
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
Just set up an invisible sprite between your pipes. when your bird passes through it, you can detect that and increase your score.
you can make an invisible sprite like this
let scoreSprite = SKSpriteNode(color: SKColor.clearColor(), size: theSize)
add a physics body to that, and youre good to go.
I am creating my first game with Sprite Kit. It is a Flappy Bird type game. The image on the left is what the game looked like earlier today. I ended up creating an entirely new project. I copied the code for the previous project and pasted it into the current one. I placed the same exact images as the previous project into the current one as well. There are no compiling errors and everything works the same except that the game now looks like the image on the right. As you can see the images widths are smaller. Any advice would be greatly appreciated, if you would like me to provide all of the code in this question let me know, thank you.
This is all of the code for the Archery Scene:
//
// ArcheryScene.swift
// FlappyBird (swift)
//
// Created by Brandon Ballard on 1/6/15.
// Copyright (c) 2015 Brandon Ballard. All rights reserved.
//
import UIKit
import SpriteKit
class ArcheryScene: SKScene, SKPhysicsContactDelegate {
var bird = SKSpriteNode()
var pipeUpTexture = SKTexture()
var pipeDownTexture = SKTexture()
var pipesMoveAndRemove = SKAction()
var score = 0
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
//Bird
var birdTexture = SKTexture(imageNamed:"Bird")
birdTexture.filteringMode = SKTextureFilteringMode.Nearest
bird = SKSpriteNode(texture: birdTexture)
bird.setScale(0.6)
bird.position = CGPoint(x: self.frame.width * 0.35 + 20, y: self.frame.size.height * 0.95)
bird.physicsBody = SKPhysicsBody(circleOfRadius: bird.size.height / 2.0)
bird.physicsBody?.dynamic = true
bird.physicsBody?.allowsRotation = true
bird.physicsBody?.affectedByGravity = true
bird.physicsBody!.collisionBitMask = ColliderType.BIRD.rawValue
bird.physicsBody!.contactTestBitMask = 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!.collisionBitMask = ColliderType.PIPE.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!.collisionBitMask = ColliderType.PIPE.rawValue
pipePair.addChild(pipeUp)
pipePair.runAction(pipesMoveAndRemove)
self.addChild(pipePair)
}
func didBeginContact(contact: SKPhysicsContactDelegate) {
scene?.view?.paused = true
//gameOver()
}
func createScoreNode() -> SKLabelNode {
let scoreNode = SKLabelNode(fontNamed: "Brandon Ballard")
scoreNode.name = "scoreNode"
let newScore = "\(score)"
scoreNode.text = newScore
scoreNode.fontSize = 125
scoreNode.fontColor = SKColor.cyanColor()
scoreNode.position = CGPointMake(CGRectGetMidX(self.frame), 58)
self.addChild(scoreNode)
return scoreNode
}
func gameOver() {
let scoreNode = self.createScoreNode()
self.addChild(scoreNode)
let fadeOut = SKAction.sequence([SKAction.waitForDuration(3.0), SKAction.fadeOutWithDuration(3.0)])
let welcomeReturn = SKAction.runBlock({
let transition = SKTransition.revealWithDirection(SKTransitionDirection.Down, duration: 1.0)
let welcomeScene = GameScene(fileNamed: "GameScene")
self.scene!.view?.presentScene(welcomeScene, transition: transition)
})
let sequence = SKAction.sequence([fadeOut, welcomeReturn])
self.runAction(sequence)
}
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 */
}
}
All I needed to do to fix my problem was add a simple line of code to my GameScene file. Here is the code I had in the GameScene file before I solved the problem that presents the ArcheryScene:
welcomeNode?.runAction(fadeAway, completion: {
let doors = SKTransition.pushWithDirection(SKTransitionDirection.Down, duration: 1.0)
let archeryScene = ArcheryScene(fileNamed: "ArcheryScene")
self.view?.presentScene(archeryScene, transition: doors)
})
All I had to do was add this line of code after I created "archeryScene":
archeryScene.scaleMode = .AspectFill
This made the images "fit to screen size". The exact description of ".AspectFill" used by Apple is:
"The scaling factor of each dimension is calculated and the larger of the two is chosen. Each axis of the scene is scaled by the same scaling factor. This guarantees that the entire area of the view is filled but may cause parts of the scene to be cropped."