Writting my own game in swift using SpriteKit I came on one problem that it seems I cannot solve. Game I'm writting is for OS X platform (not mobile), using pretty standard code layout:
First here is my AppDelegate:
import Cocoa
import SpriteKit
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate
{
@IBOutlet weak var window: NSWindow!
@IBOutlet weak var skView: SKView!
func applicationDidFinishLaunching(aNotification: NSNotification)
{
/* Pick a size for the scene */
if let scene = MainMenu(fileNamed:"MainMenu")
{
scene.size = CGSize(width: 1024, height: 768)
/* Set the scale mode to scale to fit the window */
scene.scaleMode = .AspectFit
scene.backgroundColor = NSColor.blackColor()
self.skView!.presentScene(scene)
/* Sprite Kit applies additional optimizations to improve rendering performance */
self.skView!.ignoresSiblingOrder = true
self.skView!.showsFPS = true
self.skView!.showsNodeCount = true
window.acceptsMouseMovedEvents = true
}
}
func applicationShouldTerminateAfterLastWindowClosed(sender: NSApplication) -> Bool
{
return true
}
}
This opens MainMenu scene where I have many buttons that each leads to other scenes. For buttons I have made my own subclass of SPriteKitNode:
import Foundation
import SpriteKit
class Button: SKSpriteNode
{
var isHighLighted = false
var isPressed = false
var normal: SKTexture
var highlighted: SKTexture
var pressed: SKTexture
var disabled: SKTexture
var isDisabled = false
init(normal: SKTexture, highlighted: SKTexture, pressed: SKTexture, disabled: SKTexture)
{
self.normal = normal
self.highlighted = highlighted
self.pressed = pressed
self.disabled = disabled
super.init(texture: normal, color: NSColor.clearColor(), size: normal.size())
}
required init?(coder aDecoder: NSCoder)
{
normal = SKTexture()
highlighted = SKTexture()
pressed = SKTexture()
disabled = SKTexture()
super.init(coder: aDecoder)
}
func disableButton()
{
self.texture = disabled
isDisabled = true
}
func enableButton()
{
self.texture = normal
isDisabled = false
}
//overrides
override func mouseEntered(theEvent: NSEvent)
{
if !isDisabled
{
isHighLighted = true
self.texture = highlighted
}
}
override func mouseExited(theEvent: NSEvent)
{
if !isDisabled
{
isHighLighted = false
self.texture = normal
}
}
override func mouseDown(theEvent: NSEvent)
{
if !isDisabled
{
isPressed = true
self.texture = pressed
scene?.mouseDown(theEvent)
}
}
override func mouseUp(theEvent: NSEvent)
{
if !isDisabled
{
isPressed = false
self.texture = highlighted
}
}
}
And main menu (not complete code) is here:
import SpriteKit
class MainMenu: SKScene
{
let quickGame = Button(normal: SKTexture(imageNamed: "quick"), highlighted: SKTexture(imageNamed: "quick-high"), pressed: SKTexture(imageNamed: "quick-down"), disabled: SKTexture(imageNamed: "quick-dis"))
override func didMoveToView(view: SKView)
{
let options = NSTrackingAreaOptions.MouseMoved.union(NSTrackingAreaOptions.ActiveInKeyWindow)
let trackingArea = NSTrackingArea(rect: view.frame, options: options, owner: self, userInfo: nil)
view.addTrackingArea(trackingArea)
let background = SKSpriteNode(imageNamed: "splash")
background.position = CGPoint(x: background.size.width, y: background.size.height)
background.userInteractionEnabled = true
background.position = CGPoint.zero
background.anchorPoint = CGPoint.zero
background.zPosition = 0
//background.setScale(1.0)
self.addChild(background)
//let background = self.childNodeWithName("splash")
//let quickGame = Button(normal: textureAtlas.textureNamed("quick"), highlighted: textureAtlas.textureNamed("quick-high"), pressed: textureAtlas.textureNamed("quick-down"))
quickGame.name = "quick game"
quickGame.anchorPoint = CGPoint.zero
quickGame.position = CGPoint(x: 230, y: 97)
quickGame.userInteractionEnabled = true
quickGame.zPosition = 100
background.addChild(quickGame)
… rest of init here…
}
override func mouseDown(theEvent: NSEvent)
{
if newGame.isPressed
{
}
else if quickGame.isPressed
{
if let quickGameScene = QuickGame(fileNamed: "QuickGame")
{
quickGameScene.scaleMode = self.scaleMode
let transition = SKTransition.fadeWithDuration(1.0)
self.removeAllChildren()
self.removeAllActions()
view?.presentScene(quickGameScene, transition: transition)
}
}
else if quitGame.isPressed
{
NSApp.terminate(self)
}
}
}
I'm getting EXC_BAD_ACCESS thrown when I click on quickGame button. Click goes ok, new scene shows up but after a second or two game crashes and error is placed ainside AppDelegate class declaration.
I'm suspecting that I have done something wrong with placement of call of mouseDown inside Button class.
What I wish to accomplish here is to have event-driven system and when event fires, message is sent from button to scene class where I don't need to iterate over all buttons (because in some scenes there will be a lot of buttons and pressable/selectable elements), but just execute code (In my current case switch scene).
I have done some debugging and after changing scene in MainMenu class debugger went back into Button class, so I'm thinking that problem lies there as switching scenes this MainMenenter code hereu instance is probably destroyed along with all Buttons and elements and when code returns to Button, instance of it doesn't exists anymore. I'm not 100% and also don't know how to solve this as I have jsut started to work with swift and SPriteKit.