How to change an SKSpriteNode image once a condition is met? - swift

I'm trying to change the image of the SKSpriteNode in my shop scene when an upgrade is bought, I believe I'm on the right track with the variable, but I think I am doing it wrong. Thanks for the help in advanced, I've been trying for a while to figure it out with no luck.
// ShopScene.swift
// TheLastFlight
//
// Created by -Zachary Walensa- on 8/3/16.
// Copyright © 2016 -Zachary Walensa-. All rights reserved.
//
import Foundation
import SpriteKit
var coinUpgrade = defaults.integer(forKey: "coinUpgradeSaved")
var missileUpgrade = defaults.integer(forKey: "missileUpgradeSaved")
class ShopScene: SKScene {
var coinLabel = SKLabelNode(fontNamed: "The Bold Font")
var missileUpgrades = SKSpriteNode(imageNamed: "missile1")
override func didMove(to view: SKView) {
coinLabel.text = "Coins: \(coinNumber)"
coinLabel.fontSize = 100
coinLabel.fontColor = SKColor.black
coinLabel.zPosition = 1
coinLabel.position = CGPoint(x: self.size.width/2, y: self.size.height*0.1)
self.addChild(coinLabel)
let background = SKSpriteNode(imageNamed: "background")
background.size = self.size
background.position = CGPoint(x: self.size.width/2, y: self.size.height/2)
background.zPosition = 0
self.addChild(background)
let mainMenu = SKLabelNode(fontNamed: "The Bold Font")
mainMenu.text = "Main Menu"
mainMenu.fontSize = 100
mainMenu.fontColor = SKColor.darkGray
mainMenu.position = CGPoint(x: self.size.width*0.5, y: self.size.height*0.3)
mainMenu.zPosition = 1
mainMenu.name = "Main Menu"
self.addChild(mainMenu)
let Life = SKSpriteNode(imageNamed: "lifeButton")
Life.position = CGPoint(x: self.size.width*0.3, y: self.size.height*0.8)
Life.zPosition = 1
Life.name = "Life"
self.addChild(Life)
let coinUpgrades = SKSpriteNode(imageNamed: "coinUpgrade")
coinUpgrades.position = CGPoint(x: self.size.width*0.5, y: self.size.height*0.8)
coinUpgrades.zPosition = 1
coinUpgrades.name = "Coin"
self.addChild(coinUpgrades)
missileUpgrades.position = CGPoint(x: self.size.width*0.8, y: self.size.height*0.8)
missileUpgrades.zPosition = 1
missileUpgrades.name = "missile"
self.addChild(missileUpgrades)
/*let coinLabel = SKLabelNode(fontNamed: "The Bold Font")
coinLabel.text = "Coins: \(coinNumber)"
coinLabel.fontSize = 100
coinLabel.fontColor = SKColor.black
coinLabel.zPosition = 1
coinLabel.position = CGPoint(x: self.size.width/2, y: self.size.height*0.1)
self.addChild(coinLabel) */
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches {
let pointOfTouch = touch.location(in: self)
let nodeITapped = atPoint(pointOfTouch)
if nodeITapped.name == "Main Menu" {
let sceneToMoveTo = MainMenuScene(size: self.size)
sceneToMoveTo.scaleMode = self.scaleMode
let myTrasition = SKTransition.fade(withDuration: 0.5)
self.view!.presentScene(sceneToMoveTo, transition: myTrasition)
}
if nodeITapped.name == "Life" {
if coinNumber >= 50 {
lifeNumber += 1
coinNumber -= 50
defaults.set(coinNumber, forKey: "coinNumberSaved")
defaults.set(lifeNumber, forKey: "lifeNumberSaved")
coinLabel.text = "Coins: \(coinNumber)"
}
}
if nodeITapped.name == "Coin" {
if coinNumber >= 600 {
coinNumber -= 600
defaults.set(coinNumber, forKey: "coinNumberSaved")
coinUpgrade = 1
defaults.set(coinUpgrade, forKey: "coinUpgradeSaved")
coinLabel.text = "Coins: \(coinNumber)"
}
}
if nodeITapped.name == "missile" {
if missileUpgrade == 2 {
missileUpgrade = 1
defaults.set(missileUpgrade, forKey: "missileUpgradeSaved")
if missileUpgrade == 1 {
missileUpgrade = 2
defaults.set(missileUpgrade, forKey: "missileUpgradeSaved")
}
}
else if missileUpgrade == 0 || coinNumber >= 1400 {
coinNumber -= 1400
defaults.set(coinNumber, forKey: "coinNumberSaved")
missileUpgrade = 2
defaults.set(missileUpgrade, forKey: "missileUpgradeSaved")
coinLabel.text = "Coins: \(coinNumber)"
missileUpgrades = SKSpriteNode(imageNamed: "missile")
}
}
}
}
}

