How to create NSUserdefaults to image button in spritekit - ios

I have Sound button (On/Off) in main scene and its works perfectly
but I have problem , off the background music playing when I was disqualified I return to the main screen, but the image of the button changes the music is turned on and stays on the image of the music is off.
Main Scene :
var SoundOnOff = SKSpriteNode()
override func didMoveToView(view: SKView) {
backgroundColor = UIColor(red:0.09, green:0.63, blue:0.52, alpha:1.0)
//Main Scene:
SoundOnOff.texture = SKTexture(imageNamed:"Sound-on.png")
SoundOnOff.position = CGPoint(x: self.size.width/2 - 40 , y: self.size.height/2 - 500)
SoundOnOff.size = CGSizeMake(60 , 60)
SoundOnOff.runAction(SKAction.moveToY(140, duration: 0.5))
SoundOnOff.removeFromParent()
addChild(SoundOnOff)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first
let location = touch!.locationInNode(self)
if(SoundOnOff.containsPoint(location)) {
// ---------------------------------------------
// Play Background Music
// ---------------------------------------------
if ((NSUserDefaults.standardUserDefaults().objectForKey("onoroff")) !== true)
{
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "onoroff")
Singleton.sharedInstance().pauseBackgroundMusic()
SoundOnOff.texture = SKTexture(imageNamed:"Sound-off.png")
}else {
NSUserDefaults.standardUserDefaults().setBool(false, forKey: "onoroff")
Singleton.sharedInstance().resumeBackgroundMusic()
SoundOnOff.texture = SKTexture(imageNamed:"Sound-on.png")
}
}
} }
}

Basically, you have to check in your Menu scene if music is off or on, and based on that to show appropriate texture. Right now, it seems that you are not making that check inside of a menu scene.
HINT: Obviously, your Singleton class has been implemented as a singleton. And if everything is done correctly, it is instantiated once and alive through the whole app's life time. So it has an info about music on/off state. What I wanted to point is that, when the app is closed, this singleton gets deallocated. So before that happen, you might store the info about music on/off into persistent storage, like NSUserDefaults, so the next time when user open up the app, he has his settings saved. Just a though... This really depends on you and what you want to offer in your app.

Related

How to stop button action if user moves their finger off the button in sprite kit?

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
let positionInScene = touch!.location(in: self)
let touchedNode = self.atPoint(positionInScene)
if let name = touchedNode.name {
if name == "leftbutton" {
print("left button stopped")
touchedNode.run(buttonStoppedPressAction)
player?.removeAllActions()
}
if name == "rightbutton" {
print("right button stopped")
touchedNode.run(buttonStoppedPressAction)
player?.removeAllActions()
}
}
}
Here I have code that when the user lifts off their finger from the buttons it stops the action but only if they lift of their finger inside the button. So if they press it and begin to move their finger somewhere else on the screen while continuously pressing down the button will not stop executing its code. Thank you for any help.
Essentially you should check for touch location at touch down and compare to the location at touch up. If the touch is no longer in the area of your button, you cancel all effects.
First, though, a point. It seems like you are handling button logic in the SKScene level, which is what tutorials often tell you to do. However, this may not be the best approach. The risks here, in addition to just a cluttered mess of a SKScene, emerge from handling multiple objects and how they react to touch events, and also additional complexity from multitouch (if allowed).
Years ago when I started with SpriteKit, I felt like this was a huge pain. So I made a button class that handles all the touch logic independently (and sends signals back to the parent when something needs to happen). Benefits: No needless clutter, no trouble distinguishing between objects, the ability to determine multitouch allowances per-node.
What I do in my class to see if the touch hasn't left the button before touch up is that I store the size of the button area (as a parameter of the object) and touch position within it. Simple simple.
In fact, it has baffled me forever that Apple didn't just provide a rudimentary SKButton class by default. Anyhow, I think you might want to think about it. At least for me it saves sooo much time every day. And I've shipped multiple successful apps with the same custom button class.
EDIT: Underneath is my barebones Button class.
import SpriteKit
class Button: SKNode {
private var background: SKSpriteNode?
private var icon: SKNode?
private var tapAction: () -> Void = {}
override init() {
super.init()
isUserInteractionEnabled = true
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
isUserInteractionEnabled = true
}
// MARK: Switches
public func switchButtonBackground(buttonBackgroundSize: CGSize, buttonBackgroundColor: SKColor) {
background = SKSpriteNode(color: buttonBackgroundColor, size: buttonBackgroundSize)
addChild(background!)
}
public func switchButtonIcon(_ buttonIcon: SKNode) {
if icon != nil {
icon = nil
}
icon = buttonIcon
addChild(icon!)
}
public func switchButtonTapAction(_ buttonTapAction: #escaping () -> Void) {
tapAction = buttonTapAction
}
// MARK: Touch events
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
tapAction()
}
}
And then you create the Button object by first initiating it, assigning it a background using a size and color, then assign it an icon, assign it a function to run when tapped and finally add it as a child to the scene.
let icon = SKNode()
let size = CGSize(width: 20.0, height: 20.0)
let button = Button()
button.switchButtonBackground(buttonBackgroundSize: size, buttonBackgroundColor: .clear)
button.switchButtonIcon(icon)
button.switchButtonTapAction(buttonPressed)
addChild(button)
The background defines the touch area for the button, and you can either have a color for it or determine it as .clear. The icon is sort of supposed to hold any text or images you want on top of the button. Just package them into an SKNode and you're good to go. If you want to run a function with a parameter as the tap action, you can just make a code block.
Hope that helps! Let me if you need any further help :).

