Difference between GameViewController and SKScenes - swift

I've been developing a game using SpriteKit and Swift but I seem to be having trouble determining what the real differences are between the GameViewController and any one of my SKScenes. I'm trying to understand the differences because I want to implement a GameCenter or local leaderboard into my game but in all the tutorials I find (like this one:Game Center Leaderboards! (Swift 2 in Xcode) ) they have all the logic in GameViewController as they are working with single view apps. I'm having trouble understanding the relation when I read the docs, so any help would be great. Ultimately, I want to be able to display and push data to and from GameCenter in one of my scenes such as GameOverScene. Thanks for any help!

Here is some good info to start with:
Diagram of what happens each frame in SK:
So you see, the SKScene is the class with all of the fun stuff like Nodes and Actions, and is where everything (important to you) happens. You can generate these scenes through the Editor, but then you probably need to make a new .swift file to go with it (as each scene can have its own logic).
The editor is just a 'shortcut' to initializing a bunch of stuff, and honestly, you can make complete games with little code (but you very quickly find out that you want more)
So in this code, where you declare GameScene or PauseScreen (which are basically just class declarations, that inherit from SKScene), you quickly find this line talking about something that ISNT a scene:
override func didMoveToView(view: SKView)
.. it's calling a SKView... what is that, and where did it come from?
(Read about SKView here, and look at its inheritance):
https://developer.apple.com/library/ios/documentation/SpriteKit/Reference/SKView/index.html#//apple_ref/occ/cl/SKView
We find this SKView declaration in the GameViewController file, (which is just a class), notice that it's the same as the regular iOS apps mostly, as it inherits UIViewController:
override func viewDidLoad() {
super.viewDidLoad()
if let scene = GameScene(fileNamed:"GameScene") {
// Configure the view.
let skView = self.view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = true
/* Set the scale mode to scale to fit the window */
scene.scaleMode = .AspectFill
skView.presentScene(scene)
}
Again, that method is declared in GameViewController.swift, which is basically just this:
class GameViewController: UIViewController
So how does all of this relate to iOS apps and SpriteKit? Well, they are all mashed on top of each other:
IOS app anatomy:
Basically, from right to left, you have the Window, which is (correct me if wrong) the AppDelegate, then the ViewController, then your View, which has all of the cool stuff in it (Storyboards sit inside of the View, just as SKScenes sit inside of the View.... Labels, Nodes, or Buttons, all sit inside of their respective classes ((the view)))
It's all a big sandwich of inheritance.
Check out the Apple websites for more info.
https://developer.apple.com/library/safari/documentation/UserExperience/Conceptual/MobileHIG/ContentViews.html#//apple_ref/doc/uid/TP40006556-CH13-SW1
https://developer.apple.com/spritekit/
https://developer.apple.com/library/ios/documentation/SpriteKit/Reference/SpriteKitFramework_Ref/
https://developer.apple.com/library/safari/documentation/UserExperience/Conceptual/MobileHIG/Anatomy.html
Basically, everything is an Class inherited from a class inherited from a class and so on, so on... It can get messy. You can also see these inheritances in Xcode by CMD+clicking on them, which will jump you to the source file.
Goodluck with your studies and adventures in SpriteKit :)

You should only have a single GameViewController in your game. SKScenes are the scenes that the game transitions in between an to.
For example, the home menu screen? That's an SKScene. The main gameplay? That's an SKScene. The gameover screen? That's an SKScene.
The GameViewController initializes the entire view that the game will be maintained in, so the view. The SKScenes are just scenes that are placed on top of the view. You should be looking at a tutorial that uses SKScenes.
Here's how to make game center work as of the latest Swift 2.2.
Add this function anywhere inside the GameViewController class, and then just call it right after super.viewDidLoad().
func authenticateLocalPlayer() {
let localPlayer = GKLocalPlayer.localPlayer()
localPlayer.authenticateHandler = {(viewController, error) -> Void in
if (viewController != nil) {
self.presentViewController(viewController!, animated: true, completion: nil)
}
else {
print((GKLocalPlayer.localPlayer().authenticated))
}
}
}
Add the following functions in your SKScene class file. Don't forget to import GameKit. Just call showLeader() whenever you want the leaderboard to be displayed.
func showLeader() {
let viewControllerVar = self.view?.window?.rootViewController
let gKGCViewController = GKGameCenterViewController()
gKGCViewController.gameCenterDelegate = self
viewControllerVar?.presentViewController(gKGCViewController, animated: true, completion: nil)
}
func gameCenterViewControllerDidFinish(gameCenterViewController: GKGameCenterViewController) {
gameCenterViewController.dismissViewControllerAnimated(true, completion: nil)
}
And this is a sample I have of how the score is saved to game center.
func saveHighscore(gameScore: Int) {
print("Player has been authenticated.")
if GKLocalPlayer.localPlayer().authenticated {
let scoreReporter = GKScore(leaderboardIdentifier: "YOUR_LEADERBOARD_ID")
scoreReporter.value = Int64(gameScore)
let scoreArray: [GKScore] = [scoreReporter]
GKScore.reportScores(scoreArray, withCompletionHandler: {error -> Void in
if error != nil {
print("An error has occured: \(error)")
}
})
}
}

