How to create a subview inside an SKScene? - sprite-kit

Before "Buy Ship" is pressed So I am trying to create a shop where players can buy new ships. When the player hits the "buy button" image I want a combination of images and texts to become visible which acts as a sort of conformation screen. Hopefully you can see here what I mean. I would also greatly appreciate if you could tell me how to dim everything besides the conformation box. After "Buy Ship" is pressed.
This is what my code looks like so far:
import Foundation
import SpriteKit
class ShopPage1: SKScene{
override func didMove(to view: SKView) {
let background = SKSpriteNode(imageNamed: "background")
background.position = CGPoint(x: self.size.width/2, y: self.size.height/2)
background.zPosition = 0
self.addChild(background)
let balance = SKLabelNode(fontNamed: "The Bold Font")
balance.text = "$\(balanceAmount)"
balance.fontSize = 100
balance.fontColor = SKColor.green
balance.position = CGPoint(x: self.size.width/2, y: self.size.height*0.87)
balance.zPosition = 1
self.addChild(balance)
let backToMainMenuButton = SKSpriteNode(imageNamed: "backButton2")
backToMainMenuButton.position = CGPoint(x: self.size.width*0.25, y: self.size.height*0.89)
backToMainMenuButton.zPosition = 1
backToMainMenuButton.size = CGSize(width: 200, height: 200)
backToMainMenuButton.name = "backToMainMenuButton"
self.addChild(backToMainMenuButton)
let shipNameLabel = SKLabelNode(fontNamed: "The Bold Font")
shipNameLabel.text = "Stealth"
shipNameLabel.fontSize = 200
shipNameLabel.fontColor = SKColor.white
shipNameLabel.position = CGPoint(x: self.size.width/2, y: self.size.height*0.60)
shipNameLabel.zPosition = 1
shipNameLabel.name = "shipNameLabel"
self.addChild(shipNameLabel)
let nextShipButton = SKSpriteNode(imageNamed: "nextShipButton1")
nextShipButton.position = CGPoint(x: self.size.width*0.75, y: self.size.height*0.40)
nextShipButton.zPosition = 1
nextShipButton.size = CGSize(width: 300, height: 300)
nextShipButton.name = "nextShipButton"
self.addChild(nextShipButton)
let nextShipClick = SKLabelNode(fontNamed: "The Bold Font")
nextShipClick.text = "▲"
nextShipClick.fontSize = 300
nextShipClick.fontColor = UIColor.clear
nextShipClick.position = CGPoint(x: self.size.width*0.753, y: self.size.height*0.36)
nextShipClick.zPosition = 2
nextShipClick.name = "nextShipClick"
self.addChild(nextShipClick)
let shipForSale = SKSpriteNode(imageNamed: "playerShip4")
shipForSale.position = CGPoint(x: self.size.width/2, y: self.size.height*0.40)
shipForSale.zPosition = 1
shipForSale.size = CGSize(width: 150, height: 300)
self.addChild(shipForSale)
let shipPodium = SKSpriteNode(imageNamed: "shipPodium")
shipPodium.position = CGPoint(x: self.size.width*0.527, y: self.size.height*0.31)
shipPodium.zPosition = 1
shipPodium.size = CGSize(width: 1200, height: 70)
self.addChild(shipPodium)
let shipsCostLabel = SKLabelNode(fontNamed: "The Bold Font")
shipsCostLabel.text = "$500"
shipsCostLabel.fontSize = 200
shipsCostLabel.fontColor = SKColor.white
shipsCostLabel.position = CGPoint(x: self.size.width/2, y: self.size.height*0.20)
shipsCostLabel.zPosition = 1
self.addChild(shipsCostLabel)
let shipBuyButton = SKSpriteNode(imageNamed: "shipBuyButton")
shipBuyButton.position = CGPoint(x: self.size.width*0.54, y: self.size.height*0.15)
shipBuyButton.zPosition = 1
shipBuyButton.size = CGSize(width: 1500, height: 900)
shipBuyButton.name = "shipBuyButton"
self.addChild(shipBuyButton)
let conformationBackground = SKSpriteNode(imageNamed: "conformationBackground")
conformationBackground.position = CGPoint(x: self.size.width*0.51, y: self.size.height*0.40)
conformationBackground.zPosition = 2
conformationBackground.size = CGSize(width: 1300, height: 1400)
conformationBackground.name = "conformationBackground"
self.addChild(conformationBackground)
let conformationScreenTextTop = SKLabelNode(fontNamed: "The Bold Font")
conformationScreenTextTop.text = "Are you sure you wish to"
conformationScreenTextTop.fontSize = 80
conformationScreenTextTop.fontColor = SKColor.white
conformationScreenTextTop.position = CGPoint(x: self.size.width/2, y: self.size.height*0.46)
conformationScreenTextTop.zPosition = 3
self.addChild(conformationScreenTextTop)
let conformationScreenTextBottom = SKLabelNode(fontNamed: "The Bold Font")
conformationScreenTextBottom.text = "pruchase this ship?"
conformationScreenTextBottom.fontSize = 80
conformationScreenTextBottom.fontColor = SKColor.white
conformationScreenTextBottom.position = CGPoint(x: self.size.width/2, y: self.size.height*0.41)
conformationScreenTextBottom.zPosition = 3
self.addChild(conformationScreenTextBottom)
let conformationScreenTextYes = SKLabelNode(fontNamed: "The Bold Font")
conformationScreenTextYes.text = "Yes"
conformationScreenTextYes.fontSize = 150
conformationScreenTextYes.fontColor = SKColor.green
conformationScreenTextYes.position = CGPoint(x: self.size.width*0.30, y: self.size.height*0.30)
conformationScreenTextYes.zPosition = 3
self.addChild(conformationScreenTextYes)
let conformationScreenTextNo = SKLabelNode(fontNamed: "The Bold Font")
conformationScreenTextNo.text = "No"
conformationScreenTextNo.fontSize = 150
conformationScreenTextNo.fontColor = SKColor.red
conformationScreenTextNo.position = CGPoint(x: self.size.width*0.70, y: self.size.height*0.30)
conformationScreenTextNo.zPosition = 3
self.addChild(conformationScreenTextNo)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches{
let pointOfTouch = touch.location(in: self)
let tappedNode = atPoint(pointOfTouch)
let tappedNodeName = tappedNode.name
if tappedNodeName == "nextShipClick"{
let sceneToMoveTo = ShopPage2(size: self.size)
sceneToMoveTo.scaleMode = self.scaleMode
let myTransition = SKTransition.fade(withDuration: 0.5)
self.view!.presentScene(sceneToMoveTo, transition: myTransition)
}
if tappedNodeName == "backToMainMenuButton"{
let sceneToMoveTo = MainMenuScene(size: self.size)
sceneToMoveTo.scaleMode = self.scaleMode
let myTransition = SKTransition.fade(withDuration: 0.5)
self.view!.presentScene(sceneToMoveTo, transition: myTransition)
}
}
}
}

As Whirlwind said in his comment you can create an SKNode to the center of your scene (position CGPoint.zero) prepare it with all your stuff and hide it with the alpha properties set to 0 and put it's zPosition to -1 (under all the other visible nodes of your scene).
So when you button will be pressed, you simply change the zPosition to an highest value and the alpha to 1. To make more realistic these action s you could use some animations like:
extension UIView {
func fadeIn(_ duration:TimeInterval=1.0) {
UIView.animate(withDuration: duration, delay: 0.0, options: UIViewAnimationOptions.curveEaseIn, animations: {
self.alpha = 1.0 // Instead of a specific instance of, say, birdTypeLabel, we simply set [thisInstance] (ie, self)'s alpha
}, completion: nil)
}
func fadeOut(_ duration:TimeInterval=1.0) {
UIView.animate(withDuration: duration, delay: 0.0, options: UIViewAnimationOptions.curveEaseOut, animations: {
self.alpha = 0.0
}, completion: nil)
}
}