SpriteKit: run action while scene is paused

I have a button to pause the game on my code. What I want is that pausing the game with that button makes a message that says "Paused" to appear. However, since the scene is paused, the message does not appear.
What I have right now is a SKLabelNode with the alpha on 0.0 at the beginning and when the user pauses the game, it changes to 1.0 with fadeInWithDuration(). Then when the user presses the button again, it changes back to 0.0 with fadeOutWithDuration(). The problem is that the SKAction with fadeInWithDuration() does not run when the scene is paused.
How could I achieve this?
The best way, one Apple also uses in "DemoBots", is to create a world node that you pause instead of the scene.
Create a worldNode property
class GameScene: SKScene {
let worldNode = SKNode()
}
add it to the scene in didMoveToView
addChild(worldNode)
and than add everything you need paused to the worldNode. This includes actions that are normally run by the scene (eg. timers, enemy spawning etc)
worldNode.addChild(someNode)
worldNode.run(someSKAction)
Than in your pause func you say
worldNode.isPaused = true
physicsWorld.speed = 0
and in resume
worldNode.isPaused = false
physicsWorld.speed = 1
You can also add an extra check in your Update function if you have stuff there that you want to ignore when paused.
override func update(_ currentTime: CFTimeInterval) {
guard !worldNode.isPaused else { return }
// your code
}
This way it's much easier to add your paused label or other UI when your game is paused because you haven't actually paused the scene. You can also run any action you want, unless that action is added to the worldNode or to a child of worldNode.
Hope this helps
Instead of pausing the scene, you could layer some nodes your scene like this
SKScene
|--SKNode 1
| |-- ... <--place all scene contents here
|--SKNode 2
| |-- ... <--place all overlay contents here
Then when you want to pause the game, you pause only SKNode 1.
This allows node SKNode 2 to continue to run, so you can do things like have animations going, and have a button that unpauses the scene for you, without having the need to add some non Sprite Kit object into the mix.
A quick workaround would be to pause your game after the SKLabelNode appears on screen:
let action = SKAction.fadeOutWithDuration(duration)
runAction(action) {
// Pause your game
}
Another option would be to mix UIKit and SpriteKit and inform the ViewController back that it needs show this label.
class ViewController: UIViewController {
var gameScene: GameScene!
override func viewDidLoad() {
super.viewDidLoad()
gameScene = GameScene(...)
gameScene.sceneDelegate = self
}
}
extension ViewController: GameSceneDelegate {
func gameWasPaused() {
// Show your Label on top of your GameScene
}
}
protocol GameSceneDelegate: class {
func gameWasPaused()
}
class GameScene: SKScene {
weak var sceneDelegate: GameSceneDelegate?
func pauseGame() {
// Pause
// ...
sceneDelegate?.gameWasPaused()
}
}
So you want to pause the game AFTER the action execution has completed.
class GameScene: SKScene {
let pauseLabel = SKLabelNode(text: "Paused")
override func didMoveToView(view: SKView) {
pauseLabel.alpha = 0
pauseLabel.position = CGPoint(x: CGRectGetMaxY(self.frame), y: CGRectGetMidY(self.frame))
self.addChild(pauseLabel)
}
func pause(on: Bool) {
switch on {
case true: pauseLabel.runAction(SKAction.fadeInWithDuration(1)) {
self.paused = true
}
case false:
self.paused = false
pauseLabel.runAction(SKAction.fadeOutWithDuration(1))
}
}
}
I would add the label with
self.addChild(nameOfLabel)
and then pause the game with
self.scene?.paused = true
This should all go in the if pauseButton is touched portion of your code.

