Passing a string between SKScenes using UserDefaults - ios

In a game I'm developing with Swift, I want the player to be able to choose a background in a shop-like scene and thus change the background of every SKScene. I'm trying to accomplish this using UserDefaults, but for some reason it isn't working. Here's the important code of the Shop Scene (I removed the irrelevant code):
import SpriteKit
class ShopScene: SKScene {
var backNumber = 90
var backRemainder = 0
var background = SKSpriteNode()
var backName:String = "back1"
override func didMove(to view: SKView) {
background.texture = SKTexture(imageNamed: "\(backName)")
background.size = self.size
self.addChild(background)
let nextButton: NButton = NButton(defaultButtonImage: "next", activeButtonImage: "nextP", buttonAction: nextAction)
addChild(nextButton)
let selectButton: SButton = SButton(defaultButtonImage: "next", activeButtonImage: "nextP", buttonAction: selectAction)
addChild(selectButton)
}
func selectAction() {
UserDefaults.standard.set(backName, forKey: "backSaved")
let sceneToMoveTo = MainMenuScene(size: self.size)
sceneToMoveTo.scaleMode = self.scaleMode
let sceneTransition = SKTransition.fade(withDuration: 0.4)
self.view!.presentScene(sceneToMoveTo, transition: sceneTransition)
}
func nextAction() {
backNumber += 1
backRemainder = backNumber % 3
switch backRemainder {
case 0:
backName = "back1"
case 1:
backName = "back2"
case 2:
backName = "back3"
default:
backName = "back1"
}
background.texture = SKTexture(imageNamed: "\(backName)")
}
}
As you can see, when the select button is pressed backName is saved. Now, this is the relevant code of the Main Menu Scene:
Import SpriteKit
class MainMenuScene: SKScene {
var backName = UserDefaults.standard.string(forKey: "backSaved")
override func didMove(to view: SKView) {
let background = SKSpriteNode(imageNamed: "\(backName)")
background.size = self.size
self.addChild(background)
When the 'Select' button is pressed, you should transition to Main Menu Scene and see that the background is the one you selected. However, I get a red X in a white background when I run this. I've worked before with UserDefaults to save scores, but I can't figure out why it's not working in this case. Any idea on how to pass strings between SKScenes using UserDefaults? Am I doing something wrong?
NOTE: Although Knight0fDragon's answer is marked correct, his answer works for passing a string between scenes but not for saving that value permanently. In you wish to pass the value and save it, check my answer.

I would recommend not doing UserDefaults, that is meant for preferences to your application. Instead use userData
func selectAction() {
let sceneToMoveTo = MainMenuScene(size: self.size)
sceneToMoveTo.scaleMode = self.scaleMode
sceneToMoveTo.userData = sceneToMoveTo.userData ?? NSMutableDictionary() //This lets us ensure userdata exists
sceneToMoveTo.userData!["backSaved"] = backName
let sceneTransition = SKTransition.fade(withDuration: 0.4)
self.view!.presentScene(sceneToMoveTo, transition: sceneTransition)
}
Then implement it with:
import SpriteKit
class MainMenuScene: SKScene {
lazy var backName:String = {return self.userData?["backSaved"] as? String ?? "back1"}() //This allows us to load backName at the time it is needed, or assign back1 if doesn't exist
override func didMove(to view: SKView) {
let background = SKSpriteNode(imageNamed: backName)
self.addChild(background)
Note, I am not sure if the as? String is needed, try it without it and see if Swift can infer it

Apparently, backName behaves as an optional because it can have no value, thus being nil. To make my code work I made these changes to Shop Scene:
import SpriteKit
class ShopScene: SKScene {
var backName:String? = UserDefaults.standard.string(forKey: "backSaved")
override func didMove(to view: SKView) {
if backName != nil {
backName = UserDefaults.standard.string(forKey: "backSaved")
} else {
backName = "back1"
}
background.texture = SKTexture(imageNamed: "\(backName!)")
self.addChild(background)
and these changes to Main Menu Scene:
import SpriteKit
class MainMenuScene: SKScene {
var backName:String? = UserDefaults.standard.string(forKey: "backSaved")
override func didMove(to view: SKView) {
if backName != nil {
backName = UserDefaults.standard.string(forKey: "backSaved")
} else {
backName = "back1"
}
let background = SKSpriteNode(imageNamed: "\(backName!)")
self.addChild(background)

Related

Cannot make app go to main menu (Swift 4 SpriteKit)

I am trying to direct the user to a main menu file as soon my game loads (made with Swift 4 SpriteKit). However, when I try to redirect the user to this .swift file (where a label should be displayed onto the users screen) the screen is simply blank except the for the node and FPS counter.
Here's my code in the MainMenuScene.swift file I created:
import Foundation
import SpriteKit
class MainMenu: SKScene {
override func didMove(to view: SKView) {
print("In scene")
let myLabel = SKLabelNode(fontNamed: "The Bold Font")
myLabel.fontColor = SKColor.blue
myLabel.text = "My Label"
myLabel.fontSize = 50
myLabel.position = CGPoint(x: 0, y: 0)
myLabel.zPosition = 1
self.addChild(myLabel)
}
}
Here's my code in the GameViewController.swift
import UIKit
import SpriteKit
import GameplayKit
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
// Load the SKScene from 'GameScene.sks'
if let scene = SKScene(fileNamed: "MainMenuScene") {
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
// Present the scene
view.presentScene(scene)
}
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
view.showsPhysics = true
}
}
override var shouldAutorotate: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .allButUpsideDown
} else {
return .all
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
override var prefersStatusBarHidden: Bool {
return true
}
}
I changed the
if let scene = SKScene(fileNamed: "GameScene")
to
if let scene = SKScene(fileNamed: "MainMenuScene")
That line of code should redirect the user to MainMenuScene.swift, but when I run the program the screen is simply blank.
Anyone know what im doing wrong?
Thanks!
Try using the code below after
super.viewDidLoad()
Also make sure that your Menu class is called "MainMenuScene" and that you changed the scene name to the same at MainMenuScene.sks
CODE:
if let scene = GKScene(fileNamed: "MainMenuScene") {
// Get the SKScene from the loaded GKScene
if let sceneNode = scene.rootNode as! MainMenuScene? {
scene.scaleMode = .aspectFill
// Present the scene
if let view = self.view as! SKView? {
view.presentScene(sceneNode)
view.ignoresSiblingOrder = true
//view.showsFPS = true
//view.showsNodeCount = true
}
}
}

SpriteKit : Finding unexpectedly nil while unwrapping, can't implement multiple menu scenes

I am trying to present a menu with 2 buttons as initial scene: play, credits. When play is clicked, I want to present another menu scene with 4 buttons: tutorial, easy, hard, back.
The strategy is simply to create the buttons as SKSpriteNode objects and handle clicks in touchesBegan function.
In my menuScene.sks file I properly placed and named my nodes. Here, you can check the menuScene.swift file linked to this scene:
import SpriteKit
class menuScene: SKScene {
var playButton:SKSpriteNode!
var creditsButton:SKSpriteNode!
override func didMove(to view: SKView) {
playButton = self.childNode(withName: "playButton") as! SKSpriteNode
creditsButton = self.childNode(withName: "creditsButton") as! SKSpriteNode
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
// transition is defined in Helper.swift
let touch = touches.first
if let location = touch?.location(in: self){
let nodesArray = self.nodes(at: location)
if nodesArray.first?.name == "playButton" {
let nextScene = difScene(size: self.size)
self.view?.presentScene(nextScene, transition: transition)
} else if nodesArray.first?.name == "creditsButton" {
let nextScene = creditsScene(size: self.size)
self.view?.presentScene(nextScene, transition: transition)
}
}
}
}
When I run, menuScene is presented without a problem. However when I touch the play button I get an error and it is from next scene: difScene.
Here you can find difScene.swift:
import SpriteKit
class difScene: SKScene {
var tutButton:SKSpriteNode!
var easyButton:SKSpriteNode!
var hardButton:SKSpriteNode!
var backButton:SKSpriteNode!
override func didMove(to view: SKView) {
tutButton = self.childNode(withName: "tutButton") as! SKSpriteNode // error
easyButton = self.childNode(withName: "easyButton") as! SKSpriteNode
hardButton = self.childNode(withName: "hardButton") as! SKSpriteNode
backButton = self.childNode(withName: "backButton") as! SKSpriteNode
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let transition = SKTransition.push(with: .down, duration: 0.2)
let touch = touches.first
if let location = touch?.location(in: self){
let nodesArray = self.nodes(at: location)
if nodesArray.first?.name == "tutButton" {
let nextScene = GameScene(size: self.size)
self.view?.presentScene(nextScene, transition: transition)
}
if nodesArray.first?.name == "easyButton" {
let nextScene = difScene(size: self.size)
self.view?.presentScene(nextScene, transition: transition)
}
if nodesArray.first?.name == "hardButton" {
let nextScene = difScene(size: self.size)
self.view?.presentScene(nextScene, transition: transition)
}
if nodesArray.first?.name == "backButton" {
let nextScene = menuScene(size: self.size)
self.view?.presentScene(nextScene, transition: transition)
}
}
}
}
In case it may be useful, here is my GameViewController.swift:
import UIKit
import SpriteKit
import GameplayKit
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let scene = GKScene(fileNamed: "menuScene") {
// Get the SKScene from the loaded GKScene
if let sceneNode = scene.rootNode as! menuScene? {
// Set the scale mode to scale to fit the window
sceneNode.scaleMode = .aspectFill
// Present the scene
if let view = self.view as! SKView? {
view.presentScene(sceneNode)
view.ignoresSiblingOrder = true
view.showsFPS = false
view.showsNodeCount = false
}
}
}
}
override var shouldAutorotate: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .allButUpsideDown
} else {
return .all
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
override var prefersStatusBarHidden: Bool {
return true
}
}
The error I get is:
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
I know this is not a unique error. I have Google'd, watched tutorials and tried to derive a solution from other StackOverflow threads but failed to overcome this.
The error is on this line
tutButton = self.childNode(withName: "tutButton") as! SKSpriteNode // error
because there is no child node with the name "tutButton", so force casting it to an SKSpriteNode causes the error.
When the play button is pressed, you call this code:
let nextScene = difScene(size: self.size)
self.view?.presentScene(nextScene, transition: transition)
This initializes a new difScene and presents it, which in turn calls the line of code above which is crashing.
Do you have a corresponding sks file for the difScene where you add the four buttons you reference in didMove?
If so, you'll need to initialize the difScene with the sks file like you did for your menu scene:
let scene = GKScene(fileNamed: "difScene")
If not, you'll need to create those four button nodes and add them as children to your scene programatically.
Side note: In Swift, the convention is to name types starting with capital letters. Your menuScene should be MenuScene, and your difScene should be DifScene. This will make it much easier for others to read and understand your code.

WatchKit Move a SimpleSpriteNode in SpriteKit Game

I would like to know if anyone has a way to move a SKSpriteNode in SpriteKit Watch Game Using WKCrownDelegate. either in Y direction or X direction
Hope this Helps Others that are Starting with WatchKit.
This is my GameElement.swift:
extension GameScene {
func addPlayer() {
player = SKSpriteNode(imageNamed: "Spaceship")
player.setScale(0.15)
player.position = CGPoint(x: 5, y: -60)
player.name = “ONE”
player.physicsBody?.isDynamic = false
player.physicsBody = SKPhysicsBody(rectangleOf: player.size)
player2 = SKSpriteNode(imageNamed: "Spaceship")
player2.setScale(0.15)
player2.position = CGPoint(x: 5, y: -60)
player2.name = “ONE”
player2.physicsBody?.isDynamic = false
player2.physicsBody = SKPhysicsBody(rectangleOf: player2.size)
addChild(player)
addChild(player2)
playerPosition = player.position
}
}
This is my GameScene.swift:
class GameScene: SKScene, SKPhysicsContactDelegate, WKCrownDelegate {
var watchParticles:SKEmitterNode!
var player:SKSpriteNode!
var player2:SKSpriteNode!
var playerPosition:CGPoint!
override func sceneDidLoad() {
self.scaleMode = SKSceneScaleMode.aspectFill
watchParticles = SKEmitterNode(fileNamed: "watchParticles")
addChild(watchParticles)
self.physicsWorld.gravity = CGVector(dx: 0 , dy: 0)
physicsWorld.contactDelegate = self
addPlayer()
}
func moveSprite(player : SKSpriteNode,moveDirection: String){
switch moveDirection {
case "UP":
print("UP")
player.childNode(withName: "ONE")?.physicsBody?.applyImpulse(CGVector(dx: 60, dy: 0))
case "DOWN":
print("DOWN")
player.childNode(withName: "ONE")?.physicsBody?.applyImpulse(CGVector(dx: -60, dy: 0))
case "STOP":
print("STOPPED")
player.childNode(withName: "ONE")?.physicsBody?.velocity = CGVector(dx: 0, dy: 0)
default:
break
}
}
}
This is My InterfaceController.swift:
class InterfaceController: WKInterfaceController, WKCrownDelegate {
#IBOutlet var skInterface: WKInterfaceSKScene!
private var moveDirection = ""
private var game = GameScene()
private var player = GameScene()
override func awake(withContext context: Any?) {
super.awake(withContext: context)
crownSequencer.delegate = self
crownSequencer.focus()
// Configure interface objects here.
// Load the SKScene from 'GameScene.sks'
if let scene = GameScene(fileNamed: "GameScene") {
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
// Present the scene
self.skInterface.presentScene(scene)
crownSequencer.delegate = self
crownSequencer.focus()
// Use a value that will maintain a consistent frame rate
self.skInterface.preferredFramesPerSecond = 30
}
}
func crownDidRotate(_ crownSequencer: WKCrownSequencer?, rotationalDelta: Double) {
if rotationalDelta > 0{
moveDirection = "UP"
game.moveSprite(player: player.player, moveDirection: moveDirection)
}else if rotationalDelta < 0{
moveDirection = "DOWN"
game.moveSprite(player: player.player, moveDirection: moveDirection)
}
}
func crownDidBecomeIdle(_ crownSequencer: WKCrownSequencer?) {
moveDirection = "STOP"
game.moveSprite(player: player.player, moveDirection: moveDirection)
}
override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
}
override func didDeactivate() {
// This method is called when watch view controller is no longer visible
super.didDeactivate()
}
}
Welcome to SO!
Ok, there is a lot to unpack so get some popcorn... I think that you are on the right track here, mostly you need to check for nil when you get errors.
First, this is wrong in your interface controller, and the part that concerned me. Here, you are just instantiating new GameScene instances, which are completely separate from the gameScene instance created by your interface controller a few lines down. Then, you were sending the crown delegate functions to these totally empty gameScenes.:
private var game = GameScene() // You are referencing nothing here, just creating a new gamescene.
private var player = GameScene() // I don't think that player is supposed to be a gamescene!
I fixed it by doing assigning the actual gameScene you want to use to the properties (so they can be used by the crown delegate).
private var game: GameScene!
lazy private var player: SKSpriteNode = self.game.player
override func awake(withContext context: Any?) {
// ... Stuff...
if let scene = GameScene(fileNamed: "GameScene") {
game = scene
This was also changed to represent the new code in your crown delegates:
game.moveSprite(player: player, moveDirection: moveDirection)
In addPlayer you were doing this:
player.physicsBody?.isDynamic = true // This needs to go AFTER you init your pb.
player.physicsBody = SKPhysicsBody(rectangleOf: player.size)
...and I fixed it by swapping the lines.
Personally I like to do the following, to help ensure no small mistakes are made:
let pb = SKPhysicsBody(...)
pb.isDynamic = true
player.physicsBody = pb
moveSprite had a bunch of issues with it, so I'm not going to enumerate them as I did above. Check out what I did then ask me if you have any questions. Basically, you were doomed with this func from the start, because you were calling this method from the interface controller with the whacked out player values that were all nil.
Also, the .applyImpulse was giving me pretty bad controls, so I changed it to a plain adjustment of .position. There is still a small issue with coasting before the player stops, but that can be handled in another question :) (note, I only tested this on simulator.. may not be an issue on-device).
Also also, I hate errors caused by spelling mistakes in strings, so I converted this to an enum for you.
func moveSprite(player : SKSpriteNode, moveDirection: Direction) {
// This will give us an equal amount of pixels to move across the watch devices:
// Adjust this number for shorter / longer movements:
let percentageOfScreenToMovePerRotation = CGFloat(1) // One percent
let modifier = percentageOfScreenToMovePerRotation / 100
let amountToMove = self.frame.maxX * modifier
switch moveDirection {
case .UP:
player.position.x += amountToMove
case .DOWN:
player.position.x -= amountToMove
case .STOP:
break
}
}
The real moral of the story here is to check for nil. If you just use someOptional?.someMethod() all the time, then you likely will not be able to easily determine whether or not someMethod() is actually being called or not.. thus, you don't know if the problem is with the calling logic, the method, or with the object not existing, and etc.
Force unwrapping is frowned upon in production code, but IMO it is extremely valuable when first starting out--because it helps you to quickly identify errors.
Later on, you can start using things like if let and guard to help check for nil without crashing your programs, but that adds more clutter and complexity to your code when you are trying to just learn the basics of a new API and language.
And as a final tip, try to not use hard-coded strings whenever possible: put them into an enum or a constant as I have in your code:
// Because I hate string spelling erros, and you probably do too!
enum Direction {
case UP, DOWN, STOP
}
// Because I hate errors related to spelling in strings:
let names = (ONE: "ONE", TWO: "TWO")
Here are the two files in their entirety.. note, I had to comment out a few things to get it to work in my project:
GameScene:
// Because I hate string spelling erros, and you probably do too!
enum Direction {
case UP, DOWN, STOP
}
class GameScene: SKScene, SKPhysicsContactDelegate, WKCrownDelegate {
var watchParticles:SKEmitterNode!
var player: SKSpriteNode!
var player2: SKSpriteNode!
var playerPosition:CGPoint!
// Because I hate errors related to spelling in strings:
let names = (ONE: "ONE", TWO: "TWO")
func addPlayer() {
player = SKSpriteNode(color: .blue, size: CGSize(width: 50, height: 50))
// player = SKSpriteNode(imageNamed: "Spaceship")
// player.setScale(0.15)
player.position = CGPoint(x: 5, y: -60)
player.name = names.ONE
player.physicsBody = SKPhysicsBody(rectangleOf: player.size)
player.physicsBody!.isDynamic = true // This was placed *before* pb initialzier (thus never got called)
player2 = SKSpriteNode(color: .yellow, size: CGSize(width: 50, height: 50))
// player2 = SKSpriteNode(imageNamed: "Spaceship")
// player2.setScale(0.15)
player2.position = CGPoint(x: 5, y: -60)
player2.name = names.TWO
player2.physicsBody = SKPhysicsBody(rectangleOf: player2.size)
player2.physicsBody!.isDynamic = false // This was placed *before* pb initialzier (thus never got called)
addChild(player)
addChild(player2)
playerPosition = player.position
}
override func sceneDidLoad() {
self.scaleMode = SKSceneScaleMode.aspectFill
//watchParticles = SKEmitterNode(fileNamed: "watchParticles")
//addChild(watchParticles)
self.physicsWorld.gravity = CGVector.zero
physicsWorld.contactDelegate = self
addPlayer()
}
func moveSprite(player : SKSpriteNode, moveDirection: Direction) {
// This will give us an equal amount of pixels to move across the watch devices:
// Adjust this number for shorter / longer movements:
let percentageOfScreenToMovePerRotation = CGFloat(1) // One percent
let modifier = percentageOfScreenToMovePerRotation / 100
let amountToMove = self.frame.maxX * modifier
switch moveDirection {
case .UP:
player.position.x += amountToMove
case .DOWN:
player.position.x -= amountToMove
case .STOP:
break
}
}
}
InterfaceController:
class InterfaceController: WKInterfaceController, WKCrownDelegate {
#IBOutlet var skInterface: WKInterfaceSKScene!
private var moveDirection = Direction.STOP
private var game: GameScene!
lazy private var player: SKSpriteNode = self.game.player
override func awake(withContext context: Any?) {
super.awake(withContext: context)
crownSequencer.delegate = self
crownSequencer.focus()
if let scene = GameScene(fileNamed: "GameScene") {
game = scene // VERY IMPORTANT!
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
// Present the scene
self.skInterface.presentScene(scene)
crownSequencer.delegate = self
crownSequencer.focus()
// Use a value that will maintain a consistent frame rate
self.skInterface.preferredFramesPerSecond = 30
}
else {
fatalError("scene not found")
}
}
func crownDidRotate(_ crownSequencer: WKCrownSequencer?, rotationalDelta: Double) {
if rotationalDelta > 0{
moveDirection = .UP
game.moveSprite(player: player, moveDirection: moveDirection)
} else if rotationalDelta < 0{
moveDirection = .DOWN
game.moveSprite(player: player, moveDirection: moveDirection)
}
}
func crownDidBecomeIdle(_ crownSequencer: WKCrownSequencer?) {
moveDirection = .STOP
game.moveSprite(player: player, moveDirection: moveDirection)
}
override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
}
override func didDeactivate() {
// This method is called when watch view controller is no longer visible
super.didDeactivate()
}
}

Change the text of a SKLabelNode added from the scene editor

I want to change the text of a SKLabelNode created in the scene editor named myScoreLabel to update a score on collision of two objects. Here's the relevant code:
class holeOne: SKScene, SKPhysicsContactDelegate {
var myScoreLabel: SKLabelNode!
var myscore:Int = 0
func addScore() {
myscore += 1
myScoreLabel.text = "\(myscore)"
}
func didBegin(_ contact: SKPhysicsContact) {
addScore()
}
}
At the moment after the collision the app crashes with "unexpectedly found nil while unwrapping an Optional value". What am I doing wrong and how can I do it right? Thanks!
From code that you provide var myScoreLabel: SKLabelNode! is not created.
Try to create SKLabelNode firstly. And then set value.
Example:
myScoreLabel = SKLabelNode(fontNamed: "Chalkduster")
myScoreLabel.text = "Test"
myScoreLabel.horizontalAlignmentMode = .right
myScoreLabel.position = CGPoint(x: 0, y:10)
addChild(scoreLabel)
Or you can connect it from .sks scene.
override func sceneDidLoad() {
if let label = self.childNode(withName: "myScoreLabel") as? SKLabelNode {
label.text = "Test" //Must add '.text' otherwise will not compile
}
}
Great, so in the end I did this:
class holeOne: SKScene, SKPhysicsContactDelegate {
var myScoreLabel: SKLabelNode!
var myscore:Int = 0
func addScore() {
myscore += 1
if let myScoreLabel = self.childNode(withName: "myScoreLabel") as? SKLabelNode {
myScoreLabel.text = "\(myscore)"
}
}
func didBegin(_ contact: SKPhysicsContact) {
addScore()
}
}

Segue between SKScene and UIViewController

I'm writing simple math game with swift 3 and I have a problem with segues. I have two GameViewController's, oneUIViewController and one NavigationViewController.
I want to make two menus, one with game modes and one with difficulties, I want that the difficulties menu be interactive and have gravity so I decided to make it in SkScene. Everything works fine except segue between SkScene with dark screen and difficulties menu when user lost the game. I write a protocol which works when I'm going from main menu to second menu but when I'm going from SkScene with game it doesn't. I was trying to make a manual segue but protocol doesn't want to execute.
MyGVViewController code:
import UIKit
import SpriteKit
import GameplayKit
enum skad1 {
case game,menu
}
var skad = skad1.gra
class MyGVViewController: UIViewController, GameViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
// Load the SKScene from 'GameScene.sks'
if let scene = SKScene(fileNamed: "Menugame") {
let gameScene = scene as! MySKScene
gameScene.gameViewControllerDelegate = self
gameScene.scaleMode = .aspectFill
gameScene.size = view.bounds.size
// Present the scene
view.presentScene(gameScene)
}
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
override var shouldAutorotate: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .allButUpsideDown
} else {
return .all
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
override var prefersStatusBarHidden: Bool {
return true
}
func goback() {
print("goback executed")
let view = self.view as! SKView?
view?.presentScene(nil)
navigationController?.popViewController(animated: true)
}
func goback2() {
print("goback2 executed")
let view = self.view as! SKView?
view?.presentScene(nil)
self.performSegue(withIdentifier: "comeback", sender: nil)
}
}
My SkScene with difficulties menu code:
import GameKit
import SpriteKit
import CoreMotion
protocol GameViewControllerDelegate: class {
func goback()
func goback2()
}
class MySKScene: SKScene {
//Variables
var motionManager: CMMotionManager!
var ball = SKSpriteNode()
var ball1 = SKSpriteNode()
var ball2 = SKSpriteNode()
var ball3 = SKSpriteNode()
var ball4 = SKSpriteNode()
weak var gameViewControllerDelegate: GameViewControllerDelegate?
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
if ball.contains(location){
showgame(zakres: 200)
}else if ball1.contains(location){
showgame(zakres: 100)
}else if ball2.contains(location) {
showgame(zakres: 50)
}else if ball3.contains(location) {
showgame(zakres: 150)
}else if ball4.contains(location) {
if skad == .menu{
print("should execut goback")
self.gameViewControllerDelegate?.goback()
}else{
print("should execut goback2")
self.gameViewControllerDelegate?.goback2()
}
}
}
}
func showgame(zakres:Int) {
if let view = self.view {
range = zakres
// Load the SKScene from 'GameScene.sks'
if let scene = SKScene(fileNamed: "GameScene") {
// Set the scale mode to scale to fit the window
let gameScene = scene as! GameScene
gameScene.scaleMode = .aspectFill
// Present the scene
gameScene.size = view.bounds.size
view.presentScene(gameScene)
}
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
override func didMove(to view: SKView) {
ball = self.childNode(withName: "ball") as! SKSpriteNode
ball1 = self.childNode(withName: "ball1") as! SKSpriteNode
ball2 = self.childNode(withName: "ball2") as! SKSpriteNode
ball3 = self.childNode(withName: "ball3") as! SKSpriteNode
ball4 = self.childNode(withName: "ball4") as! SKSpriteNode
motionManager = CMMotionManager()
motionManager.startAccelerometerUpdates()
let border = SKPhysicsBody(edgeLoopFrom: self.frame)
border.friction = 0
border.restitution = 0
self.physicsBody = border
}
override func update(_ currentTime: TimeInterval) {
if let accelerometerData = motionManager.accelerometerData {
physicsWorld.gravity = CGVector(dx: accelerometerData.acceleration.x * 20, dy: accelerometerData.acceleration.y * 35)
}
}
}
PS.
I'm reading what I wrote and I have to correct it, in difficulties menu I have a button which execute protocol that hide SKScene and show main menu. It works only when I'm going from main menu but when segue is from game when user lost, protocol isn't executed

Resources