There are a few ways to do this:
1) Create a Boolean variable that switches to true when the condition is met. Then in the update function, you can constantly check for if the condition is met:
var conditionIsMet = false
// Your code
//
//...
override func update(_ currentTime: CFTimeInterval) {
if conditionIsMet {
// Change your texture here
}
}
2) Where you get the trigger for when you want to change the texture, you can simply change the texture as part of the code/function that runs. That means that if you are trying to do this on a touch, you would add the code to change the texture there.
FYI: To change the texture just do something like this:
node.texture = SKTexture(imageNamed: "image name goes here")

Related

How to shoot with two or more bullets in Swift

I am trying to find a way to shoot more than one bullets as power up increases. Also it's only going up straight on the first 4 power ups, but I would like it to have a little angle as it reaches 5 and up.
Can someone help me implement that with the following codes I currently have?
import SpriteKit
import GameplayKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var player: SKSpriteNode!
var scoreLabel: SKLabelNode!
var score: Int = 0 {
didSet {
scoreLabel.text = "Score: \(score)"
}
}
var gameTimer: Timer!
var possibleAliens = ["alien", "alien2", "alien3"]
//bitmask for alien and torpedo's physics body
let alienCategory:UInt32 = 0x1 << 1
let photonTorpedoCategory:UInt32 = 0x1 << 0
//lives
var livesArray:[SKSpriteNode]!
//powerUp
var powerUp: Int = 1
//didMove
override func didMove(to view: SKView) {
addLives()
starField = SKEmitterNode(fileNamed: "Starfield")
starField.position = CGPoint(x: 0, y: 1472)
starField.advanceSimulationTime(10)
self.addChild(starField)
starField.zPosition = -1
player = SKSpriteNode(imageNamed: "shuttle")
player.position = CGPoint(x: self.frame.size.width / 3.6, y: player.size.height / 2 + 20)
self.addChild(player)
//physicsWorld
self.physicsWorld.gravity = CGVector(dx: 0, dy: 0)
self.physicsWorld.contactDelegate = self
//score and scoreLabel
scoreLabel = SKLabelNode(text: "Score: 0")
scoreLabel.position = CGPoint(x: 80, y: self.frame.size.height - 60)
scoreLabel.fontName = "AmericanTypewriter-Bold"
scoreLabel.fontSize = 28
scoreLabel.fontColor = UIColor.white
score = 0
self.addChild(scoreLabel)
//create a timeInterval that can be changed depending on the difficulty
var timeInterval = 0.6
if UserDefaults.standard.bool(forKey: "hard"){
timeInterval = 0.2
}
//gameTimer
gameTimer = Timer.scheduledTimer(timeInterval: timeInterval, target: self, selector: #selector(addAlien), userInfo: nil, repeats: true)
//motion Manager initialization in didMove
motionManger.accelerometerUpdateInterval = 0.2
//creatingan acceleration data in didMove
motionManger.startAccelerometerUpdates(to: OperationQueue.current!) { (data: CMAccelerometerData?, error: Error?) in
if let accelerometerData = data{
let acceleration = accelerometerData.acceleration
self.xAcceleration = CGFloat(acceleration.x) * 0.75 + self.xAcceleration * 0.25
}
}
}
//func addLives
func addLives() {
//initialize livesArray from GameScene
livesArray = [SKSpriteNode]()
for live in 1 ... 3 {
let liveNode = SKSpriteNode(imageNamed: "shuttle")
liveNode.name = "live\(live)"
liveNode.position = CGPoint(x: self.frame.size.width - CGFloat((4-live)) * liveNode.size.width, y: self.frame.size.height - 60)
self.addChild(liveNode)
livesArray.append(liveNode)
}
}
//func addAlien
func addAlien() {
//using GK, pick possibleAliens[arrays] randomly, and shuffle
possibleAliens = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: possibleAliens) as! [String]
//bring the aliens to random position
let alien = SKSpriteNode(imageNamed: possibleAliens[0])
let randomAlienPosition = GKRandomDistribution(lowestValue: 0, highestValue: 414)
//make the position constant, use randomAlien and get next integer and use CGFloat then set alien position
let position = CGFloat(randomAlienPosition.nextInt())
alien.position = CGPoint(x: position, y: self.frame.size.height + alien.size.height)
//physicsBody of addAlien
alien.physicsBody = SKPhysicsBody(rectangleOf: alien.size)
alien.physicsBody?.isDynamic = true
alien.physicsBody?.categoryBitMask = alienCategory
alien.physicsBody?.contactTestBitMask = photonTorpedoCategory
alien.physicsBody?.collisionBitMask = 0
self.addChild(alien)
//make aliens move
let animationDuration: TimeInterval = 6
//SKAction to alien will make alien move from top to bottom of the screen, then remove alien from screen and from parent so it doesnt consume too much memory
var actionArray = [SKAction]()
actionArray.append(SKAction.move(to: CGPoint(x: position, y: -alien.size.height), duration: animationDuration))
//THIS ACTION WILL SEE IF IT REACHES THE FINAL DESTINATION BEFORE IT GETS ERASED AND TAKES A LIFE
//A RUN ACTION THAT WILL PLAY SOUND WHEN PLAYER LOSES SOUND.
actionArray.append(SKAction.run{
self.run(SKAction.playSoundFileNamed("loose.mp3", waitForCompletion: false))
if self.livesArray.count > 0 {
let liveNode = self.livesArray.first
liveNode!.removeFromParent()
self.livesArray.removeFirst()
if self.livesArray.count == 0{
let transition = SKTransition.flipHorizontal(withDuration: 0.5)
let gameOver = SKScene(fileNamed: "GameOverScene") as! GameOverScene
gameOver.score = self.score
self.view?.presentScene(gameOver, transition: transition)
}
}
})
actionArray.append(SKAction.removeFromParent())
//make a run function on the alien to pass allong actionArray
alien.run(SKAction.sequence(actionArray))
}
//fire fireTorpedo when tapped
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
fireTorpedo()
}
//func FireTorpedo or bullet
func fireTorpedo(){
//adds sound, image and position of the torpedo
self.run(SKAction.playSoundFileNamed("torpedo.mp3", waitForCompletion: false))
let torpedoNode = SKSpriteNode(imageNamed: "torpedo")
//powerUp switch
switch (powerUp)
{
case 1:
torpedoNode.position.x = player.position.x
torpedoNode.position.y = player.position.y
case 2:
torpedoNode.position.y = player.position.y
torpedoNode.position.x = player.position.x - 10
torpedoNode.position.x = player.position.x + 10
default:
print("out of torpedo ammo")
break
}
torpedoNode.position.y += 5
//add physicsBody for torpedo just like the aliens
torpedoNode.physicsBody = SKPhysicsBody(circleOfRadius: torpedoNode.size.width / 2)
torpedoNode.physicsBody?.isDynamic = true
torpedoNode.physicsBody?.categoryBitMask = photonTorpedoCategory
torpedoNode.physicsBody?.contactTestBitMask = alienCategory
torpedoNode.physicsBody?.collisionBitMask = 0
torpedoNode.physicsBody?.usesPreciseCollisionDetection = true
self.addChild(torpedoNode)
//add animation like in alien
let animationDuration: TimeInterval = 0.3
//make torpedo move up and disappear
var actionArray = [SKAction]()
actionArray.append(SKAction.move(to: CGPoint(x: player.position.x, y: self.frame.size.height + 10), duration: animationDuration))
actionArray.append(SKAction.removeFromParent())
//run the torpedo
torpedoNode.run(SKAction.sequence(actionArray))
}
func didBegin(_ contact: SKPhysicsContact){
var firstBody: SKPhysicsBody
var secondBody: SKPhysicsBody
//check if two bodies touch
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask{
firstBody = contact.bodyA
secondBody = contact.bodyB
} else {
firstBody = contact.bodyB
secondBody = contact.bodyA
}
//to findout which body is the torpedo and which is alien
if (firstBody.categoryBitMask & photonTorpedoCategory) != 0 && (secondBody.categoryBitMask & alienCategory) != 0 {
torpedoDidCollideWithAlien(torpedoNode: firstBody.node as? SKSpriteNode, alienNode: secondBody.node as? SKSpriteNode)
}
}
func torpedoDidCollideWithAlien (torpedoNode: SKSpriteNode?, alienNode: SKSpriteNode?){
if let explosion = SKEmitterNode(fileNamed: "Explosion"){
if let alien = alienNode{
explosion.position = alien.position
}
self.addChild(explosion)
self.run(SKAction.playSoundFileNamed("explosion.mp3", waitForCompletion: false))
if let torpedo = torpedoNode{
torpedo.removeFromParent()
}
if let alien = alienNode{
alien.removeFromParent()
}
//see the explosion effect longer and not disappear immediately with run function with action and completion handler
self.run(SKAction.wait(forDuration: 2)){
explosion.removeFromParent()
}
//add and update score
score += 5
}
}
override func didSimulatePhysics() {
player.position.x += xAcceleration * 50
if player.position.x < -20 {
player.position = CGPoint(x: self.size.width + 20, y: player.position.y)
} else if player.position.x > self.size.width + 20 {
player.position = CGPoint(x: -20, y: player.position.y)
}
}
}

