1
votes

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.

1

1 Answers

-1
votes

The found the solution on some japanesse forum. custom button for spritekit in swift

This can be used on OS X and iOS.

The solution to problem is to use Protocol on Scene class that includes button functions and to call those functions from button class via optional delegate (parent scene instance).