SpriteKit Scene order - swift

I'm creating a game using Sprite Kit. I have my GameScene and GameOverScenenow I want to create a MenuScene Where do I control the order of the MenuScene so that it shows up after LaunchScene
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let scene = GameScene.unarchiveFromFile("MenuScene") as? GameScene {
// Configure the view.
let skView = self.view as! SKView
skView.ignoresSiblingOrder = true
scene.scaleMode = .AspectFill
skView.presentScene(scene)
}
}
Edit tried putting ("MenuScene") as the scene but when I run the game that scene won't show

Ok so I found out that I was one the right path. I just needed to change this line. Instead of this
if let scene = GameScene.unarchiveFromFile("MenuScene") as? GameScene {
add this
if let scene = MenuScene.unarchiveFromFile("MenuScene") as? MenuScene {

Related

Losing reference to GameViewController from inside SpriteKit when moving between scenes - want to move between SKScene and UITableView

I have a simple (multi scene) spritekit game and would like to implement and display a settings screen using a UITableViewContoller. I am close but keep losing my reference to the games view controller when i change scene, which means i can only segue to the UITableViewController on the first scene when my game starts. I'm hoping someone can help. I setup the UITableView in the storyboard and setup a segue to the tableview from my GameViewController in there.
I have the following set up in my GameViewController file:
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
// Load the SKScene
if let scene = SKScene(fileNamed: "MenuScene") {
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
//I'VE ADDED THIS
if let gameScene = scene as? MenuScene {
gameScene.viewController = self
}
// Present the scene
view.presentScene(scene)
}
view.ignoresSiblingOrder = true
}
}
func showSettings() {
performSegue(withIdentifier: "openSettings", sender: self)
}
which gives me a reference to the GameViewController from my SKScene. I then load the UITableViewController from my SKScene in spritekit when a user touches an SKLabelNode. A simpler version of my MenuScene code looks something like this:
class MenuScene: SKScene {
var viewController: GameViewController?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
{
let touch:UITouch = touches.first!
let positionInScene = touch.location(in: self)
let touchedNode = self.atPoint(positionInScene)
if let name = touchedNode.name
{
let transition:SKTransition = SKTransition.fade(withDuration: 0.75)
var scene:SKScene = SKScene()
if (name == "settingsBTN"){
viewController?.showSettings()
}
else if (name == "randomBTN"){
scene = StarScene(size: self.size)
scene.backgroundColor = SKColor.black
self.view?.presentScene(scene, transition: transition)
}
}
}
}//end of MenuScene
The table view is presented fine when i tap the settingsBTN when first starting the game. however, if i visit another scene in my game (e.g. by pressing randomBTN) and return back to the menu scene i can no longer segue to the Settings view as i have lost the reference to the viewcontroller (it shows as nil) so i can't call the showSettings method in the GameViewController that performs the segue (presumably because i set up the reference to the view controller in the view controller itself).
I can't figure out how to maintain the reference to my GameViewController so i can present the settings screen at any point. Please could someone help.
There are two simple methods to call a GameViewController function from other classes: through a delegate or by making the class instance static.
// DELEGATE
//
// GameViewController.swift
protocol GameView_Delegate
{
func show_ui()
}
class GameViewController: UIViewController, GameView_Delegate
{
override func viewDidLoad()
{
// ...
if let view = self.view as! SKView?
{
if let scene = SKScene(fileNamed: "MenuScene")
{
scene.scaleMode = .aspectFill
// pass GameViewController reference
scene.gv_delegate = self
}
view.presentScene(scene)
}
// ...
}
func show_ui()
{
// show ui
// ...
}
}
// MenuScene.swift
class MenuScene: SKScene
{
var gv_delegate : GameView_Delegate!
func settings()
{
// call function show_ui from GameViewController
gv_delegate.show_ui()
}
func game_scene()
{
let transition = SKTransition.fade(with: UIColor.white, duration: 1.0)
let next_scene = GameScene()
next_scene.anchorPoint = CGPoint(x: 0.5, y: 0.5)
next_scene.scaleMode = self.scaleMode
next_scene.size = self.size
// before moving to next scene, we must pass the delegate, so we
// don't lose the connection with the GameViewController
next_scene.gv_delegate = gv_delegate
self.view?.presentScene(next_scene, transition: transition)
}
}
// GameScene.swift
class GameScene: SKScene
{
var gv_delegate : GameView_Delegate!
func settings()
{
// call function show_ui from GameViewController
gv_delegate.show_ui()
}
func main_scene()
{
let transition = SKTransition.fade(with: UIColor.white, duration: 1.0)
let next_scene = MainScene()
next_scene.anchorPoint = CGPoint(x: 0.5, y: 0.5)
next_scene.scaleMode = self.scaleMode
next_scene.size = self.size
// before moving to next scene, we must pass the delegate, so we
// don't lose the connection with the GameViewController
next_scene.gv_delegate = gv_delegate
self.view?.presentScene(next_scene, transition: transition)
}
}
When you enter a new scene, for example GameScene, the old scene MainScene in our case is deallocated (all variables emptied, all references destroyed) and a new GameScene instance is created and displayed on screen. If afterwards you want to exit that scene and enter MainScene, a new instance of that class will be created with the GameViewController reference empty. We should reference it again or pass the reference before exiting the scene (as in my example).
// STATIC
//
// GameViewController.swift
class GameViewController: UIViewController
{
static var shared : GameViewController!
override func viewDidLoad()
{
super.viewDidLoad()
GameViewController.shared = self
// ...
}
func show_ui()
{
// show ui
// ...
}
}
// MenuScene.swift
class MenuScene: SKScene
{
func settings()
{
// call function show_ui from GameViewController
GameViewController.shared.show_ui()
}
}
Read pros/cons of each method and chose the one that suits your app the best. Also, avoid using UIKit inside SpriteKit.