It all depends about how you are designing your app, and what technologies you want to use.
If you are looking to build an app in 100% Sprite Kit, then you treat your UIViewController as a shell that holds your Sprite Kit app. The only time you should be touching this is when you need to do things that the SpriteKit scene shouldn't be doing, like creating gesture controls and what not.
However, there are uses to having multiple view controllers with sprite kit elements. Perhaps you are making a business application, and decide to include a little game to go with it.
IMO the best way to think about it in terms of web design is think of your View controller as your HTML page, and think of your Scene as your flash/silverlight/unity/etc game player that you embed in the website. Sometimes you want that player to be full screen, some times you do not, it comes down to the design of the application. When in full screen, we do not need any other components, so the player can do all the work. But what if we attach a how to guide link on the page. We wouldn't want this in game, we want this outside of it. This link will then open up a new page, not associated with the old page, and has no use for the game player components.
Now for your situation with Game Center, it gets more complicated. Game Center was built before Sprite Kit came to existence, so all of its functionality is built on UIKit. But Game Center also allows for customization, so you do not have to use the UIKit features of it. Of course, you will have to do all of the work then in displaying the information inside of your scene with Sprite Kit objects.
To make life easiest for you, you would include all of the built in code needed into your View Controller, then what you do is create a delegate that the scene knows about, and assign your view controller to this delegate. Now Game Scene can access any element of that view controller that you allow, like presenting leader boards or passing up leader boards. Check out this tutorial in its entirety, it will help you learn all you will need to achieve what you want.
https://www.raywenderlich.com/115300/swift-2-tutorial-part-3-tuples-protocols-delegates-and-table-views

In MVC the controller acts as a coordinator, a bit like the conductor in an orchestra. My preference is that scenes just do the one thing they were designed for i.e. implement game play. When a scene is completed, the final task is to notify the controller (using delegate pattern) that the scene is complete. It is then up to the controller to decide what happens next i.e. transition to next scene or game over.

Related

SceneKit and SpriteKit together