How to fix my while statement so it doesn't terminate due to a memory issue

So I'm in the early stages of making an incremental game, I've been doing testing to understand how I could make the upgrades and such work. I'm trying to have it happen that when you buy the first upgrade it adds 1 coin every second, but the while statement is giving me this error in the debugger "Terminated due to memory issue" I believe this is due to the while statement. Please help and thanks!
//
// MainMenuScene.swift
// SoloPlane
//
// Created by -Zach on 7/27/16.
// Copyright © 2016 -Zach. All rights reserved.
//
import Foundation
import SpriteKit
import AVFoundation
import GameController
let defaults = UserDefaults()
var coins = defaults.integer(forKey: "coinsSaved")
let coinLabel = SKLabelNode(fontNamed: "The Bold Font")
var upgrade1 = defaults.integer(forKey: "upgrade1Saved")
class MainMenuScene: SKScene {
override func didMove(to view: SKView) {
//var backingAudio = AVAudioPlayer()
let background = SKSpriteNode(imageNamed: "background")
background.size = self.size
background.position = CGPoint(x: self.size.width/2, y: self.size.height/2)
background.zPosition = 0
self.addChild(background)
let gameTitle1 = SKLabelNode(fontNamed: "The Bold Font")
gameTitle1.text = "The Kingdom"
gameTitle1.fontSize = 200
gameTitle1.fontColor = SKColor.darkGray
gameTitle1.position = CGPoint(x: self.size.width*0.5, y: self.size.height*0.9)
gameTitle1.zPosition = 1
self.addChild(gameTitle1)
let Shop = SKLabelNode(fontNamed: "The Bold Font")
Shop.text = "Shop"
Shop.fontSize = 125
Shop.fontColor = SKColor.darkGray
Shop.position = CGPoint(x: self.size.width*0.5, y: self.size.height*0.3)
Shop.zPosition = 1
Shop.name = "Shop"
self.addChild(Shop)
let beginGame = SKLabelNode(fontNamed: "The Bold Font")
beginGame.text = "Coins"
beginGame.fontSize = 150
beginGame.fontColor = SKColor.darkGray
beginGame.position = CGPoint(x: self.size.width*0.5, y: self.size.height*0.4)
beginGame.zPosition = 1
beginGame.name = "Coins"
self.addChild(beginGame)
let Game = SKLabelNode(fontNamed: "The Bold Font")
Game.text = "Upgrade1"
Game.fontSize = 150
Game.fontColor = SKColor.darkGray
Game.position = CGPoint(x: self.size.width*0.5, y: self.size.height*0.2)
Game.zPosition = 1
Game.name = "Upgrade1"
self.addChild(Game)
coinLabel.text = "Coins: \(coins)"
coinLabel.fontSize = 100
coinLabel.fontColor = SKColor.black
coinLabel.zPosition = 1
coinLabel.position = CGPoint(x: self.size.width/2, y: self.size.height*0.1)
self.addChild(coinLabel)
//if backingAudio.playing {
/*
let settings = SKLabelNode(fontNamed: "The Bold Font")
settings.text = "Settings"
settings.fontSize = 150
settings.fontColor = SKColor.darkGrayColor()
settings.position = CGPoint(x: self.size.width*0.5, y: self.size.height*0.3)
settings.zPosition = 1
settings.name = "settings"
self.addChild(settings)
*/
while upgrade1 >= 1{
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1) ) {
coins += 1
}
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches {
let pointOfTouch = touch.location(in: self)
let nodeITapped = atPoint(pointOfTouch)
if nodeITapped.name == "Coins" {
coins += 1
coinLabel.text = "Coins: \(coins)"
defaults.set(coins, forKey: "coinsSaved")
/*let sceneToMoveTo = GameScene(size: self.size)
sceneToMoveTo.scaleMode = self.scaleMode
let myTrasition = SKTransition.fade(withDuration: 0.5)
self.view!.presentScene(sceneToMoveTo, transition: myTrasition)*/
}
if nodeITapped.name == "Upgrade1" {
upgrade1 += 1
defaults.set(upgrade1, forKey: "upgrade1Saved")
}
if nodeITapped.name == "wood" {
/* let sceneToMoveTo = ShopScene(size: self.size)
sceneToMoveTo.scaleMode = self.scaleMode
let myTrasition = SKTransition.fade(withDuration: 0.5)
self.view!.presentScene(sceneToMoveTo, transition: myTrasition) */
}
if nodeITapped.name == "Credits" {
/*
let sceneToMoveTo = CreditsScene(size: self.size)
sceneToMoveTo.scaleMode = self.scaleMode
let myTrasition = SKTransition.fade(withDuration: 0.5)
self.view!.presentScene(sceneToMoveTo, transition: myTrasition) */
}
while (upgrade1 == 1){
//DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1) ) {
sleep(1)
upgrade1 += 1
coins += 1
coinLabel.text = "Coins: \(coins)"
defaults.set(coins, forKey: "coinsSaved")
}
}
}
}
I can see you were trying to use DispatchQueue and asyncAfter but don't know the right way to do it.
You can have a async helper method call that itself recursively to achieve the async while effect.
func update () {
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1) ) {
if upgrade1 == 1 {
self.upgrade1 += 1
self.coins += 1
self.coinLabel.text = "Coins: \(coins)"
self.defaults.set(coins, forKey: "coinsSaved")
self.update() // schedule another update after 1 second
}
}
}
and replace the while loop with update()
You may need to tweak the position of if upgrade1 == 1