Terminated due to memory issue - ViewControllers taking up memory

I'm developing a game using sprite kit and have various view controllers for different scenes. Like story mode, multiplayer etc...
I've noticed a problem where every time I open up a view controller with a SpriteKit scene, the memory usage for my app increases. And when the view is dismissed, that memory isn't allocated back?
Is there something I need to be doing in my code to return the memory the ViewController or SpriteKit scene used up upon loading?
EDIT: As requested, here is some code from when the VC loads up with a SpriteKit scene & the code used to dismiss the VC:
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
if let scene = SKScene(fileNamed: "GameScene.sks") {
scene.scaleMode = .aspectFill
view.presentScene(scene)
(scene as! GameScene).parentVC = self
}
}
}
For the view dismissal, I first present a loading screen over the UIApplication.shared.keyWindow and then dismiss:
dismiss(animated: true) {
LoadingScreen?.removeFromSuperview()
}
The loading screen class is handled in the AppDelegate.
I think it's an issue of retain cycles
1 if let view = self.view as! SKView? {
2 if let scene = SKScene(fileNamed: "GameScene.sks") {
3 scene.scaleMode = .aspectFill
4 view.presentScene(scene)
5 (scene as! GameScene).parentVC = self
}
}
if line 5, inside the closure, you're referencing self, so when the viewcontroller should be deallocated, the reference on line 5 is stopping the deallocation, Can you try this :
if let view = self.view as! SKView? {
if let scene = SKScene(fileNamed: "GameScene.sks") {
scene.scaleMode = .aspectFill
view.presentScene(scene)
guard let scene = scene as? GameScene else { return }
guard let scencePVC = scene.parentVC else { return }
scencePVC = self
}
}
I'm trying to fix the issue without knowing the object hierarchy but it's hard. Basically the issue is that you have 2 objects( viewcontrollers) referencing each other that would stop the deallocation.
You can read more about this issue here & here

Swift blank app