I have figured out how to get the conformation screen to work. Thanks to #AlessandroOrnano for all the help! I have attached my code below for future reference. If anyone has a more efficient way of doing this I would greatly appreciate any suggestions.
import Foundation
import SpriteKit
class ShopPage1: SKScene{
override func didMove(to view: SKView) {
let background = SKSpriteNode(imageNamed: "background")
background.position = CGPoint(x: self.size.width/2, y: self.size.height/2)
background.zPosition = 0
self.addChild(background)
let balance = SKLabelNode(fontNamed: "The Bold Font")
balance.text = "$\(balanceAmount)"
balance.fontSize = 100
balance.fontColor = SKColor.green
balance.position = CGPoint(x: self.size.width/2, y: self.size.height*0.87)
balance.zPosition = 1
self.addChild(balance)
let backToMainMenuButton = SKSpriteNode(imageNamed: "backButton2")
backToMainMenuButton.position = CGPoint(x: self.size.width*0.25, y: self.size.height*0.89)
backToMainMenuButton.zPosition = 1
backToMainMenuButton.size = CGSize(width: 200, height: 200)
backToMainMenuButton.name = "backToMainMenuButton"
self.addChild(backToMainMenuButton)
let shipNameLabel = SKLabelNode(fontNamed: "The Bold Font")
shipNameLabel.text = "Stealth"
shipNameLabel.fontSize = 200
shipNameLabel.fontColor = SKColor.white
shipNameLabel.position = CGPoint(x: self.size.width/2, y: self.size.height*0.60)
shipNameLabel.zPosition = 1
shipNameLabel.name = "shipNameLabel"
self.addChild(shipNameLabel)
let nextShipButton = SKSpriteNode(imageNamed: "nextShipButton1")
nextShipButton.position = CGPoint(x: self.size.width*0.75, y: self.size.height*0.40)
nextShipButton.zPosition = 1
nextShipButton.size = CGSize(width: 300, height: 300)
nextShipButton.name = "nextShipButton"
self.addChild(nextShipButton)
let nextShipClick = SKLabelNode(fontNamed: "The Bold Font")
nextShipClick.text = "▲"
nextShipClick.fontSize = 300
nextShipClick.fontColor = UIColor.clear
nextShipClick.position = CGPoint(x: self.size.width*0.753, y: self.size.height*0.36)
nextShipClick.zPosition = 2
nextShipClick.name = "nextShipClick"
self.addChild(nextShipClick)
let shipForSale = SKSpriteNode(imageNamed: "playerShip4")
shipForSale.position = CGPoint(x: self.size.width/2, y: self.size.height*0.40)
shipForSale.zPosition = 1
shipForSale.size = CGSize(width: 150, height: 300)
self.addChild(shipForSale)
let shipPodium = SKSpriteNode(imageNamed: "shipPodium")
shipPodium.position = CGPoint(x: self.size.width*0.527, y: self.size.height*0.31)
shipPodium.zPosition = 1
shipPodium.size = CGSize(width: 1200, height: 70)
self.addChild(shipPodium)
let shipsCostLabel = SKLabelNode(fontNamed: "The Bold Font")
shipsCostLabel.text = "$500"
shipsCostLabel.fontSize = 200
shipsCostLabel.fontColor = SKColor.white
shipsCostLabel.position = CGPoint(x: self.size.width/2, y: self.size.height*0.20)
shipsCostLabel.zPosition = 1
self.addChild(shipsCostLabel)
let shipBuyButton = SKSpriteNode(imageNamed: "shipBuyButton")
shipBuyButton.position = CGPoint(x: self.size.width*0.54, y: self.size.height*0.15)
shipBuyButton.zPosition = 1
shipBuyButton.size = CGSize(width: 1500, height: 900)
shipBuyButton.name = "shipBuyButton"
self.addChild(shipBuyButton)
let shipBuyButtonClick = SKLabelNode(fontNamed: "The Bold Font")
shipBuyButtonClick.text = "▅▅"
shipBuyButtonClick.fontSize = 300
shipBuyButtonClick.fontColor = UIColor.clear
shipBuyButtonClick.position = CGPoint(x: self.size.width/2, y: self.size.height*0.05)
shipBuyButtonClick.zPosition = 2
shipBuyButtonClick.name = "shipBuyButtonClick"
self.addChild(shipBuyButtonClick)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches{
let pointOfTouch = touch.location(in: self)
let tappedNode = atPoint(pointOfTouch)
let tappedNodeName = tappedNode.name
if tappedNodeName == "nextShipClick"{
let sceneToMoveTo = ShopPage2(size: self.size)
sceneToMoveTo.scaleMode = self.scaleMode
let myTransition = SKTransition.fade(withDuration: 0.5)
self.view!.presentScene(sceneToMoveTo, transition: myTransition)
}
if tappedNodeName == "backToMainMenuButton"{
let sceneToMoveTo = MainMenuScene(size: self.size)
sceneToMoveTo.scaleMode = self.scaleMode
let myTransition = SKTransition.fade(withDuration: 0.5)
self.view!.presentScene(sceneToMoveTo, transition: myTransition)
}
if tappedNodeName == "shipBuyButtonClick"{
let sceneToMoveTo = ShopPage1ConformationScreen(size: self.size)
sceneToMoveTo.scaleMode = self.scaleMode
let myTransition = SKTransition.fade(withDuration: 0.5)
self.view!.presentScene(sceneToMoveTo, transition: myTransition)
}
}
}
}
This is the code for ShopPage1ConformationScreen:
import Foundation
import SpriteKit
let shipForSale = SKSpriteNode(imageNamed: "playerShip4")
class ShopPage1ConformationScreen: SKScene{
override func didMove(to view: SKView) {
let background = SKSpriteNode(imageNamed: "background")
background.position = CGPoint(x: self.size.width/2, y: self.size.height/2)
background.zPosition = 0
self.addChild(background)
let balance = SKLabelNode(fontNamed: "The Bold Font")
balance.text = "$\(balanceAmount)"
balance.fontSize = 100
balance.fontColor = SKColor.green
balance.position = CGPoint(x: self.size.width/2, y: self.size.height*0.87)
balance.zPosition = 1
self.addChild(balance)
let backToMainMenuButton = SKSpriteNode(imageNamed: "backButton2")
backToMainMenuButton.position = CGPoint(x: self.size.width*0.25, y: self.size.height*0.89)
backToMainMenuButton.zPosition = 1
backToMainMenuButton.size = CGSize(width: 200, height: 200)
backToMainMenuButton.name = "backToMainMenuButton"
self.addChild(backToMainMenuButton)
let shipNameLabel = SKLabelNode(fontNamed: "The Bold Font")
shipNameLabel.text = "Stealth"
shipNameLabel.fontSize = 200
shipNameLabel.fontColor = SKColor.white
shipNameLabel.position = CGPoint(x: self.size.width/2, y: self.size.height*0.60)
shipNameLabel.zPosition = 1
shipNameLabel.name = "shipNameLabel"
self.addChild(shipNameLabel)
let nextShipButton = SKSpriteNode(imageNamed: "nextShipButton1")
nextShipButton.position = CGPoint(x: self.size.width*0.75, y: self.size.height*0.40)
nextShipButton.zPosition = 1
nextShipButton.size = CGSize(width: 300, height: 300)
nextShipButton.name = "nextShipButton"
self.addChild(nextShipButton)
let nextShipClick = SKLabelNode(fontNamed: "The Bold Font")
nextShipClick.text = "▲"
nextShipClick.fontSize = 300
nextShipClick.fontColor = UIColor.clear
nextShipClick.position = CGPoint(x: self.size.width*0.753, y: self.size.height*0.36)
nextShipClick.zPosition = 2
nextShipClick.name = "nextShipClick"
self.addChild(nextShipClick)
shipForSale.position = CGPoint(x: self.size.width/2, y: self.size.height*0.40)
shipForSale.zPosition = 1
shipForSale.size = CGSize(width: 150, height: 300)
self.addChild(shipForSale)
let shipPodium = SKSpriteNode(imageNamed: "shipPodium")
shipPodium.position = CGPoint(x: self.size.width*0.527, y: self.size.height*0.31)
shipPodium.zPosition = 1
shipPodium.size = CGSize(width: 1200, height: 70)
self.addChild(shipPodium)
let shipsCostLabel = SKLabelNode(fontNamed: "The Bold Font")
shipsCostLabel.text = "$500"
shipsCostLabel.fontSize = 200
shipsCostLabel.fontColor = SKColor.white
shipsCostLabel.position = CGPoint(x: self.size.width/2, y: self.size.height*0.20)
shipsCostLabel.zPosition = 1
self.addChild(shipsCostLabel)
let shipBuyButton = SKSpriteNode(imageNamed: "shipBuyButton")
shipBuyButton.position = CGPoint(x: self.size.width*0.54, y: self.size.height*0.15)
shipBuyButton.zPosition = 1
shipBuyButton.size = CGSize(width: 1500, height: 900)
shipBuyButton.name = "shipBuyButton"
self.addChild(shipBuyButton)
let conformationBackground = SKSpriteNode(imageNamed: "conformationBackground")
conformationBackground.position = CGPoint(x: self.size.width*0.51, y: self.size.height*0.40)
conformationBackground.zPosition = 2
conformationBackground.size = CGSize(width: 1300, height: 1400)
conformationBackground.name = "conformationBackground"
self.addChild(conformationBackground)
let conformationScreenTextTop = SKLabelNode(fontNamed: "The Bold Font")
conformationScreenTextTop.text = "Are you sure you wish to"
conformationScreenTextTop.fontSize = 80
conformationScreenTextTop.fontColor = SKColor.white
conformationScreenTextTop.position = CGPoint(x: self.size.width/2, y: self.size.height*0.46)
conformationScreenTextTop.zPosition = 3
conformationScreenTextTop.name = "comformationScreenTextTop"
self.addChild(conformationScreenTextTop)
let conformationScreenTextBottom = SKLabelNode(fontNamed: "The Bold Font")
conformationScreenTextBottom.text = "purchase this ship?"
conformationScreenTextBottom.fontSize = 80
conformationScreenTextBottom.fontColor = SKColor.white
conformationScreenTextBottom.position = CGPoint(x: self.size.width/2, y: self.size.height*0.41)
conformationScreenTextBottom.zPosition = 3
conformationScreenTextBottom.name = "conformationScreenTextBottom"
self.addChild(conformationScreenTextBottom)
let conformationScreenTextYes = SKLabelNode(fontNamed: "The Bold Font")
conformationScreenTextYes.text = "Yes"
conformationScreenTextYes.fontSize = 150
conformationScreenTextYes.fontColor = SKColor.green
conformationScreenTextYes.position = CGPoint(x: self.size.width*0.30, y: self.size.height*0.30)
conformationScreenTextYes.zPosition = 3
conformationScreenTextYes.name = "conformationScreenTextYes"
self.addChild(conformationScreenTextYes)
let conformationScreenTextNo = SKLabelNode(fontNamed: "The Bold Font")
conformationScreenTextNo.text = "No"
conformationScreenTextNo.fontSize = 150
conformationScreenTextNo.fontColor = SKColor.red
conformationScreenTextNo.position = CGPoint(x: self.size.width*0.70, y: self.size.height*0.30)
conformationScreenTextNo.zPosition = 3
conformationScreenTextNo.name = "conformationScreenTextNo"
self.addChild(conformationScreenTextNo)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches{
let pointOfTouch = touch.location(in: self)
let tappedNode = atPoint(pointOfTouch)
let tappedNodeName = tappedNode.name
if(balanceAmount >= 500){
if tappedNodeName == "conformationScreenTextYes"{
player = shipForSale
balanceAmount -= 500
let sceneToMoveTo = ShopPage1(size: self.size)
sceneToMoveTo.scaleMode = self.scaleMode
let myTransition = SKTransition.fade(withDuration: 0.5)
self.view!.presentScene(sceneToMoveTo, transition: myTransition)
}
}
if tappedNodeName == "conformationScreenTextNo"{
let sceneToMoveTo = ShopPage1(size: self.size)
sceneToMoveTo.scaleMode = self.scaleMode
let myTransition = SKTransition.fade(withDuration: 0.5)
self.view!.presentScene(sceneToMoveTo, transition: myTransition)
}
}
}
}
For additional shop pages just copy and paste these sets of codes. With the exception of changing the the tapped nodes around to the correct order in which you wish to move from page to page.