I'm building a 3D game scene with SceneKit and I want to build like a "hud" over top, in order to get it kind of working, I built my ViewController by using a subview and overlaySKScene like so:
class ViewController:UIViewController {
var sceneView: SCNView!
var mainScene = MainScene() // <- SCNScene
var spriteScene: OverlayScene! // <- SKScene
override func viewDidLoad() {
super.viewDidLoad()
self.sceneView = SCNView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height))
self.sceneView.scene = mainScene
self.sceneView.backgroundColor = UIColor.clear
self.view.addSubview(self.sceneView)
self.spriteScene = OverlayScene(size: self.view.bounds.size)
self.sceneView.overlaySKScene = self.spriteScene
}
}
I've got a few questions about how this should be done the right way, as I'm running into some things that seem unusual.
First, lets say I need to do a segue, it doesn't seem clear to me how I would actually observe a tap on one of the SpriteKit nodes in the OverlayScene and then change controllers for the main parent ViewController. I haven't found any resources online that describe a similar situation.
Here is a trimmed down version of my OverlayScene:
import UIKit
import SpriteKit
class OverlayScene: SKScene {
var actionBarNode: SKSpriteNode!
override init(size: CGSize) {
super.init(size: size)
self.backgroundColor = UIColor.clear
self.actionBarNode = SKSpriteNode()
self.actionBarNode.size = CGSize(width: size.width, height: 100)
self.actionBarNode.position = CGPoint(x: size.width / 2, y: 50)
self.actionBarNode.color = .white
self.addChild(self.actionBarNode)
}
}
I am not answering your two questions directly, instead I will deal with your larger issue which is that you want a HUD on your game. A user interface. I never made a Scenekit game, but I did make a Spritekit game and the HUD is a clever trick. Let me explain.
Imagine you are standing behind a camera and looking through to see what the camera is seeing through its lens. You see the scene in front of it. Now take a piece of paper, cut out a rectangular box in the middle and draw on the remaining periphery. Tape this piece of paper to the front of the camera and look through. You still see the scene (a little smaller now), but now you also see the paper with the drawings. This is how HUDs work for the game.
I did some searching and both SpriteKit and SceneKit have a camera. In sceneKit you create a camera node and set the point of view of the scene to that camera. This represents the player looking out into the scene.
I am not sure if it works the same way in SceneKit, but in SpriteKit you can add nodes to the camera that will be locked in place. These become your HUD elements (buttons, score, etc). Since these are all child nodes of the camera they move when you move the camera so it feels like they are a static HUD. This is how you achieve a HUD. Read up on the documentation for SceneKit cameras as this will help you understand how to achieve the same effect.
I once also thought about doing it the way you are going now and it proved to be a very painful experience to mix SceneKit and SpriteKit so I would caution against it.
Also, while you can mix Interface Builder and SpriteKit I would caution against it. It basically ruined my first game into a buggy mess. If you do decide to use Interface Builder, try to limit it to menu screens and options screens. But, I found that programmatically creating everything gives you a much smoother, cleaner, and bug free experience.
Hope this helps!
I found a solution for my problem for anyone who may stumble into this same issue (which I think is likely as many examples demonstrate an overlay scene added as a subview in the manner I did.
First the tapHandler I had in my viewController needed to have the property cancelsTouchesInView set to false. This will allow the gesture to be received by the underlying views.
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
tapGesture.cancelsTouchesInView = false
sceneView.addGestureRecognizer(tapGesture)
The reason this doesn't happen to all subviews is because I was relying on a view that was sized to 100% of the frame, covering the underlying ones, rather than just having the view occupy a smaller screen area, like a button or scoreboard as seen in the example docs.

Game controller layout detection - swift

How do I detect the game controllers layout from apple tv? I want to change the controls if the layout is different for the controller which will make the game easier to play. .For example, the apple recommended Nimbus Controller is shaped like a play station controller with two joysticks at the bottom, but I have seem other types of controllers which have a xbox like design with a d-pad and a joystick at the bottom instead, if I can detect which is which, to change to controls for different controllers, it will make the game easier to play
Any help would be appreciated
You should use controller profiles to map physical controls to game inputs.
Controllers are automatically discovered, a physical controller is represented by a GCController object, which ‘profiles’ the controllers controls such as GCGamepad, extendedGamepad etc. You should check which controls are registered to each controller. From the documentation on Discovering And Connecting Controllers:
“After your app has finished launching, the operating system
automatically creates a list of connected controllers. Call
the controllers class method to retrieve an array
of GCController objects for all connected controllers.”
In apples sample code they register for .GCControllerDidConnect Notifications and cast the notification object as a GCController instance to a function that set’s up the controls if they exist, parse the controller and assign the corresponding handler method:
NotificationCenter.default.addObserver(self, selector: #selector(GameViewController.handleControllerDidConnectNotification(_:)), name: .GCControllerDidConnect, object: nil)
#objc func handleControllerDidConnectNotification(_ notification: NSNotification) {
let gameController = notification.object as! GCController
registerCharacterMovementEvents(gameController)
}
private func registerCharacterMovementEvents(_ gameController: GCController) {
//…
// Gamepad D-pad
if let gamepad = gameController.gamepad {
gamepad.dpad.valueChangedHandler = movementHandler
}
// Extended gamepad left thumbstick
if let extendedGamepad = gameController.extendedGamepad {
extendedGamepad.leftThumbstick.valueChangedHandler = movementHandler
}
//…
}
I ended up just simply asking the user for their game controller layout. The answer from Ercell0 is a nice way to connect and use the game controllers, but doesn't really answer my question.

How to change the default SKScene that displays on startup

I've been developing a game that has been done in the single default GameScene created by XCode. Now I want to add more screens including a title screen that displays before the GameScene does and a 'Game Over' scene. The game over scene is transitioned from the GameScene and back and works fine but I cant get the title screen to display despite altering the code in GameViewController.swift:
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//Original default code:
//if let scene = GameScene(fileNamed:"GameScene") {
//modified code
if let scene = Titles(fileNamed:"Titles") {
//<...more code...>
}
The app still runs but just displays a blank screen. I note that the GameScene file also has a GameScene.sks which I think is a graphical representation of the scene content. Do I need to create another .sks or link this to the new default scene even though the scene content is being added programmatically.
I could simply move all the code from GameScene to a new SKScene and use GameScene as the titles but I was curious about the problem and how to resolve it. Any ideas?
Many Thanks,
Kw
Yes - initialising a scene with 'fileNamed' refers to a .sks file, so you should create one.
In this line of code:
let scene = Titles(fileNamed:"Titles")
you are create a new scene object called scene of type Titles. This should match the class name in your swift file i.e. in titles.swift you should have:
class Titles: SKScene {
The fileNamed: is just one of many initialisers for SKSCene and this specifies a .sks file to load. If you want to place all content in your scene programmatically, perhaps use:
let scene = Titles(size: CGSize(width: 1536, height: 2048))
This question and it's accepted answer might help with any potential problems you might encounter - How to add an .sks files to existing Swift/Sprite-Kit project?

Swift: SKSpriteKit, using Storyboards, UIViewController and UIButton to set in game parameters?

Context
The default code that comes with a new SpriteKit game has a storyboard such that - following the launch screen - all there is, is a GameViewController which calls forth the GameScene. However, this may be less ideal for many games. For example, one may wish to have the user select difficulty from a main menu, and then go to the GameScene - outlined below:
Notably, the middle view controller is a custom class MyUIViewController so that the UIButtons "easy" and "hard" can have the following IBActions:
#IBAction func setGameDifficultyToEasy(sender: AnyObject) {
gameDifficulty = "easy"
print("Game difficulty set to \(gameDifficulty)")
}
#IBAction func setGameDifficultyToHard(sender: AnyObject) {
gameDifficulty = "hard"
print("Game difficulty set to \(gameDifficulty)")
}
where gameDifficulty is a global variable, that the GameScene utilizes to determine aspects of the game play.
In the M.W.E. setting gameDifficulty to "hard" causes there to be three sprites on the screen, whereas setting gameDifficulty to "easy" puts forth only two.
Easy
Hard
Question
In the following gif, one sees that:
gameDifficuly is initialized as "hard"
the UIButton "Easy" was selected.
This can be seen be the printout statements.
Interestingly, although the UIButton was pressed first then the GameViewController was called, the changing of the parameter gameDifficulty was not set until after the GameScene was rendered.
**How can I get the UIButtons to set the parameter gameDifficulty prior to GameScene being called?
Minimum Working Example
Stack Overflow SKSpriteKit
Note
My answer here shows that this is clearly possible, but by relegating everything to SKViews rather than using a storyboard. So if possible, please keep answers related to using storyboards.