Is it possible to update an SKLabelNode?

I'm trying to update the coinLabel, so that when the "life" node is tapped, it will update the SKLabelNode and display the correct coins after they have been subtracted from the total. I'm new to coding, so if there is anything else wrong with this please reply! Thanks!
import Foundation
import SpriteKit
class ShopScene: SKScene {
override func didMove(to view: SKView) {
let background = SKSpriteNode(imageNamed: "background")
background.size = self.size
background.position = CGPoint(x: self.size.width/2, y: self.size.height/2)
background.zPosition = 0
self.addChild(background)
let mainMenu = SKLabelNode(fontNamed: "The Bold Font")
mainMenu.text = "Main Menu"
mainMenu.fontSize = 100
mainMenu.fontColor = SKColor.darkGray
mainMenu.position = CGPoint(x: self.size.width*0.5, y: self.size.height*0.3)
mainMenu.zPosition = 1
mainMenu.name = "Main Menu"
self.addChild(mainMenu)
let Life = SKLabelNode(fontNamed: "The Bold Font")
Life.text = "Click here to buy 1 life!"
Life.fontSize = 130
Life.fontColor = SKColor.darkGray
Life.position = CGPoint(x: self.size.width*0.5, y: self.size.height*0.6)
Life.zPosition = 1
Life.name = "Life"
self.addChild(Life)
let coinLabel = SKLabelNode(fontNamed: "The Bold Font")
coinLabel.text = "Coins: \(coinNumber)"
coinLabel.fontSize = 100
coinLabel.fontColor = SKColor.black
coinLabel.zPosition = 1
coinLabel.position = CGPoint(x: self.size.width/2, y: self.size.height*0.1)
self.addChild(coinLabel)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches {
let pointOfTouch = touch.location(in: self)
let nodeITapped = atPoint(pointOfTouch)
if nodeITapped.name == "Main Menu" {
let sceneToMoveTo = MainMenuScene(size: self.size)
sceneToMoveTo.scaleMode = self.scaleMode
let myTrasition = SKTransition.fade(withDuration: 0.5)
self.view!.presentScene(sceneToMoveTo, transition: myTrasition)
}
if nodeITapped.name == "Life" {
if coinNumber > 10 {
lifeNumber += 1
coinNumber -= 10
defaults.set(coinNumber, forKey: "coinNumberSaved")
defaults.set(lifeNumber, forKey: "lifeNumberSaved")
return
}
}
}
}
}
Just update your text in your tap recognizer again to change it
coinLabel.text = "Coins: \(coinNumber)"
You will need to make sure you have a reference of your coinLabel outside of your init so you can manipulate it outside of it.
At the top of your ShopScene you can do...
class ShopScene: SKScene {
var coinLabel = SKLabelNode()
//your code
}
and then when you go to init in your didMove(to view:) func, do instead...
coinLabel = SKLabelNode(fontNamed: "The Bold Font")
That way you can have a reference of it throughout your code. Although in the future you will want to think about making a subclass of SKNode to handle all your UI elements so you don't init everything in your GameScene.

