I'm creating my first Game and it crashes after come back from GameOverScene. This are the part that gives me the error:
// GameScene.swift
// Test_Crash_1
import SpriteKit
struct global {
static var wheelRotPlat2 = SKShapeNode(circleOfRadius: 400)
static var button : SKShapeNode = SKShapeNode()
static var actualPlayer : String = String()
static let btnNextPlayer = SKShapeNode(circleOfRadius: 70)
}
class GameScene: SKScene {
deinit {
print("The GameScene has been removed from memory")
}
override func didMoveToView(view: SKView) {
let buttonDice = SKShapeNode(rectOfSize: (CGSizeMake(40,40)), cornerRadius: 5)
global.button = buttonDice
global.button.position = CGPoint(x: (CGRectGetMidX(self.frame)-400), y:(CGRectGetMidY(self.frame)-350))
global.button.fillColor = UIColor(red: 0.2, green: 1.0, blue: 0.2, alpha: 0.8)
global.button.name = "button"
global.button.zPosition = 1
addChild(global.button)
global.wheelRotPlat2.addChild(global.btnNextPlayer)
global.btnNextPlayer.name = "btnNextPlayer"
} // end didMoveToView
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
//func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches //as! Set<UITouch>
let location = touch.first!.locationInNode(self)
let node = self.nodeAtPoint(location)
if (node.name == "button") {
self.removeAllActions()
self.removeAllChildren()
let reveal = SKTransition.flipHorizontalWithDuration(0.5)
let gameOverScene = GameOverScene(size: self.size, player: global.actualPlayer)
self.view?.presentScene(gameOverScene, transition: reveal)
}
} // End func touchesBegan
} // end GameScene
And the GameOverScene file:
// GameOverScene.swift
// Test_Crash_1
import Foundation
import SpriteKit
class GameOverScene: SKScene {
init(size: CGSize, player: String) {
super.init(size: size)
backgroundColor = SKColor.whiteColor()
runAction(SKAction.sequence([
SKAction.waitForDuration(3.0),
SKAction.runBlock() {
let reveal = SKTransition.flipHorizontalWithDuration(0.5)
let scene = GameScene(size: size)
self.view?.presentScene(scene, transition:reveal)
}
]))
} // end init(size
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
} // end GameOverScene
The error is well the known:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attemped to add a SKNode which already has a parent: SKShapeNode name:'btnNextPlayer'
This example has just a green button that takes me to the GameOverScene. When it returns the game crashes.
I don't understand why it happens in this case.
Can anyone tell me why this happens and how to fix it?
The error tells you what is happening, you are adding a node to another node, but the node already has a parent. This is why using globals is a bad idea and should almost never be used. You have a button that is global. It is attached to GameScene, lets call GameScene1. You then call Gameover. Now you call a new GameScene, called GameScene2. Since you are not managing your memory correctly because of things like global variables, GameScene1 still exists. Well button is still attached to GameScene1, so when you try to add it to GameScene2, XCode says "No, this node has a parent, go fix yo stuff". Try to not use global data, and you should see less problems like this.
Edit:
As #Whirlwind pointed out, the issue is in the global item global.wheelRotPlat2 that is causing the problem.
global.wheelRotPlat2.addChild(global.btnNextPlayer) is being called every time a GameScene is moved to a view, and since you never remove the btnNextPlayer from its parent when you kill your 1st Scene, when you create your 2nd Scene, the global.btnNextPlayer will try to reattach to the same exact parent again (global.wheelRotPlat2), which is causing your problem.
Related
I have a spriteKit project that brings sprites to the scene by loading them from an sks file. I know that they are loading correctly because I can perform other functions on them. In the simulator they even run SKActions, but on an actual device (iPad pro gen 2) they do not run those actions. I made the following test program that illustrates the problem.
The question specifically is why don't sprites that have been loaded from an SKS file run SKActions on a real device?
import SpriteKit
import GameplayKit
class GameScene: SKScene {
private var fileDropSite: SKSpriteNode!
var lessonNode: SKSpriteNode?
var lessonButton: SKSpriteNode?
override func didMove(to view: SKView) {
fileDropSite = childNode(withName: "fileDropSite") as! SKSpriteNode
}
func bringSKSTOLoad() {
fileDropSite.removeAllChildren()
let lessonToLoad = SKSpriteNode(fileNamed: "practiceLesson")?.childNode(withName: "unitNode")
lessonToLoad?.move(toParent: fileDropSite)
lessonNode = fileDropSite.childNode(withName: "//lessonNode") as? SKSpriteNode
lessonButton = fileDropSite.childNode(withName: "//lessonButton") as? SKSpriteNode
print("lessonLoaded")
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let touchLocation = touch.location(in: self)
let touchedNode = atPoint(touchLocation)
if let nodesName = touchedNode.name {
if nodesName == "fileDropSite" {
print("fileDropSite TOuched")
bringSKSTOLoad()
}
if nodesName == "lessonButton" {
print("lessonButton TOuched")
lessonNode?.run(SKAction.repeatForever(SKAction.sequence([SKAction.moveBy(x: -100, y: 0, duration: 0.5), SKAction.moveBy(x: 100, y: 0, duration: 0.5)])))
}
}
}
}
to clarify: fileDropSite is a child sprite in the gamescene, lessonNode and lessonButton are sprites in the file named practiceLesson and are children of the sprite named unitNode. The following code initializes these as variables and upon touching the fileDropSite sprite, it then loads the sprites in the file to the scene via the fileDropSite(child of the scene), and initializes them. There is a touch function that causes the lessonNode and its child to shake back and forth on the simulator but not on a rea device.
try...
override func didMove(to view: SKView) {
fileDropSite = childNode(withName: "fileDropSite") as! SKSpriteNode
self.isPaused = false
}
as of iOS11 Apple has made the default state for scenes and SKSpriteNodes to be paused. Generally running self.isPaused = false on the scene works for me.
and if need be try...
func bringSKSTOLoad() {
fileDropSite.removeAllChildren()
let lessonToLoad = SKSpriteNode(fileNamed: "practiceLesson")?.childNode(withName: "unitNode")
lessonToLoad?.move(toParent: fileDropSite)
lessonNode = fileDropSite.childNode(withName: "//lessonNode") as? SKSpriteNode
lessonButton = fileDropSite.childNode(withName: "//lessonButton") as? SKSpriteNode
print("lessonLoaded")
lessonToLoad.isPaused = false
lessonNode.isPaused = false
lessonButton.isPaused = false
}
I want to build an inventory for my SpriteKit game. For this I want to go to another SKScene File which represents my inventory when I press the pause button. My problem is that when I make a transition from my InventoryScene back to my GameScene, the GameScene loads completely new. This is the transition code from my GameScene class:
func loadInventory(){
if !transitionInProgress{
transitionInProgress = true
if let scene = InventoryScene(fileNamed: "Inventory"){
Globals.InventoryGlobals.levelBeforeSwitch = currentLevel
scene.scaleMode = .aspectFill
let transition = SKTransition.push(with: .down, duration: 0.5)
self.view?.presentScene(scene, transition: transition)
}
}
}
With this code I'll go to my InventoryScene.
Now in my InventoryScene I want to go back to my GameScene with this:
func loadLevel(level: String){
if !transitionInProgress{
transitionInProgress = true
if let scene = GameScene(fileNamed: level){
scene.currentLevel = level
scene.scaleMode = .aspectFill
let transition = SKTransition.doorsOpenHorizontal(withDuration: 1)
self.view?.presentScene(scene, transition: transition)
}
}
}
The transitions are working but my problem is that the GameScene loads completely new, which is obviously because I instantiate a new GameScene. So when the player is in the middle of the level and then goes to the Inventory and back to the GameScene, the player is back at the beginning of the level. If I go from the Inventory back to the scene I want to be the GameScene as it was before (player position, enemy health, etc.)
Has anyone an idea how i can do this?
Retaining your scene is very simple, all you need to do is retain the scene with a strong reference
In your ViewController, it is as simple as storing a variable
class ViewController : UIViewController
{
var gameScene = GameScene(fileNamed:"GameScene")
}
Now for as long as your view controller is alive, your scene will be alive.
To access it, you just need to find a way to tell the MenuScene where your view controller is, then present the scene.
class ViewController : UIViewController
{
var gameScene = GameScene(fileNamed:"GameScene")
lazy var skView : SKView = self.view as! SKView
func gotoMenu()
{
let menu = MenuScene(fileNamed"MenuScene")
menu.viewController = self
skView.presentScene(menu)
}
}
class MenuScene : SKScene
{
var viewController : ViewController!
func returnToGame()
{
view.presentScene(viewcontroller.gameScene)
}
}
But, what if you don't want to use custom SKScene classes all the time, use a view controller, or would rather rely on components, why isn't there a convenient way to go back to a scene.
Well my friend, there is, and it is where userData comes into play
class GameScene : SKScene
{
func gotoMenu()
{
let menu = MenuScene(fileNamed:"MenuScene")
menu.userData = menu.userData ?? ["":Any]()
menu.userData["backToScene"] = self
view.presentScene(menu)
}
}
class MenuScene : SKScene
{
func returnToGame()
{
guard let userData = userData, let scene = userData["backToScene"] as? SKScene
view.presentScene(scene)
}
}
Since we are retaining it in the user data, we can now present the old scene anywhere we have access to the menu scene.
userData is also great in transferring inventory, of course I would create a class to manage the inventory, and just pass the reference via userData
Now, to create a menu that overlays the current scene, that is as simple as applying a new node onto your scene.
You can even use a separate SKS file to layout your menu, and overlay it:
class GameScene : SKScene
{
let menu = MenuScene(fileNamed:"MenuScene")
func overlayMenu()
{
scene.addChild(menu) //You probably want to add an SKCameraNode, and add it to the camera instead
}
override func update(currentTime: CFTimeInterval)
{
if menu.parent != nil
{
menu.update(currentTime:currentTime) //do this only when you need to have a constant update call, be sure to include additional functionality like `didFinishUpdate` in the approprate functions when needed
}
}
}
Of course now would be a good time to develop what is called a worldNode, also may be referred to as gameNode
Essentially what this node is, is the node that holds all your game elements.
This allows you to add overlay nodes that can pause your game.
Your scene hierarchy would like like this:
SKScene
--worldNode
----all nodes that belong in the game
--menuNode
----all nodes that belong on the menu
Now at any time, menu can set the worldNode's isPaused state to true, allowing the game to pause and still giving you the ability to interact with the menuNode
I do layover windows all the time in my Spritekit games, and it doesn't have to be as complicated as you are thinking. Here is how you can do it all in Spritekit without leaving the Scene.
Create a new class which is a subclass of SKSpriteNode for your InventoryDialog.
import SpriteKit
protocol InventoryDialogDelegate: class {
func close()
}
class InventoryDialog: SKSpriteNode {
private var closeButton: SKSpriteNode!
weak var delegate: InventoryDialogDelegate?
init(size: CGSize) {
super.init(texture: nil, color: .clear, size: size)
name = "inventoryDialog"
//do some background design work here
let background = SKSpriteNode(color: .white, size: self.size)
background.zPosition = 1
addChild(background)
closeButton = SKSpriteNode(texture: SKTexture(imageNamed: "closeButton"))
closeButton.position = CGPoint(x: self.size.width / 2 - closeButton.size.width / 2, y: self.size.height / 2 - closeButton.size.height / 2)
closeButton.zPosition = 2
addChild(closeButton)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
let touchLocation = touch!.location(in: self)
if closeButton.contains(touchLocation) {
close()
}
}
func close() {
self.delegate?.close()
}
}
inside your GameScene file
class GameScene: SKScene {
var inventoryDialog: InventoryDialog!
var openButton: SKSpriteNode!
override func didMove(to view: SKView) {
openButton = SKSpriteNode(texture: SKTexture(imageNamed: "openButton"))
openButton.position = CGPoint(x: self.size.width / 2 - closeButton.size.width / 2, y: self.size.height / 2 - closeButton.size.height / 2)
openButton.zPosition = 2
addChild(openButton)
}
func displayInventoryDialog() {
backgroundBlocker = SKSpriteNode(imageNamed: "background3")
backgroundBlocker.size = self.size
backgroundBlocker.zPosition = 4999
addChild(backgroundBlocker)
inventoryDialog = InventoryDialog(size: CGSize(width: 500, height: 800))
inventoryDialog.delegate = self
inventoryDialog.zPosition = 5000
addChild(inventoryDialog)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
//pause any action that you don't want running while the dialog is open
gameLayer.isPaused = true
let touch = touches.first
let touchLocation = touch!.location(in: self)
if openButton.contains(touchLocation) {
displayInventoryDialog()
}
}
}
//MARK: - InventoryDialogDelegate Methods
extension GameScene: InventoryDialogDelegate {
func close() {
//at this point you could update any GUI nesc. based on what happened in your dialog
backgroundBlocker.removeFromParent()
inventoryDialog?.removeFromParent()
gameLayer.isPaused = false
}
}
here is exactly what you wanted.
class GameScene: SKScene {
class InventoryManager: UIView {
override init(frame: CGRect) {
super.init(frame: CGRect(x: 0, y: 0, width: 300, height: 200))
//here is where you set up anything you want to display inside the view
self.backgroundColor = UIColor.green
let Button = UIButton()
Button.frame = CGRect(x: 0, y: 0, width: 40, height: 20)
Button.backgroundColor = UIColor.red
let TouchView = UIView()
TouchView.frame = self.frame
Button.center.x = TouchView.center.x
Button.center.y = TouchView.center.y
TouchView.backgroundColor = UIColor.brown
self.addSubview(TouchView)
//make sure you add any objects that you want to the TouchView and not to the self
TouchView.addSubview(Button)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
var InventoryView = InventoryManager()
var Node = SKSpriteNode()
override func didMove(to view: SKView) {
//this keeps the view centered in the screen
InventoryView.center.x = (self.view?.center.x)!
InventoryView.center.y = (self.view?.center.y)!
Node = SKSpriteNode(color: UIColor.blue, size: CGSize(width: 40, height: 40))
Node.position = CGPoint(x: self.frame.size.width / 3, y: self.frame.size.height / 3)
self.addChild(Node)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
if !(self.view?.frame.contains(location))! {
InventoryView.removeFromSuperview()
}
if Node.contains(location) && InventoryView.isDescendant(of: self.view!) {
self.view?.addSubview(InventoryView)
}
}
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}
clicking the blue button adds it to the scene again and clicking anywhere else removes it from the scene
So for a school project, I have been tasked with making a 2D game. The game is fine but I'm struggling with how to make a back button (In the middle of the page) so was wondering if there was specific code to make this work. I am using spriteKit so I'm trying to go back to the previous scene after clicking on a colour sprite.
I apologise if this is a stupid question but I am slightly new to Swift.
Kind Regards,
James
Here is an example of how you can create a button using a colored sprite. It shows how you can set up a button to receive touch events and how you can use those touch events to navigate between scenes.
In this example you can navigate forward to new scenes and backwards to previous scenes.
import SpriteKit
class Button: SKSpriteNode {
var tapped: (() -> Void)?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
tapped?()
}
}
class GameScene: SKScene {
var parentScene: SKScene?
var sceneCount = 1
override func didMove(to view: SKView) {
if parentScene != nil {
let backButton = addButton(color: .red, position: CGPoint(x: -200, y: 0))
backButton.tapped = {
if let previousScene = self.parentScene {
view.presentScene(previousScene)
}
}
}
let nextButton = addButton(color: .blue, position: CGPoint(x: 200, y: 0))
nextButton.tapped = {
if let nextScene = SKScene(fileNamed: "GameScene") as? GameScene {
nextScene.scaleMode = self.scaleMode
nextScene.parentScene = self
nextScene.sceneCount = self.sceneCount + 1
view.presentScene(nextScene)
}
}
let label = SKLabelNode(text: "Scene \(sceneCount)")
addChild(label)
}
func addButton(color: SKColor = .white, position: CGPoint = .zero) -> Button {
let button = Button(color: color, size: CGSize(width: 200, height: 200))
button.position = position
button.isUserInteractionEnabled = true
addChild(button)
return button
}
}
Too add a button the simplest way is to detect touches on your sprite(s) in the relevant SKScene.
enum NodeName: String {
case coloredSprite1
case coloredSprite2
}
class GameScene: SKScene {
let coloredSprite = SKSpriteNode(imageNamed: "YourImageName")
/// Scene setup
override func didMove(to view: SKView) {
// set up your colored sprite if necessary
// Give your sprites unique names to identify them
coloredSprite.name = NodeName.coloredSprite1.rawValue // always use enums for things like string identifiers so you avoid typos
}
/// Touches
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
let touchedNode = atPoint(location)
// Way 1 by node (probably less preferable)
switch touchedNode {
case coloredSprite:
// do something (e.g call loadScene method)
// see below
default:
break
}
// Way 2 by node name (probably more preferable)
// name is an optional so we have to unwrap it when using it in the switch statement.
// The easiest way is by providing an alternative string, by using the nil coalescing operator (?? "NoNodeNameFound")
switch touchedNode.name ?? "NoNodeNameFound" {
case NodeName.coloredSprite1.rawValue:
// do something (e.g call loadScene method)
// see below
default:
break
}
}
}
// Also call touchesEnded, touchesMoved and touchesCancelled and do necessary stuff
}
For a more reusable solution you ideally want to create a button subclass. There is quite a few tutorials to google on how to do this.
To than transition between SKScenes you can create a loadScene method in each scene and than call them when necessary.
// Start Scene
class StartScene: SKScene {
...
func loadGameScene() {
// If you do everything in code
let gameScene = GameScene(size: self.size)
view?.presentScene(gameScene, transition: ...)
// If you use SpriteKit scene editor
guard let gameScene = SKScene(fileNamed: "GameScene") else { return } // fileNamed is the name you gave the .sks file
view?.presentScene(gameScene, transition: ...)
}
}
// Game scene
class GameScene: SKScene {
....
func loadStartScene() {
// If you do everything in code
let startScene = StartScene(size: self.size)
view?.presentScene(startScene, transition: ...)
// If you use SpriteKit scene editor
guard let startScene = SKScene(fileNamed: "StartScene") else { return } // fileNamed is the name you gave the .sks file
view?.presentScene(startScene, transition: ...)
}
}
Hope this helps
Explanation
The title pretty much asks for itself. I'm trying to make a button activate an ad banner, but Xcode returns me this message below; so, because of the UIViewController, I saw that this banner should be called on GameViewController, not GameScene. I looked for it everywhere, but did not find how to call this banner from GameScene or another way to activate this banner by a button.
Cannot convert value of type 'GameScene' to expected argument type 'UIViewController!'
Code
This code below is pretty simple. It has a button on its attempt to call an ad banner.
import SpriteKit
import Ads
class GameScene: SKScene {
var button = SKSpriteNode()
override func didMoveToView(view: SKView) {
/* Setup your scene here */
button = SKSpriteNode(imageNamed: "button")
button.position = CGPoint(x: self.frame.width / 2, y: self.frame.height / 2)
button.setScale(0.4)
addChild(button)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
for touch in touches {
let location = touch.locationInNode(self)
let node = nodeAtPoint(location)
if node == button{
Ads.showAd(AdsShowStyle.BannerBottom, rootViewController: self) //issue line
}
}
}
}
Attempt
After some research, I found out that it might be possible by using delegation. Following the highest voted answer of this question, I came up to this code below, but I'm having many issues that I'm, unsuccessfully, struggling to solve.
GameScene.swift
GameViewController.swift
Thanks in advance,
Luiz.
Since you are using SpriteKit but we don't know what ad network you are using, I would recommend using a NSNotificationCenter command in GameViewController and use a postNotificationName command in the GameScene.swift instead of a ViewControllerDelegate. This would allow your ad code to be used in any other .swift files. Here's a better explanation of this from another post if needed
Note: This may or may not work depending on ad network
GameViewController.swift (from my project)
import UIKit
import SpriteKit
import GoogleMobileAds
class GameViewController: UIViewController, GADAdDelegate {
var adMobBanner : GADBannerView!
override func viewDidLoad() {
super.viewDidLoad()
if let scene = GameScene(fileNamed:"GameScene") {
// Configure the view.
let skView = self.view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = true
/* Set the scale mode to scale to fit the window */
scene.scaleMode = .AspectFill
skView.presentScene(scene)
}
adMobBanner = GADBannerView(adSize: kGADAdSizeSmartBannerPortrait)
adMobBanner.adUnitID = "your ad unit id"
adMobBanner.rootViewController = self
adMobBanner.frame = CGRectMake(0, view.bounds.height - adMobBanner.frame.size.height, adMobBanner.frame.size.width, adMobBanner.frame.size.height)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(GameViewController.showAdMob), name: "showAdMobKey", object: nil)
}
func showAdMob() {
let request : GADRequest = GADRequest()
adMobBanner.loadRequest(request)
self.view.addSubview(adMobBanner)
print("adMob")
}
GameScene.swift (edited yours)
import SpriteKit
import Ads
class GameScene: SKScene {
var button = SKSpriteNode()
override func didMoveToView(view: SKView) {
/* Setup your scene here */
button = SKSpriteNode(imageNamed: "button")
button.position = CGPoint(x: self.frame.width / 2, y: self.frame.height / 2)
button.setScale(0.4)
addChild(button)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
for touch in touches {
let location = touch.locationInNode(self)
if (button.containsPoint(location)) {
NSNotificationCenter.defaultCenter().postNotificationName("showAdMobKey", object: nil)
}
}
}
}
Hi than you for looking at this i was wondering why my code in swift sprite kit won't work i can run it but when i click on the button it does not work(it does not go to the scene) but when i add a println to the if statement and then i click it it does run the println. Thank you in advance!
Here is my code for my first scene:
//
// GameScene.swift
// Pocket Rocket
//
// Created by Lucas Farleigh on 11/11/2014.
// Copyright (c) 2014 Lucas Farleigh. All rights reserved.
//
import SpriteKit;
class GameScene: SKScene {
let background = SKSpriteNode(imageNamed:"background")
let playButton = SKSpriteNode(imageNamed: "playbutton")
override func didMoveToView(view: SKView) {
playButton.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidX(self.frame))
background.yScale = 2
background.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidX(self.frame))
self.addChild(background)
self.addChild(playButton)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
//making the scene vars
var scene = PlayScene(size: self.size)
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
let node = self.nodeAtPoint(location)
let skview = self.view as SKView!
scene.size = skview.bounds.size
playButton.name = "PB"
if node.name == "PB"{
println("it worked")
skview.presentScene(scene)
}
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
And my second scene(playscene)
//
// PlayScene.swift
// Pocket Rocket
//
// Created by Lucas Farleigh on 14/11/2014.
// Copyright (c) 2014 Lucas Farleigh. All rights reserved.
//
import SpriteKit
import UIKit
import Foundation
class PlayScene:SKScene{
let background = SKSpriteNode(imageNamed: "background")
let bar1 = SKSpriteNode(imageNamed:"Bar1")
let bar2 = SKSpriteNode(imageNamed:"Bar2")
let bar3 = SKSpriteNode(imageNamed:"Bar3")
let bar4 = SKSpriteNode(imageNamed:"Bar4")
let bar5 = SKSpriteNode(imageNamed:"Bar5")
let bar6 = SKSpriteNode(imageNamed:"Bar6")
override func didMoveToView(view: SKView) {
background.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidX(self.frame))
addChild(background)
background.yScale = 2
Note that you are creating background and PlayButton nodes twice: once as a class property, and a second time as a local variable in didMoveToView. It is the local version that you are then adding to the scene as a child view. If you want to be able to test against these nodes in other methods in your class, get rid of the local declarations in didMoveToView. You can still fill in the other information about these nodes, just remove the let statements.
Then in your touchesBegan you want to get the location of the touch in the scene by passing self to touch.locationInNode(self) and then retrieve the node that corresponds to that touch:
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
let node = self.nodeAtPoint(location)
if node == Play_Button {
println("Hello!!!!")
}
}
}
A second way to accomplish the PlayButton would be to give the node a name when you create it:
Play_Button.name = "playButton"
and then your touchesBegan would look like this:
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
let node = self.nodeAtPoint(location)
if node.name == "playButton" {
println("Hello!!!!")
}
}
}
I agree with vacawama's answer (I use the naming option) but I'd like to add a little on my approach to buttons in SK. Usually you either have a background and a label or just a PNG image with both background and label.
When using nodeAtPoint on the label you might not have enough area. If instead you use the background area to detect taps then you might have conflicts with the label on top of the node.
To solve this I create a third element (usually an SKShapeNode) on top of both, transparent and a little bigger to make it easier for the user to tap on it.
To make sure you are positioning these tap areas on top of the buttons remember to use a higher zPosition