Trouble subclassing SCNScene

I've been trying to subclass SCNScene as that seems like the best place to keep my scene related logic. Now I'm not sure wether that's reccomended so my first question is - Should I be subclassing SCNScene, and if not why not? The documentation seems to suggest it's common but I've read comments online that suggest I shouldn't subclass it. Scrap that, I was looking at the documentation for SKScene. The SCNScene class reference makes no mention of subclassing.
Assuming it's ok to structure my game that way, here's my progress
// GameScene.swift
import Foundation
import SceneKit
class GameScene: SCNScene {
lazy var enityManager: BREntityManager = {
return BREntityManager(scene: self)
}()
override init() {
print("GameScene init")
super.init()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func someMethod() {
// Here's where I plan to setup the GameplayKit stuff
// for the scene
print("someMethod called")
}
}
Note: I'm using lazy var as per the answer to this question
In my view controller, i'm trying to use GameScene like this
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// create a new scene
let scene = GameScene(named: "art.scnassets/game.scn")!
// retrieve the SCNView
let scnView = self.view as! SCNView
// set the scene to the view
scnView.scene = scene
// Should print "someMethod called"
scene.someMethod()
}
}
However, the call to GameScene.someMethod() triggers an EXEC_BAD_ACCESS error.
Also, if I omit the call to GameScene.someMethod, the scene loads correctly, but the overridden initializer in GameScene doesn't appear to be called.
I'm not sure what's going on here. It's clear there's something about subclassing in Swift that I've not understood. Or perhaps there' some aspect of order in which things are meant to run that I've missed.
Should I be subclassing SCNScene, and if not why not?
No, you don't need to subclass, and you're probably better off not subclassing. SCNScene is more like basic data/model classes (NSString, UIImage, etc) than like behavior/controller/view classes (UIViewController, UIControl, etc). That is, it's a general description of or container for something (in this case, a 3D scene) and doesn't really provide behavior. Since there's not much in the way of behavior, there's not much opportunity to override behavior with a subclass. (Nor is the class designed around subclass entry points meant to be overridden, like viewDidLoad and whatnot.)
While it's possible to subclass SCNScene, you don't gain much from doing so, and some APIs might not work as you expect...
However, the call to GameScene.someMethod() triggers an EXEC_BAD_ACCESS error.
Also, if I omit the call to GameScene.someMethod, the scene loads correctly, but the overridden initializer in GameScene doesn't appear to be called.
A .scn file is actually an NSKeyedArchiver archive, which means that the objects inside it have the same classes that were used to create it. when you call init(named:) on your SCNScene subclass, the superclass' implementation eventually calls through to NSKeyedUnarchiver unarchiveObjectWithData: to load the archive. Absent some unarchiver mangling, that method will instantiate a SCNScene, not an instance of your subclass.
Because you don't get an instance of your subclass, your subclass initializers aren't called, and attempting to call your subclass' methods results in a runtime error.
Aside: Why is SpriteKit different from SceneKit here? SKScene is a bit of an odd duck in that it's neither a pure model class nor a pure controller class. This is why you see a lot of projects, including the Xcode template, using an SKScene subclass. There are drawbacks to this approach, however — if you don't plan carefully, it gets too easy to wed your game logic tightly to your game data, which makes expanding your game (say, to multiple levels) require tedious coding instead of data editing. There's a WWDC 2014 session on this topic that's well worth watching.
So, what to do?
You have a few choices here...
Don't subclass. Keep your game logic in a separate class. This doesn't necessarily have to be a view controller class — you could have one or more Game objects that are owned by your view controller or app delegate. (This makes especially good sense if your game logic transitions between or manipulates the content of multiple scenes.)
Subclass, but arrange to put the stuff from an unarchived SCNScene into an instance of your subclass — instantiate your subclass, load an SCNScene, then move all children of its root node into your subclass' root node.
Subclass, but force NSKeyedUnarchiver to load your subclass instead of SCNScene. (You can do this with class name mappings.)
Of these, #1 is probably the best.
tl;dr: Extend SCNNode to generate your scene programmatically.
This started as a comment on Rickster's excellent answer but I could see it getting too long. I taught a course this quarter that spent a substantial amount of time on SceneKit. We subclassed SCNScene all the time and didn't run into any trouble.
However, after spending an hour just now reviewing the code I presented, the code the students wrote, and a ton of other sample code (from Apple and from other developers), I would not subclass SCNScene again.
None of the Apple sample code subclasses SCNScene. That should be a very strong clue. Handling the serialization (initWithCoder and the read from file) is a recurring question on StackOverflow. That's a second strong clue that you don't want to go there.
In all of the code I reviewed that subclassed SCNScene, the reason to do it was to generate the scene programmatically. That's Rickster's choice #2. But none of that code really needs to be on SCNScene. If you're building a bunch of nodes in particular configurations, adding lighting and look-at constraints, and setting up materials, it feels like you're building an SCNScene. But really you're building a tree of SCNNode instances.
So I think in hindsight, choice #2 does not provide a good enough reason to subclass. All of that construction can be done by writing a generator function, in an extension to SCNNode.
It's tempting to subclass SCNNode for this custom tree. Don't do it. You'll be able to save it and read it just fine (assuming you write the appropriate NSSecureCoding functions). But you will be unable to open your scene with the Xcode Scene Editor, because the Scene Editor doesn't know how to unarchive and instantiate your custom SCNNode subclasses.