didBeginContact not working in Swift 2 + SpriteKit

I'm working on a game, and I'm using spritekit and Swift.
This is my code:
import SpriteKit
struct collision {
static let arrow:UInt32 = 0x1 << 1
static let runner:UInt32 = 0x1 << 2
static let target:UInt32 = 0x1 << 3
static let targetCenter:UInt32 = 0x1 << 4
}
class GameScene: SKScene, SKPhysicsContactDelegate {
var person = SKSpriteNode()
var box = SKSpriteNode()
var screenSize:CGSize!
var gameScreenSize:CGSize!
var gameStarted:Bool = false
var moveAndRemove = SKAction()
var boxVelocity:NSTimeInterval = 5.5
override func didMoveToView(view: SKView) {
self.physicsWorld.gravity = CGVectorMake(0, -1.0)
self.physicsWorld.contactDelegate = self
screenSize = self.frame.size
gameScreenSize = view.frame.size
createPerson()
}
func createPerson() -> Void {
person.texture = SKTexture(imageNamed:"person")
person.setScale(1.0)
person.size = CGSize(width: 80, height: 80)
person.position = CGPoint(x: screenSize.width / 2, y: 150)
person.physicsBody = SKPhysicsBody(rectangleOfSize: person.size)
person.physicsBody?.affectedByGravity = false
person.physicsBody?.dynamic = false
self.addChild(person)
}
func createTarget() -> Void {
box = SKSpriteNode()
box.size = CGSize(width: 70, height: 100)
box.setScale(1.0)
box.position = CGPoint(x: (screenSize.width / 3) * 2, y: screenSize.height + box.size.height)
box.texture = SKTexture(imageNamed: "box")
box.physicsBody? = SKPhysicsBody(rectangleOfSize: box.size)
box.physicsBody?.categoryBitMask = collision.target
box.physicsBody?.collisionBitMask = collision.arrow
box.physicsBody?.contactTestBitMask = collision.targetCenter
box.physicsBody?.affectedByGravity = false
box.physicsBody?.dynamic = true
box.physicsBody?.usesPreciseCollisionDetection = true
self.addChild(box)
let distance = CGFloat(self.frame.height - box.frame.height)
let moveTargets = SKAction.moveToY(-distance, duration: boxVelocity)
let removeTargets = SKAction.removeFromParent()
moveAndRemove = SKAction.sequence([moveTargets,removeTargets])
box.runAction(moveAndRemove)
}
func createBall() ->Void {
let ball = SKSpriteNode()
ball.size = CGSize(width: 20, height: 22)
ball.zPosition = 5
let moveToXY = CGPoint(x: self.size.width, y: self.size.height)
ball.texture = SKTexture(imageNamed: "ball")
ball.position = CGPointMake(person.position.x + ball.size.width, person.position.y + ball.size.height)
ball.physicsBody? = SKPhysicsBody(rectangleOfSize: ball.size)
ball.physicsBody?.categoryBitMask = collision.arrow
ball.physicsBody?.collisionBitMask = collision.target
ball.physicsBody?.affectedByGravity = false
ball.physicsBody?.dynamic = true
ball.physicsBody?.usesPreciseCollisionDetection = true
let action = SKAction.moveTo(moveToXY, duration: 1.5)
let delay = SKAction.waitForDuration(1.5)
ball.runAction(SKAction.sequence([action,delay]))
self.addChild(ball)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
if gameStarted == false {
gameStarted = true
let spawn = SKAction.runBlock { () in
self.createTarget()
}
let delay = SKAction.waitForDuration(1.5)
let spawnDelay = SKAction.sequence([spawn, delay])
let spanDelayForever = SKAction.repeatActionForever(spawnDelay)
self.runAction(spanDelayForever)
} else {
createBall()
boxVelocity -= 0.1
}
}
func didBeginContact(contact: SKPhysicsContact) {
print("Detect")
}
func didEndContact(contact: SKPhysicsContact) {
print("end detect")
}
override func update(currentTime: CFTimeInterval) {
}
}
But when I run the game, the collision between objects does not. I'm trying to solve a while, but found nothing. Can someone help me?
Project files.
Try with these to modifications:
box.physicsBody = SKPhysicsBody(rectangleOfSize: box.size)
box.physicsBody?.categoryBitMask = collision.target
box.physicsBody?.collisionBitMask = collision.arrow
box.physicsBody?.contactTestBitMask = collision.targetCenter
and
ball.physicsBody = SKPhysicsBody(rectangleOfSize: ball.size)
ball.physicsBody?.categoryBitMask = collision.arrow
ball.physicsBody?.collisionBitMask = collision.target
ball.physicsBody?.contactTestBitMask = collision.target
Note the absence of "?" while you init the physicsBody and the new contactTestBitMask