Sprite moves two places after being paused and then unpaused

I am having trouble figuring out the solution to this and am starting to get very frustrated with it.
I have a pause button and an unpause button in my game scene to allow the player to pause the game, which is the following code
else if (node == pauseButton) {
pauseButton.removeFromParent()
addChild(unpauseButton)
addChild(restartButton)
self.runAction (SKAction.runBlock(self.pauseGame))
}
func pauseGame(){
pauseButton.hidden = true
unpauseButton.hidden = false
scene!.view!.paused = true // to pause the game
}
Problem
The problem is that when I pause the game then unpause the game my player sprite seems to move two spaces forward automatically
I also have a tap and swipe gesture that allows me to move the player up left right and down when I tap anywhere on the screen.
func tapUp(){
let amountToMove:CGFloat = levelUnitHeight
let move:SKAction = SKAction.moveByX(0, y: amountToMove, duration: 0.1)
menubutton.hidden = true
settingsButton.hidden = true
highscoreLabel.hidden = true
pauseButton.hidden = false
thePlayer.runAction(move)
}
func swipedRight(){
let amountToMove:CGFloat = levelUnitHeight
let move:SKAction = SKAction.moveByX(amountToMove, y: 0, duration: 0.1)
thePlayer.runAction(move) // links the action with the players
}
As the member above me said the player is not really moving 2 spaces.
Also you should maybe change your strategy when pausing your game, because pausing the scene.view makes it very hard to add SpriteKit elements afterwards.
I think a better way is to create a worldNode in your GameScene and add all the sprites that need to be paused to that worldNode. It basically gives you more flexibility pausing a node rather than the whole scene.
First create a world node property
let worldNode = SKNode()
and add it to the scene in ViewDidLoad
addChild(worldNode)
Than add all the sprites you need paused to the worldNode
worldNode.addChild(sprite1)
worldNode.addChild(sprite2)
...
Create a global enum for your game states
enum GameState {
case Playing
case Paused
case GameOver
static var current = GameState.Playing
}
Than make a pause and resume func in your game scene
func pause() {
GameState.current = .Paused
// show pause menu etc
}
func resume() {
GameState.current = .Playing
self.physicsWorld.speed = 1
worldNode.paused = false
}
And finally add the actual pause code to your updateMethod. This way it will not resume the game even if spriteKit itself tries to resume (e.g. reopened app, dismissed alert etc)
override func update(currentTime: CFTimeInterval) {
if GameState.current == .Paused {
self.physicsWorld.speed = 0
worldNode.paused = true
}
}
In regards to your tapGesture recogniser, in the method that gets called after a tap you can add this before the rest of the code
guard GameState.current != .Paused else { return }
....
Hope this helps

SKAudioNode positional audio seems to not work