Related

Trying to load images from the assets folder in xcode into an array but having hard time

import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate{
var picsArray = [String]()
var scoreLabel: SKLabelNode!
var editLabel: SKLabelNode!
var editingMode = false{
didSet{
if editingMode{
editLabel.text = "Done"
}
else{
editLabel.text = "Edit"
}
}
}
var score = 0 {
didSet{
scoreLabel.text = " score: \(score)"
}
}
override func didMove(to view: SKView) {
let fm = FileManager.default
let path = Bundle.main.resourcePath!
let items = try! fm.contentsOfDirectory(atPath: path)
for item in items {
if item.hasPrefix("ball"){
picsArray.append(item)
}
print(picsArray.count)
}
physicsWorld.contactDelegate = self
let background = SKSpriteNode(imageNamed: "background")
background.position = CGPoint(x: 512, y: 384)
background.blendMode = .replace
background.zPosition = -1
addChild(background)
scoreLabel = SKLabelNode(fontNamed: "chalkduster")
scoreLabel.text = "Score: 0"
scoreLabel.horizontalAlignmentMode = .right
scoreLabel.position = CGPoint(x: 980, y: 700)
addChild(scoreLabel)
editLabel = SKLabelNode(fontNamed: "chalkduster")
editLabel.text = "Edit"
editLabel.position = CGPoint(x: 80, y: 700)
addChild(editLabel)
physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
makeSlots(at: CGPoint(x: 128, y: 0), isGood: true)
makeSlots(at: CGPoint(x: 384, y: 0), isGood: false)
makeSlots(at: CGPoint(x: 640, y: 0), isGood: true)
makeSlots(at: CGPoint(x: 896, y: 0), isGood: false)
makeBouncer(at: CGPoint(x: 0, y: 0))
makeBouncer(at: CGPoint(x: 256, y: 0))
makeBouncer(at: CGPoint(x: 512, y: 0))
makeBouncer(at: CGPoint(x: 768, y: 0))
makeBouncer(at: CGPoint(x: 1024, y: 0))
// Get label node from scene and store it for use later
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else{return}
let location = touch.location(in: self)
let objects = nodes(at: location)
if objects.contains(editLabel){
editingMode.toggle()
}
else{
if editingMode{
let size = CGSize(width: Int.random(in: 16...128), height: 16)
let box = SKSpriteNode(color: UIColor(red: CGFloat.random(in: 0...1), green: CGFloat.random(in: 0...1), blue: CGFloat.random(in: 0...1), alpha: 1), size: size)
box.zRotation = CGFloat.random(in: 0...3)
box.position = location
box.physicsBody = SKPhysicsBody(rectangleOf: box.size)
box.physicsBody?.isDynamic = false
addChild(box)
}
else{
let ball = SKSpriteNode(imageNamed: "ballRed")
ball.physicsBody = SKPhysicsBody(circleOfRadius: ball.size.width/2.0)
ball.physicsBody?.restitution = 0.4
ball.physicsBody?.contactTestBitMask = ball.physicsBody?.collisionBitMask ?? 0
ball.position = location
ball.name = "ball"
addChild(ball)
}
}
}
func makeBouncer(at position: CGPoint){
let bouncer = SKSpriteNode(imageNamed: "bouncer")
bouncer.position = position
bouncer.physicsBody = SKPhysicsBody(circleOfRadius: bouncer.size.width/2)
bouncer.physicsBody?.isDynamic = false
addChild(bouncer)
}
func makeSlots(at position: CGPoint, isGood: Bool){
var slotBase: SKSpriteNode
var slotGlow: SKSpriteNode
if isGood{
slotBase = SKSpriteNode(imageNamed: "slotBaseGood")
slotGlow = SKSpriteNode(imageNamed: "slotGlowGood")
slotBase.name = "good"
} else{
slotBase = SKSpriteNode(imageNamed: "slotBaseBad")
slotGlow = SKSpriteNode(imageNamed: "slotGlowBad")
slotBase.name = "bad"
}
slotGlow.position = position
slotBase.position = position
slotBase.physicsBody = SKPhysicsBody(rectangleOf: slotBase.size)
slotBase.physicsBody?.isDynamic = false
addChild(slotGlow)
addChild(slotBase)
let spin = SKAction.rotate(byAngle: .pi, duration: 1)
let spinforever = SKAction.repeatForever(spin)
slotGlow.run(spinforever)
}
func collision(between ball: SKNode, object: SKNode){
if object.name == "good"{
destroy(ball: ball)
score+=1
}
else{
if object.name == "bad"{
destroy(ball: ball)
score-=1
}
}
}
func destroy(ball: SKNode){
ball.removeFromParent()
}
func didBegin(_ contact: SKPhysicsContact) {
guard let nodeA = contact.bodyA.node else{return}
guard let nodeB = contact.bodyB.node else{return}
if nodeA.name == "ball"{
collision(between: nodeA, object: nodeB)
}
else if nodeB.name == "ball"{
collision(between: nodeB, object: nodeA)
}
}
}