Physics issue: ball is bouncing too high

I am working on game where a ball bounces and hits a platform and then bounces right back. Ideally the ball should bounce to exactly the height that it started at. However, in my game the ball slowly keeps bouncing higher and higher. I've looked at the documentation and tried to change all of the physics properties but nothing seems to work.
Any suggestions?
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
let ballCategory:UInt32 = 0x1 << 0;
let circleCategory:UInt32 = 0x1 << 1;
let platformCategory:UInt32 = 0x1 << 2;
var ball = SKShapeNode(circleOfRadius: 20.0)
var circle = SKShapeNode(circleOfRadius: 200.0)
var platform = SKShapeNode(rectOfSize: CGSizeMake(10, 1))
var circleColor = 2
var ballColor = 3
var scoreLabel: SKLabelNode!
var score = 0
override func didMoveToView(view: SKView) {
setUpLabels()
self.physicsWorld.contactDelegate = self
backgroundColor = (UIColor.whiteColor())
ball.fillColor = SKColor.redColor()
ball.strokeColor = SKColor.clearColor()
ball.position = CGPoint(x: self.size.width/2, y: self.size.height/2+60)
ball.physicsBody = SKPhysicsBody(circleOfRadius: 20)
ball.physicsBody?.categoryBitMask = ballCategory
ball.physicsBody?.collisionBitMask = platformCategory
ball.physicsBody?.contactTestBitMask = platformCategory
ball.physicsBody?.dynamic = true
ball.physicsBody?.restitution = 1.0
ball.physicsBody?.affectedByGravity = true
ball.physicsBody?.linearDamping = 0.0
ball.physicsBody?.friction = 0.0
self.addChild(ball)
circle.fillColor = SKColor.clearColor()
circle.strokeColor = SKColor.redColor()
circle.lineWidth = 5.0
circle.position = CGPoint(x: self.size.width/2, y: self.size.height/2)
circle.physicsBody = SKPhysicsBody(edgeLoopFromPath: circle.path)
circle.physicsBody?.categoryBitMask = circleCategory
self.addChild(circle)
platform.fillColor = SKColor.clearColor()
platform.strokeColor = SKColor.clearColor()
platform.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(10, 1))
platform.position = CGPoint(x: self.size.width/2, y: circle.position.y/2)
platform.physicsBody?.categoryBitMask = platformCategory
platform.physicsBody?.dynamic = false
platform.physicsBody?.friction = 0.0
self.addChild(platform)
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
circleColor += 1
if circleColor%3 == 1 {
circle.strokeColor = UIColor.redColor()
}
if circleColor%3 == 2 {
circle.strokeColor = UIColor.greenColor()
}
if circleColor%3 == 3 {
circle.strokeColor = UIColor.yellowColor()
}
if circleColor%3 == 4 {
circle.strokeColor = UIColor.greenColor()
}
if circleColor%3 == 5 {
circle.strokeColor = UIColor.blueColor()
}
if circleColor%3 == 0 {
circle.strokeColor = UIColor.yellowColor()
}
}
func didBeginContact(contact: SKPhysicsContact) {
if ball.fillColor == circle.strokeColor {
score += 1
scoreLabel.text = String("Score \(score)")
}
if ball.fillColor != circle.strokeColor {
let transition = SKTransition.revealWithDirection(SKTransitionDirection.Down, duration: 1.0)
let scene = SecondScene(size: self.scene!.size)
scene.scaleMode = SKSceneScaleMode.AspectFill
self.scene!.view!.presentScene(scene, transition: transition)
}
ballColor = Int(arc4random_uniform(10))
if ballColor%3 == 1 {
ball.fillColor = UIColor.redColor()
}
if ballColor%3 == 2 {
ball.fillColor = UIColor.greenColor()
}
if ballColor%3 == 3 {
ball.fillColor = UIColor.yellowColor()
}
if ballColor%3 == 4 {
ball.fillColor = UIColor.greenColor()
}
if ballColor%3 == 5 {
ball.fillColor = UIColor.blueColor()
}
if ballColor%3 == 0 {
ball.fillColor = UIColor.yellowColor()
}
}
func setUpLabels () {
scoreLabel = SKLabelNode(fontNamed: "Arial")
scoreLabel.position = CGPoint(x: 60, y: self.size.height-30)
scoreLabel.text = String("Score \(score)")
scoreLabel.fontColor = UIColor.blackColor()
scoreLabel.fontSize = 25
self.addChild(scoreLabel)
}
override func update(currentTime: CFTimeInterval) {
}
}
Reduce restitution a little which take control of the bounciness of the physics body. Like this:
ball.physicsBody?.restitution = 0.9
In general numerical differential equation solvers that solve the ode for the next time step will introduce energy into the system, which is why you are seeing what you are seeing. You can mitigate this by introduce a damping force. In the case of SpriteKit this means changing the restitution or linear damping factors.
For more about the drift that occurs see: https://en.wikipedia.org/wiki/Energy_drift or http://www.cs.cmu.edu/~baraff/sigcourse/notesb.pdf.