I'm trying to implement the "3d" audio Apple has added to SpriteKit, but the sounds really don't seem to be positioned at all, even with headphones on. Here's my relevant code.
Play Sound function:
func playTapSound(node: SKNode) {
let tapSound = SKAudioNode(fileNamed: "glassNote")
tapSound.positional = true
tapSound.autoplayLooped = false
node.addChild(tapSound)
tapSound.runAction(SKAction.play())
}
Game Scene:
let listenerNode = SKNode()
...
override func didMoveToView(view: SKView) {
listenerNode.position = view.center
listener = listenerNode
}
playTapSound gets called if the user taps a game piece node.
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touchedNode = scene.nodeAtPoint(sceneTouchPoint)
playTapSound(touchedNode)
}
I've checked the positions of all the nodes including the listener node and they're all correct. Listener node is in the center of the screen and the game pieces are basically a checkerboard layout.
When I tap a piece, I really can't tell there's any sort of positioning on the audio. Is it just extremely subtle? Can't find much info out there about it except Apple saying it's part of SpriteKit..
Turns out my mistake was not adding my listenerNode to the scene before setting it to the listener.
From the docs on listener:
If a non-nil value is specified, it must be a node in the scene.
Game Scene updated and verified:
let listenerNode = SKNode()
...
override func didMoveToView(view: SKView) {
listenerNode.position = view.center
addChild(listenerNode)
listener = listenerNode
}

How to change View Controller when player collides with an object?

I am having trouble figuring out how to change view controllers when your player collided with an object.
I want to like a menu to pop-up displaying a menu button and a replay button, also so extra buttons that are not important at this moment of time. I am not sure how some of those end of game menus are made, I am thinking switching view controllers, if you know exactly how they are made please tell me.
This is the code I have at the moment, and the only thing it does is display a label that the game is over and when that label is tapped the game will restart:
import Foundation
import AVFoundation
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var movingGround: PPMovingGround!
var square1: PPSquare1!
var wallGen: PPWallGen!
var diamondGen: PPDiamondGen!
var isStarted = false
var isGameOver = false
var isDiamondContact = false
var playerNode: SKNode!
override func didMoveToView(view: SKView) {
//code that is not important was deleted
func collisionWithDiamond() {
isDiamondContact = true
}
func restart() {
let newScence = GameScene(size: view!.bounds.size)
newScence.scaleMode = .AspectFill
view!.presentScene(newScence)
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
if isGameOver {
restart()
} else {
square1.flip()
}
}
override func update(currentTime: CFTimeInterval) {
}
// MARK: - SKPhysicsContactDelegate
func didBeginContact(contact: SKPhysicsContact) {
if !isGameOver {
gameOver()
} else {
!isDiamondContact
collisionWithDiamond()
}
}
Note: I have deleted code that is unrelated or not necessary.
Updates:
Link to a game play of a game: https://www.youtube.com/watch?v=WUibTETfEQY
SKIP TO 2:32 TO SEE THE GAME OVER SCREEN
Link to image of game over screen: Image
(I was unable to post an image here because I don't have the required 10 rep points yet.)
// Edited Answer
This will be the easiest. Create a new GameOverScene.swift that is a SKScene. Then customize that scene however you want with background image, SKLabelNodes for buttons. Checkout creating buttons in skview to point to different scenes
When the game ends in GameScene,
let gameOverScene: GameOverScene = GameOverScene(size: self.size)
self.view!.presentScene(gameOverScene, transition: SKTransition.doorsOpenHorizontalWithDuration(1.0))
Here is a project that has this implemented, http://www.raywenderlich.com/76741/make-game-like-space-invaders-sprite-kit-and-swift-tutorial-part-2
// First Answer -----------------------------------------------
If you want to switch viewControllers, you will have to present the new viewController like this or with segue,
self.view?.window?.rootViewController?.presentViewController(newView, animated: true, completion: nil)
self.view?.window?.rootViewController?.performSegueWithIdentifier("id", sender: AnyObject)
Otherwise create a SKView and add buttons, then add it to the scene when game is over or add it before, hide it, then show it. Once user picks a choice, remove it or hide it with,
SKView.hidden = false
SKView.hidden = true
Add SKView with,
self.view?.addSubview(SKView)
Simple SKView overlay,
let view1 = SKView(frame: CGRectMake(0, 0, 200, 200))
view1.center = self.view!.center
self.view?.addSubview(view1)
If the game is over and you want to present a selective menu, you could present a UIAlertView that presents the user with whatever options that you want. From there they could restart the game, or they could choose to go to some other view like one that manages their player stats or something ( I don't know what exactly your game is).

Resources