SpriteKit - didBegin contact is called 30 times instead of 1 time

I am making a little FlappyBird clone and have got everything working as it should until I changed the physics body of the bird to be exact to the texture. Now what it does is when it flies through the gap in the pipes it counts 30 points instead of just 1 point.
This only happens when I use the texture exact physics body which I need because the bird isn't round nor rectangular.
How would I go about making the collision so it only collides once with each gap node. I have tried setting the categoryBitBask to 0 after the contact but then all the gaps after don't add to the point count anymore at all.
Here is the full game code:
var score = 0
class GameScene: SKScene, SKPhysicsContactDelegate {
var bird = SKSpriteNode()
var bg = SKSpriteNode()
var ground = SKSpriteNode()
var scoreLabel = SKLabelNode(fontNamed: "Candice")
var gameOverLabel = SKLabelNode(fontNamed: "Candice")
var countbg = SKSpriteNode()
var timer = Timer()
enum ColliderType: UInt32 {
case Bird = 1
case Object = 2
case Gap = 4
}
var gameOver = false
let swooshSound = SKAction.playSoundFileNamed("sfx_swooshing.wav", waitForCompletion: false)
let pointSound = SKAction.playSoundFileNamed("sfx_point.wav", waitForCompletion: false)
let hitSound = SKAction.playSoundFileNamed("sfx_hit.wav", waitForCompletion: false)
#objc func makePipes() {
let movePipes = SKAction.move(by: CGVector(dx: -2 * self.frame.width, dy: 0), duration: TimeInterval(self.frame.width / 150))
let removePipes = SKAction.removeFromParent()
let moveAndRemovePipes = SKAction.sequence([movePipes, removePipes])
let gapHeight = bird.size.height * 2.8
let movementAmount = arc4random() % UInt32(self.frame.height) / 2
let pipeOffset = CGFloat(movementAmount) - self.frame.height / 4
let pipeTexture = SKTexture(imageNamed: "pipe1.png")
let pipe1 = SKSpriteNode(texture: pipeTexture)
pipe1.position = CGPoint(x: self.frame.midX + self.frame.width, y: self.frame.midY + pipeTexture.size().height / 2 + gapHeight / 2 + pipeOffset)
pipe1.zPosition = 2
pipe1.run(moveAndRemovePipes)
pipe1.physicsBody = SKPhysicsBody(rectangleOf: pipeTexture.size())
pipe1.physicsBody!.isDynamic = false
pipe1.physicsBody!.contactTestBitMask = ColliderType.Object.rawValue
pipe1.physicsBody!.categoryBitMask = ColliderType.Object.rawValue
pipe1.physicsBody!.collisionBitMask = ColliderType.Object.rawValue
self.addChild(pipe1)
let pipe2Texture = SKTexture(imageNamed: "pipe2.png")
let pipe2 = SKSpriteNode(texture: pipe2Texture)
pipe2.position = CGPoint(x: self.frame.midX + self.frame.width, y: self.frame.midY - pipeTexture.size().height / 2 - gapHeight / 2 + pipeOffset)
pipe2.zPosition = 2
pipe2.run(moveAndRemovePipes)
pipe2.physicsBody = SKPhysicsBody(rectangleOf: pipe2Texture.size())
pipe2.physicsBody!.isDynamic = false
pipe2.physicsBody!.contactTestBitMask = ColliderType.Object.rawValue
pipe2.physicsBody!.categoryBitMask = ColliderType.Object.rawValue
pipe2.physicsBody!.collisionBitMask = ColliderType.Object.rawValue
self.addChild(pipe2)
let gap = SKNode()
gap.position = CGPoint(x: self.frame.midX + self.frame.width, y: self.frame.midY + pipeOffset)
gap.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 1, height: gapHeight))
gap.physicsBody!.isDynamic = false
gap.run(moveAndRemovePipes)
gap.physicsBody!.contactTestBitMask = ColliderType.Bird.rawValue
gap.physicsBody!.categoryBitMask = ColliderType.Gap.rawValue
gap.physicsBody!.collisionBitMask = ColliderType.Gap.rawValue
self.addChild(gap)
}
func didBegin(_ contact: SKPhysicsContact) {
if gameOver == false {
if contact.bodyA.categoryBitMask == ColliderType.Gap.rawValue || contact.bodyB.categoryBitMask == ColliderType.Gap.rawValue {
score += 1
scoreLabel.text = String(format: "%05d", score)
run(pointSound)
} else {
self.speed = 0
run(hitSound)
gameOver = true
timer.invalidate()
bird.removeFromParent()
let changeSceneAction = SKAction.run(changeScene)
self.run(changeSceneAction)
}
}
}
//MARK: Change to Game Over Scene
func changeScene(){
let sceneToMoveTo = GameOverScene(size: self.size)
sceneToMoveTo.scaleMode = self.scaleMode
let myTransition = SKTransition.fade(withDuration: 0.5)
self.view!.presentScene(sceneToMoveTo, transition: myTransition)
}
override func didMove(to view: SKView) {
self.physicsWorld.contactDelegate = self
setupGame()
}
func setupGame() {
timer = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(self.makePipes), userInfo: nil, repeats: true)
let groundTexture = SKTexture(imageNamed: "ground.png")
let moveGroundAnimation = SKAction.move(by: CGVector(dx: -groundTexture.size().width, dy: 0), duration: 7)
let shiftGroundAnimation = SKAction.move(by: CGVector(dx: groundTexture.size().width, dy: 0), duration: 0)
let moveGroundForever = SKAction.repeatForever(SKAction.sequence([moveGroundAnimation, shiftGroundAnimation]))
var i: CGFloat = 0
while i < 3 {
ground = SKSpriteNode(texture: groundTexture)
ground.position = CGPoint(x: self.size.width * i, y: self.size.height / 7.65)
ground.zPosition = 3
ground.run(moveGroundForever)
self.addChild(ground)
i += 1
}
let bottom = SKNode()
bottom.position = CGPoint(x: self.frame.midX, y: self.size.height / 7)
bottom.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: self.frame.width, height: 1))
bottom.physicsBody!.isDynamic = false
bottom.physicsBody!.contactTestBitMask = ColliderType.Object.rawValue
bottom.physicsBody!.categoryBitMask = ColliderType.Object.rawValue
bottom.physicsBody!.collisionBitMask = ColliderType.Object.rawValue
self.addChild(bottom)
let bgTexture = SKTexture(imageNamed: "bg.png")
bg = SKSpriteNode(texture: bgTexture)
bg.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
bg.size = self.frame.size
bg.zPosition = 1
self.addChild(bg)
let birdTexture = SKTexture(imageNamed: "flappy1.png")
let bird2Texture = SKTexture(imageNamed: "flappy2.png")
let bird3Texture = SKTexture(imageNamed: "flappy3.png")
let bird4Texture = SKTexture(imageNamed: "flappy4.png")
let bird5Texture = SKTexture(imageNamed: "flappy5.png")
let bird6Texture = SKTexture(imageNamed: "flappy6.png")
let animation = SKAction.animate(with: [birdTexture, bird2Texture, bird3Texture, bird4Texture, bird5Texture, bird6Texture], timePerFrame: 0.1)
let makeBirdFlap = SKAction.repeatForever(animation)
bird = SKSpriteNode(texture: birdTexture)
bird.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
bird.setScale(1)
bird.zPosition = 6
bird.run(makeBirdFlap)
self.addChild(bird)
bird.physicsBody = SKPhysicsBody.init(circleOfRadius: birdTexture.size().height / 2)
//bird.physicsBody = SKPhysicsBody(texture: birdTexture, size: birdTexture.size())
bird.physicsBody!.isDynamic = false
bird.physicsBody!.contactTestBitMask = ColliderType.Object.rawValue
bird.physicsBody!.categoryBitMask = ColliderType.Bird.rawValue
bird.physicsBody!.collisionBitMask = ColliderType.Bird.rawValue
let countbg = SKSpriteNode(imageNamed: "count_bg.png")
countbg.position = CGPoint(x: self.size.width / 4.8, y: self.size.height * 0.94)
countbg.setScale(0.8)
countbg.zPosition = 4
addChild(countbg)
scoreLabel.fontSize = 80
scoreLabel.text = String(format: "%05d", score)
scoreLabel.fontColor = SKColor(red: 218/255, green: 115/255, blue: 76/255, alpha: 1)
scoreLabel.position = CGPoint(x: self.size.width / 4, y: self.size.height * 0.94)
scoreLabel.zPosition = 5
addChild(scoreLabel)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if gameOver == false {
bird.physicsBody!.isDynamic = true
bird.physicsBody!.velocity = CGVector(dx: 0, dy: 0)
bird.physicsBody!.applyImpulse(CGVector(dx: 0, dy: 280))
//run(swooshSound)
} else {
gameOver = false
score = 0
self.speed = 1
self.removeAllChildren()
setupGame()
}
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}
If you would be using RxSwift, you would be able to easily get rid of those extra events easily by using debounce() or throttle() or distinctUntilChanged(). If you are willing to try this approach, give RxSpriteKit framework a go. Otherwise, store a timestamp of the last contact and ignore the following contacts until some time period elapses.