I want to learn SpriteKit and am following this tutorial, https://www.raywenderlich.com/187645/spritekit-tutorial-for-beginners-2
I have copied and pasted the same code from the tutorial into GameScene.swift as well as moved the picture of the ninja into Assets.xcassets
However, when I get two errors,
Before I run it I get
Forced cast of SkView to same type has no effect
After I run it I also get
Thread 1: EXC_BAD_ACCESS(code=2, address=0x7ffeed594fa8
When I run it, the app is simply blank... The app should show FPS count and NodeCount?
Here's my code:
import SpriteKit
import GameplayKit
class GameScene: SKScene {
override func didMove(to view: SKView) {
let scene = GameScene(size: view.bounds.size)
let skView = view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
skView.ignoresSiblingOrder = true
scene.scaleMode = .resizeFill
skView.presentScene(scene)
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}
All of this code is in GameScene.swift
If someone could please help me that would be great!
UPDATE: I am using Swift 4
It seems you have an infinite recursion there. Note that you already have a GameScene instance but in the didMove you are creating another GameScene which you are presenting again which again is presented in the view and didMove is called again and so on. I think your code should look something like this:
override func didMove(to view: SKView) {
self.size = view.bounds.size
view.showsFPS = true
view.showsNodeCount = true
view.ignoresSiblingOrder = true
self.scaleMode = .resizeFill
}

Changing the initial game scene in Swift

I want to change the initial scene being presented to be another class other than the default GameScene class. From reading other questions, I understand I must change this part from the GameViewController:
if let scene = SKScene(fileNamed: "GameScene") {
print(scene)
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
// Present the scene
view.presentScene(scene)
}
So within the GameScene.swift file I am creating a new class:
class MainMenu : SKScene {
override func didMove(to view: SKView) {
print("At least it ran")
self.scene?.view?.presentScene(GameScene())
}
}
However, when I change the scene to:
if let scene = SKScene(fileNamed: "MainMenu")
When I run the project, it gets stuck, but when I run it with the string "GameScene" then it works perfectly. I am doing something wrong loading the MainMenu?
In my opinion you should separate your scenes into their own files...
Do you have an corresponding SKS file for MenuScene? You need to create one if you are trying to load it with fileNamed:
or -
Use this code to load a SKScene file that is created in code only and not in the Scene editor
if let skView = self.view as? SKView {
if skView.scene == nil {
let scene = MenuScene(size: skView.bounds.size)
scene.scaleMode = .aspectFill
skView.presentScene(scene)
}
}
and then in your MenuScene file you will need an init func
init(size: CGSize) {
super.init(size: size)
name = "menuScene"
}

Swift -- Sprite Kit -- Scenes have different sizes

I am programming a game but I have one problem. I have two scenes. (1. GameScene 2. PlayScene). The GameScene displays the PlayButton. If you press the PlayButton the PlayScene will be displayed. The PlayScene displays the game. If you lose the Game, the GameScene will be presented again.
GameScene --> PlayScene --> GameScene
This is the code, which is in the GameScene and presents the PlayScene.
if self.nodeAtPoint(location) == self.playButton { // Wenn position des klicks gleich mit position des play button
println("Go to the game")
var scene = PlayScene(size: self.size)
let skView = self.view! as SKView
skView.ignoresSiblingOrder = true
scene.scaleMode = .ResizeFill
scene.size = skView.bounds.size
skView.presentScene(scene)
}
This is the code, which is in the PlayScene and presents the GameScene again.
if let scene = GameScene.unarchiveFromFile("GameScene") as? GameScene {
let skView = self.view as SKView!
skView.ignoresSiblingOrder = true
scene.size = skView.bounds.size
scene.scaleMode = .AspectFill
skView.presentScene(scene)
}
My Problem is that the GameScene has first a different size than the GameScene which is presented after the PlayScene. I can't show photos because I don't have 10 reputations -.-, but I can explain it. For example: The PlayButton in the GameScene, which is presented after the program starts, is smaller than the PlayButton, which is presented in the GameScene after the PlayScene is presented.
I tried to change the scaleModes but that didn't work.
Here are the links for the photos, which show my problem.
This is the "second" GameScene presented:
https://drive.google.com/open?id=0B57TI4Xur5Fldy1EcVh6NmVIdm8&authuser=0
This is the "first" GameScene presented:
https://drive.google.com/open?id=0B57TI4Xur5FlR3ZXT2NFSEZRZHc&authuser=0
The issue could be that you aren't using the same settings as you are for the first scene. Try this.
if let scene = GameScene.unarchiveFromFile("GameScene") as? GameScene {
let skView = self.view as SKView!
skView.ignoresSiblingOrder = true
scene.size = self.size
scene.scaleMode = self.scaleMode
skView.presentScene(scene)
}
Thank you but it didn´t worked. I tried some other changes and it worked but I don´t know why.
if let scene = GameScene.unarchiveFromFile("GameScene") as? GameScene {
let skView = self.view as SKView!
skView.ignoresSiblingOrder = true
scene.scaleMode = .AspectFill
skView.presentScene(scene)
}
Now both Scenes have the same size.