Looping my code so that the user can continue to play

import SpriteKit
import GameplayKit
class GameScene: SKScene, SKPhysicsContactDelegate {
let balls = [
SKSpriteNode(imageNamed: "blueball.png"),
SKSpriteNode(imageNamed: "greenball.png"),
SKSpriteNode(imageNamed: "realredball.png"),
]
let redRectangle = SKSpriteNode(imageNamed: "redrectangle.png")
let blueRectangle = SKSpriteNode(imageNamed: "bluerectangle.png")
let greenRectangle = SKSpriteNode(imageNamed: "greenrectangle.png")
let wall1 = SKSpriteNode(imageNamed: "drop_wall.png")
let wall2 = SKSpriteNode(imageNamed: "drop_wall.png")
let bottom = SKSpriteNode(imageNamed:"drop_bottom.png")
let top = SKSpriteNode(imageNamed:"drop_bottom.png")
let blueBallCategory :UInt32 = 0x1 << 0
let greenBallCategory :UInt32 = 0x1 << 1
let realRedBallCategory :UInt32 = 0x1 << 2
let redRectangleCategory : UInt32 = 0x1 << 3
let blueRectangleCategory : UInt32 = 0x1 << 4
let greenRectangleCategory : UInt32 = 0x1 << 5
override func didMove(to view: SKView) {
spawnBalls()
spawnRectangles()
physicsWorld.contactDelegate = self
moveRectangles()
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for ball in balls{
ball.isUserInteractionEnabled = false
}
physics()
}
func didBegin(_ contact: SKPhysicsContact) {
let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
switch contactMask {
case blueBallCategory | blueRectangleCategory:
for ball in balls{
ball.removeFromParent()
}
print("Alive! Blue ball has hit blue rectangle.")
case greenBallCategory | greenRectangleCategory:
print("Alive! Green ball has hit green rectangle.")
case realRedBallCategory | redRectangleCategory:
print("Alive! Red ball has hit red rectangle.")
case blueBallCategory | redRectangleCategory:
print("dead")
case blueBallCategory | greenRectangleCategory:
print("dead")
case realRedBallCategory | blueRectangleCategory:
print("dead")
case realRedBallCategory | greenRectangleCategory:
print("dead")
case greenBallCategory | redRectangleCategory:
print("dead")
case greenBallCategory | blueRectangleCategory:
print("dead")
default:
print("missed")
}
}
func spawnRectangles() {
redRectangle.position = CGPoint(x: 0, y: -400)
redRectangle.size = CGSize(width: 200, height: 20)
blueRectangle.position = CGPoint(x: -300, y: -200)
blueRectangle.size = CGSize(width: 200, height: 20)
greenRectangle.position = CGPoint(x: 100, y: -550)
greenRectangle.size = CGSize(width: 200, height: 20)
self.addChild(redRectangle)
self.addChild(blueRectangle)
self.addChild(greenRectangle)
wall1.position = CGPoint(x: -367.04, y: 0)
wall1.size = CGSize(width: 20, height: 1350)
wall1.physicsBody = SKPhysicsBody(rectangleOf: wall1.size)
wall1.physicsBody?.isDynamic = false
wall1.physicsBody?.affectedByGravity = false
self.addChild(wall1)
wall2.position = CGPoint(x: 367.04, y: 0)
wall2.size = CGSize(width: 20, height: 1350)
wall2.physicsBody = SKPhysicsBody(rectangleOf: wall2.size)
wall2.physicsBody?.isDynamic = false
wall2.physicsBody?.affectedByGravity = false
self.addChild(wall2)
top.position = CGPoint(x: 0, y: 657)
top.size = CGSize(width: 765, height: 20)
top.physicsBody = SKPhysicsBody(rectangleOf: top.size)
top.physicsBody?.isDynamic = false
top.physicsBody?.affectedByGravity = false
self.addChild(top)
bottom.size = CGSize(width: 765, height: 20)
bottom.position = CGPoint(x: 0, y: -657)
bottom.physicsBody = SKPhysicsBody(rectangleOf: bottom.size)
bottom.physicsBody?.isDynamic = false
bottom.physicsBody?.affectedByGravity = false
self.addChild(bottom)
}
func physics(){
for ball in balls{
ball.physicsBody = SKPhysicsBody(circleOfRadius: ball.size.height/2)
ball.physicsBody?.contactTestBitMask = blueRectangleCategory | greenRectangleCategory | redRectangleCategory
}
redRectangle.physicsBody = SKPhysicsBody(rectangleOf: redRectangle.size)
redRectangle.physicsBody?.affectedByGravity = false
redRectangle.physicsBody?.isDynamic = false
blueRectangle.physicsBody = SKPhysicsBody(rectangleOf: redRectangle.size)
blueRectangle.physicsBody?.affectedByGravity = false
blueRectangle.physicsBody?.isDynamic = false
greenRectangle.physicsBody = SKPhysicsBody(rectangleOf: redRectangle.size)
greenRectangle.physicsBody?.isDynamic = false
greenRectangle.physicsBody?.affectedByGravity = false
balls[0].physicsBody?.categoryBitMask = blueBallCategory
balls[1].physicsBody?.categoryBitMask = greenBallCategory
balls[2].physicsBody?.categoryBitMask = realRedBallCategory
redRectangle.physicsBody?.categoryBitMask = redRectangleCategory
blueRectangle.physicsBody?.categoryBitMask = blueRectangleCategory
greenRectangle.physicsBody?.categoryBitMask = greenRectangleCategory
}
func moveRectangles(){
let redMoveRight = SKAction.moveTo(x: 300, duration: 2)
let redMoveLeft = SKAction.moveTo(x: -280, duration: 2)
let redWholeMovement = SKAction.repeatForever(SKAction.sequence([redMoveRight,redMoveLeft]))
redRectangle.run(redWholeMovement)
let blueMoveRight = SKAction.moveTo(x: 300, duration: 2)
let blueMoveLeft = SKAction.moveTo(x: -280, duration: 1.5)
let blueWholeMovement = SKAction.repeatForever(SKAction.sequence([blueMoveRight,blueMoveLeft]))
blueRectangle.run(blueWholeMovement)
let greenMoveRight = SKAction.moveTo(x: 300, duration: 2)
let greenMoveLeft = SKAction.moveTo(x: -280, duration: 1.5)
let greenWholeMovement = SKAction.repeatForever(SKAction.sequence([greenMoveLeft,greenMoveRight]))
greenRectangle.run(greenWholeMovement)
}
func spawnBalls(){
let ball = balls[Int(arc4random_uniform(UInt32(balls.count)))]
ball.position = CGPoint(x: 0, y: 250)
ball.size = CGSize(width: 70, height: 70)
self.addChild(ball)
}
}
I want a new ball to spawn at the top of the screen if a ball hits a same colored rectangle. Right now when I run the app a randomly colored ball is spawned at the top and when the user clicks the ball drops. If the ball makes contact with a moving rectangle of the same color of the ball the game is supposed to keep going. But it just ends after. If anyone could help that would be great.Thanks!
You only spawn one ball in spawnBalls(). So when you touch only one ball appears. You should try to move spawnBalls() to touchesBegan().

How can I make my level menu scrollable vertically?

I have the following level Menu (as seen below). I would like to make it vertically scrollable, resulting in a total height double that of the screen (full scroll height). How can I achieve this?
Below is the code for the image above:
class LevelMenu: SKScene {
let levelButtonSize = SKSpriteNode(imageNamed: "b1").size
let levelButton1: SKSpriteNode = SKSpriteNode(imageNamed: "b1")
let levelButton2: SKSpriteNode = SKSpriteNode(imageNamed: "b2")
let levelButton3: SKSpriteNode = SKSpriteNode(imageNamed: "b3")
let levelButton4: SKSpriteNode = SKSpriteNode(imageNamed: "b4")
let levelButton5: SKSpriteNode = SKSpriteNode(imageNamed: "b5")
let levelButton6: SKSpriteNode = SKSpriteNode(imageNamed: "b6")
let levelButton7: SKSpriteNode = SKSpriteNode(imageNamed: "b7")
let levelButton8: SKSpriteNode = SKSpriteNode(imageNamed: "b8")
let levelButton9: SKSpriteNode = SKSpriteNode(imageNamed: "b9")
let levelButton10: SKSpriteNode = SKSpriteNode(imageNamed: "b10")
let levelButton11: SKSpriteNode = SKSpriteNode(imageNamed: "b11")
let levelButton12: SKSpriteNode = SKSpriteNode(imageNamed: "b12")
override init(size: CGSize){
super.init(size: size)
let bg = SKSpriteNode(imageNamed: "bg")
backgroundImage.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
self.addChild(bg)
let column1PosX = levelButtonSize.width*cDiff
let column2PosX = levelButtonSize.width*cDiff + levelButtonSize.width*2.0
let column3PosX = levelButtonSize.width*cDiff + levelButtonSize.width*4.0
let row1PosY = self.frame.height - levelButtonSize.width*1.5
let row2PosY = row1PosY - levelButtonSize.height - levelButtonSize.width*rDiff
let row3PosY = row2PosY - levelButtonSize.height - levelButtonSize.width*rDiff
let row4PosY = row3PosY - levelButtonSize.height - levelButtonSize.width*rDiff
levelButton1.position = CGPoint(x: column1PosX, y: row1PosY)
levelButton1.zPosition = 10
self.addChild(levelButton1)
levelButton2.position = CGPoint(x: column2PosX, y: row1PosY)
self.addChild(levelButton2)
levelButton3.position = CGPoint(x: column3PosX, y: row1PosY)
self.addChild(levelButton3)
levelButton4.position = CGPoint(x: column1PosX, y: row2PosY)
self.addChild(levelButton4)
levelButton5.position = CGPoint(x: column2PosX, y: row2PosY)
self.addChild(levelButton5)
levelButton6.position = CGPoint(x: column3PosX, y: row2PosY)
self.addChild(levelButton6)
levelButton7.position = CGPoint(x: column1PosX, y: row3PosY)
self.addChild(levelButton7)
levelButton8.position = CGPoint(x: column2PosX, y: row3PosY)
self.addChild(levelButton8)
levelButton9.position = CGPoint(x: column3PosX, y: row3PosY)
self.addChild(levelButton9)
levelButton10.position = CGPoint(x: column1PosX, y: row4PosY)
self.addChild(levelButton10)
levelButton11.position = CGPoint(x: column2PosX, y: row4PosY)
self.addChild(levelButton11)
levelButton12.position = CGPoint(x: column3PosX, y: row4PosY)
self.addChild(levelButton12)
}
UPDATE
Based on Ron Myschuk's solution, the code below show's what I've been able to achieve and this link shows a .gif of the issue I am having currently, where the screen scrolls too much at the top of the menu.
class LMScene: SKScene {
let levelButtonSize = SKSpriteNode(imageNamed: "b1").size
let levelButton1: SKSpriteNode = SKSpriteNode(imageNamed: "b1")
let levelButton2: SKSpriteNode = SKSpriteNode(imageNamed: "b2")
let levelButton3: SKSpriteNode = SKSpriteNode(imageNamed: "b3")
let levelButton4: SKSpriteNode = SKSpriteNode(imageNamed: "b4")
let levelButton5: SKSpriteNode = SKSpriteNode(imageNamed: "b5")
let levelButton6: SKSpriteNode = SKSpriteNode(imageNamed: "b6")
let levelButton7: SKSpriteNode = SKSpriteNode(imageNamed: "b7")
let levelButton8: SKSpriteNode = SKSpriteNode(imageNamed: "b8")
let levelButton9: SKSpriteNode = SKSpriteNode(imageNamed: "b9")
let levelButton10: SKSpriteNode = SKSpriteNode(imageNamed: "b10")
let levelButton11: SKSpriteNode = SKSpriteNode(imageNamed: "b11")
let levelButton12: SKSpriteNode = SKSpriteNode(imageNamed: "b12")
let levelButton13: SKSpriteNode = SKSpriteNode(imageNamed: "b13")
let levelButton14: SKSpriteNode = SKSpriteNode(imageNamed: "b14")
let levelButton15: SKSpriteNode = SKSpriteNode(imageNamed: "b15")
let levelButton16: SKSpriteNode = SKSpriteNode(imageNamed: "b16")
let levelButton17: SKSpriteNode = SKSpriteNode(imageNamed: "b17")
let levelButton18: SKSpriteNode = SKSpriteNode(imageNamed: "b18")
private var scrollCell = SKSpriteNode()
private var moveAmtX: CGFloat = 0
private var moveAmtY: CGFloat = 0
private let minimum_detect_distance: CGFloat = 30
private var initialPosition: CGPoint = CGPoint.zero
private var initialTouch: CGPoint = CGPoint.zero
private var resettingSlider = false
override init(size: CGSize){
super.init(size: size)
scrollCell = SKSpriteNode(color: .blue, size: CGSize(width: self.size.width, height: 2*self.size.height - self.frame.width*0.24734))
scrollCell.position = CGPoint(x: 0, y: 0)
scrollCell.anchorPoint = CGPoint.zero
scrollCell.zPosition = 0
self.addChild(scrollCell)
let backgroundImage = SKSpriteNode(imageNamed: "bg")
backgroundImage.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
self.addChild(backgroundImage)
let column1PosX = levelButtonSize.width/2 + self.frame.width*0.14855
let column2PosX = 3*levelButtonSize.width/2 + 2*self.frame.width*0.14855
let column3PosX = 5*levelButtonSize.width/2 + 3*self.frame.width*0.14855
let row1PosY = self.frame.height - levelButtonSize.height/2 - self.frame.width*0.24734
let row2PosY = row1PosY - levelButtonSize.height - self.frame.width*0.24734
let row3PosY = row2PosY - levelButtonSize.height - self.frame.width*0.24734
let row4PosY = row3PosY - levelButtonSize.height - self.frame.width*0.24734
let row5PosY = row4PosY - levelButtonSize.height - self.frame.width*0.24734
let row6PosY = row5PosY - levelButtonSize.height - self.frame.width*0.24734
levelButton1.position = CGPoint(x: column1PosX, y: row1PosY)
levelButton1.zPosition = 10
scrollCell.addChild(levelButton1)
levelButton2.position = CGPoint(x: column2PosX, y: row1PosY)
levelButton2.zPosition = 10
scrollCell.addChild(levelButton2)
levelButton3.position = CGPoint(x: column3PosX, y: row1PosY)
levelButton3.zPosition = 10
scrollCell.addChild(levelButton3)
levelButton4.position = CGPoint(x: column1PosX, y: row2PosY)
levelButton4.zPosition = 10
scrollCell.addChild(levelButton4)
levelButton5.position = CGPoint(x: column2PosX, y: row2PosY)
levelButton5.zPosition = 10
scrollCell.addChild(levelButton5)
levelButton6.position = CGPoint(x: column3PosX, y: row2PosY)
levelButton6.zPosition = 10
scrollCell.addChild(levelButton6)
levelButton7.position = CGPoint(x: column1PosX, y: row3PosY)
levelButton7.zPosition = 10
scrollCell.addChild(levelButton7)
levelButton8.position = CGPoint(x: column2PosX, y: row3PosY)
levelButton8.zPosition = 10
scrollCell.addChild(levelButton8)
levelButton9.position = CGPoint(x: column3PosX, y: row3PosY)
levelButton9.zPosition = 10
scrollCell.addChild(levelButton9)
levelButton10.position = CGPoint(x: column1PosX, y: row4PosY)
levelButton10.zPosition = 10
scrollCell.addChild(levelButton10)
levelButton11.position = CGPoint(x: column2PosX, y: row4PosY)
levelButton11.zPosition = 10
scrollCell.addChild(levelButton11)
levelButton12.position = CGPoint(x: column3PosX, y: row4PosY)
levelButton12.zPosition = 10
scrollCell.addChild(levelButton12)
levelButton13.position = CGPoint(x: column1PosX, y: row5PosY)
levelButton13.zPosition = 10
scrollCell.addChild(levelButton13)
levelButton14.position = CGPoint(x: column2PosX, y: row5PosY)
levelButton14.zPosition = 10
scrollCell.addChild(levelButton14)
levelButton15.position = CGPoint(x: column3PosX, y: row5PosY)
levelButton15.zPosition = 10
scrollCell.addChild(levelButton15)
levelButton16.position = CGPoint(x: column1PosX, y: row6PosY)
levelButton16.zPosition = 10
scrollCell.addChild(levelButton16)
levelButton17.position = CGPoint(x: column2PosX, y: row6PosY)
levelButton17.zPosition = 10
scrollCell.addChild(levelButton17)
levelButton18.position = CGPoint(x: column3PosX, y: row6PosY)
levelButton18.zPosition = 10
scrollCell.addChild(levelButton18)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first as UITouch! {
if let touch = touches.first as UITouch! {
self.scrollCell.removeAllActions()
initialTouch = touch.location(in: self.scene!.view)
moveAmtY = 0
initialPosition = self.scrollCell.position
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first as UITouch! {
let movingPoint: CGPoint = touch.location(in: self.scene!.view)
moveAmtY = movingPoint.y - initialTouch.y
scrollCell.position = CGPoint(x: initialPosition.x, y: initialPosition.y - moveAmtY)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
checkForResettingSlider()
yMoveActions(moveTo: -moveAmtY)
}
func checkForResettingSlider() {
if resettingSlider { return }
let scrollDif: CGFloat = (scrollCell.size.height - self.size.height) / 2.0
if scrollCell.position.y > scrollDif {
let move: SKAction = SKAction.moveTo(y: scrollDif, duration: 0.3)
move.timingMode = .easeOut
scrollCell.run(move, completion: { self.resettingSlider = false })
}
if scrollCell.position.y < -scrollDif {
let move: SKAction = SKAction.moveTo(y: 0 - scrollDif, duration: 0.3)
move.timingMode = .easeOut
scrollCell.run(move, completion: { self.resettingSlider = false })
}
}
func yMoveActions(moveTo: CGFloat) {
let move: SKAction = SKAction.moveBy(x: 0, y: (moveTo * 1.5), duration: 0.3)
move.timingMode = .easeOut
self.scrollCell.run(move, completion: { self.checkForResettingSlider() })
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Here is some code that I use to scroll vertically, I've adapted it to fit your menu. It doesn't perfectly line up to all your items, but it'll give you somewhere to start. And it'll show you how to figure this out on your own.
EDIT: I've updated the code use these funds instead. Still declare your buttons the same way but call my createMenu func to actually create the menu. It is looped so it auto adjusts if you change the number of menu items. the only thing you have to be aware of is; if you add or remove buttons change the array at the top of createMenu accordingly. Also adjust the padding variable to how much vertical space you want between the items
func createMenu() {
let buttons = [levelButton1, levelButton2, levelButton3, levelButton4, levelButton5, levelButton6, levelButton7, levelButton8, levelButton9, levelButton10, levelButton11, levelButton12, levelButton13, levelButton14, levelButton15, levelButton16, levelButton17, levelButton18]
let padding: CGFloat = 400
let numberOfRows = CGFloat(buttons.count / 3)
scrollCell = SKSpriteNode(color: .blue, size: CGSize(width: 1024, height: levelButtonSize.height * numberOfRows + padding * numberOfRows))
scrollCell.position = CGPoint(x: 0 - self.size.width / 4, y: 0 - (scrollCell.size.height - self.size.height / 2))
scrollCell.anchorPoint = CGPoint.zero
scrollCell.zPosition = 0
self.addChild(scrollCell)
// let backgroundImage = SKSpriteNode(imageNamed: "bg")
// backgroundImage.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
// self.addChild(backgroundImage)
let column1PosX = scrollCell.size.width / 3 / 2
let column2PosX = scrollCell.size.width / 2
let column3PosX = scrollCell.size.width / 3 / 2 + scrollCell.size.width / 3 * 2
var colCount = 0
var rowCount = 0
for button in buttons {
var posX: CGFloat = column2PosX
if colCount == 0 {
posX = column1PosX
}
else if colCount == 2 {
posX = column3PosX
colCount = -1
}
let indexOffset = CGFloat(rowCount) * (levelButtonSize.height + padding)
let posY = scrollCell.size.height - levelButtonSize.height / 2 - (indexOffset + padding / 2)
button.position = CGPoint(x: posX, y: posY)
button.setScale(0.5)
button.zPosition = 10
scrollCell.addChild(button)
if colCount == -1 {
rowCount += 1
}
colCount += 1
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first as UITouch! {
self.scrollCell.removeAllActions()
initialTouch = touch.location(in: self.scene!.view)
moveAmtY = 0
initialPosition = self.scrollCell.position
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first as UITouch! {
let movingPoint: CGPoint = touch.location(in: self.scene!.view)
moveAmtY = movingPoint.y - initialTouch.y
scrollCell.position = CGPoint(x: initialPosition.x, y: initialPosition.y - moveAmtY)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
checkForResettingSlider()
yMoveActions(moveTo: -moveAmtY)
}
func checkForResettingSlider() {
let topPos: CGFloat = scrollCell.size.height - self.size.height / 2
let bottomPos = 0 - (self.size.height / 2)
if scrollCell.position.y > bottomPos {
let move = SKAction.moveTo(y: bottomPos, duration: 0.3)
move.timingMode = .easeOut
scrollCell.run(move)
}
if scrollCell.position.y < -topPos {
let move = SKAction.moveTo(y: -topPos, duration: 0.3)
move.timingMode = .easeOut
scrollCell.run(move)
}
}
func yMoveActions(moveTo: CGFloat) {
let move = SKAction.moveBy(x: 0, y: (moveTo * 1.5), duration: 0.3)
move.timingMode = .easeOut
self.scrollCell.run(move, completion: { self.checkForResettingSlider() })
}

How to make text appear on button? -SpriteKit

I am new to SpriteKit, and I am creating a game where I have designed the main menu with sketch and apply the image to spritekit as my background image. Everytime I put a label/text above the button images. It doesn't work properly. When they tap on play lets say, they will play and when they lose, and start over all of sudden the labels disappear.
Here is an image:
Here is my code
//creating the start game programmatically.
let dw_startButton = SKSpriteNode()
dw_startButton.name = "dw_startbutton"
dw_startButton.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
self.addChild(dw_startButton)
//Instructions Button
let dw_selector = SKSpriteNode()
dw_selector.name = "dw_selector"
dw_selector.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
self.addChild(dw_selector)
//Starting text
let startText = SKLabelNode(text: "Play!")
startText.fontColor = UIColor.white
startText.position = CGPoint(x: 50, y: 50)
startText.fontSize = 45
startText.fontName = "Helvetica-Bold"
startText.verticalAlignmentMode = SKLabelVerticalAlignmentMode(rawValue: 1)!
startText.name = "dw_startbutton"
dw_startButton.addChild(startText)
//instructions text
let startTexts = SKLabelNode(text: "Insturctions")
startTexts.fontColor = UIColor.white
startTexts.position = CGPoint(x: 0, y: 0)
startTexts.fontSize = 20
startTexts.fontName = "Helvetica-Bold"
startTexts.verticalAlignmentMode = SKLabelVerticalAlignmentMode(rawValue: 1)!
startTexts.name = "dw_selector"
dw_selector.addChild(startTexts)
You always need to add zPosition to your sprites and labels. Otherwise sometimes they will appear and other times they may appear behind the background
//creating the start game programmatically.
let dw_startButton = SKSpriteNode()
dw_startButton.zPosition = 1
dw_startButton.name = "dw_startbutton"
dw_startButton.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
self.addChild(dw_startButton)
//Instructions Button
let dw_selector = SKSpriteNode()
dw_selector.name = "dw_selector"
dw_selector.zPosition = 1
dw_selector.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
self.addChild(dw_selector)
//Starting text
let startText = SKLabelNode(text: "Play!")
startText.fontColor = UIColor.white
startText.position = CGPoint(x: 50, y: 50)
startText.fontSize = 45
startText.fontName = "Helvetica-Bold"
startText.verticalAlignmentMode = SKLabelVerticalAlignmentMode(rawValue: 1)!
startText.name = "dw_startbutton"
startText.zPosition = 2
dw_startButton.addChild(startText)
//instructions text
let startTexts = SKLabelNode(text: "Insturctions")
startTexts.fontColor = UIColor.white
startTexts.position = CGPoint(x: 0, y: 0)
startTexts.fontSize = 20
startTexts.fontName = "Helvetica-Bold"
startTexts.verticalAlignmentMode = SKLabelVerticalAlignmentMode(rawValue: 1)!
startTexts.name = "dw_selector"
startTexts.zPosition = 2
dw_selector.addChild(startTexts)
//new button
let newButton = SKSpriteNode(imageNamed: "button_back")
newButton.zPosition = 1
newButton.name = "button1"
newButton.position = CGPoint(x: 100, y: 100)
self.addChild(newButton)
let buttonText = SKLabelNode(text: "Play")
buttonText.fontColor = .white
buttonText.fontSize = 20
buttonText.fontName = "Helvetica-Bold"
buttonText.verticalAlignmentMode = .center
buttonText.horizontalAlignmentMode = .center
buttonText.name = "dw_selector"
buttonText.zPosition = 2
newButton.addChild(buttonText)