I just created a new SpriteKit project, and right out of the door bam! problem. I ran the project without touching anything, and as it should be, the default hello world project appeared. All good on that front. Now, I created a new Coco Touch Class and named it test with the superclass of SKScene. Then, in the GameViewController, I changed ONLY the following line
if let scene = GameScene(fileNamed: "GameScene") {
to
if let scene = GameScene(fileNamed: "test") {
and the game crashed on load.
Am I messing up somewhere when I create the new scene, did I miss something? Any suggestions would be helpful, thanks.
Thats because that line of code of code runs an initializer that unarchives a file from a .sks file, which you didn't create yet for the scene. You probably have a GameScene .sks configured but not a test.sks.
You need to create a .sks file if you want to initialize your first scene like that. Otherwise, just create a scene without unarchiving .sks using SKScene initializer initWithSize:
Related
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?
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.
When using Xcode, I've faced the annoying problem, which is Xcode always crashes when I click .SKS files. I have raised questions here, and even in Apple developer forum, as well as searched for the solutions on the Internet... but it is hopeless.
Because I am making a game with many scenes, so if I can't use SpriteKit editor to interact with the scenes in an easy way, I want to know how can I interact with my scenes by coding their .swift files.
So the question is, for example, when I create a file "EndScene.sks", how can I create a "EndScene.swift", which links with my .sks file?
Thank you very much!
Create a new swift file and name it the same as your .sks file. Let's say you have a .sks file called MainMenu.sks. You'd create a swift file called MainMenu.swift. Inside that file, you'll want to create a Main Menu class that inherits from SKScene.
import SpriteKit
class MainMenu: SKScene {
}
Inside there is where you'll put all your code. The key is, as you said, linking this to the .sks file.
When you go to present your scene, you'll instantiate the class and associate it with the .sks file.
import UIKit
import SpriteKit
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let scene = MainMenu(fileNamed:"MainMenu") {
let skView = self.view as! SKView
// Some settings applied to the scene and view
// ... code ...
skView.presentScene(scene)
}
}
//... other code
}
Note the line let scene = MainMenu(fileNamed:"MainMenu"). That's where you are instantiating the class you created in MainMenu.swift. So, technically, MainMenu() is the .swift file and fileNamed: "MainMenu" is the .sks file. You can technically put any .sks file into the init call and it'll render that scene.
Imagine having a game with all the logic for a maze runner. You could build all the game logic in a class called MazeScene and just make a bunch of .sks files, each with a different maze. You could then so something like, MazeScene(fileNamed: "MazeOne") or MazeScene(fileNamed: "MazeThree").
For the documentation on this, SKScene inherits from SKNode so you'll find the documentation for init(fileNamed:) here
That should get you going.
So instead of commenting I have to wander because StackOverflow won't let me comment, how much of this is the same in swift 3? And when you were doing this in the UIViewController you said
if let scene = MainMenu(fileNamed:"MainMenu") {
let skView = self.view as! SKView
// Some settings applied to the scene and view
// ... code ...
skView.presentScene(scene)
}
What do you mean by
// ... code ...
Do we put all of the code we would put in the SKScene class in there instead? I thought the whole point was to have a separate SKScene class. And when I try it like that the SKScene class I have doesn't load. Thanks for any clarification and sorry I have to answer instead of comment. It's pretty stupid that you need 50 reputation to leave a comment I don't see any point in that. Anyway, Thanks!
I have a few problems in my game and I think I found the source. First of all, I use a CMMotionManager in my game to capture gyro motion, and if the user plays the game twice without closing the app completely first, the gyro can glitch and stop listening to the user, probably because there are two gameScene's, so there are two CMMotionManagers. I also have music playing in the menu that I have to explicitly call stop on when I transition to the gameScene, and it should just be deallocating that scene.
I don't want to deal with multiple view controllers, so my idea is:
Pass the old scene as an argument into the new scene
Once the new scene has loaded, set the old scene to nil
Should this solve my problems?
Calling SKView presentScene: or presentScene:transition: will properly get rid of old SKScene.
However, if there is a strong reference to the old scene, that scene will not be deallocated. My experience of this problem was from Trial and Error.
Please check this code post. It demos when the scene has a weak reference or a strong reference.
https://gist.github.com/naterhat/5399eec40eaa23edbfbc
Without seeing your code, my suggestion to solve the problem is by removing any connection to the scene other than SKView. Log when scene suppose to be deallocated by switching to a new scene. If works, then slowly connect new code. If not deallocated, something is still referencing the old scene.
You can check in Instruments to see if a scene is deallocated. Instead, I find it best to do this by adding logs in the init and dealloc methods of the scene.
- (void)dealloc
{
NSLog(#"%# Scene DEALLOCATED", self.name);
}
- (instancetype)initWithSize:(CGSize)size
{
if(self = [super initWithSize:size]){
self.name = #"TEST";
NSLog(#"%# Scene CREATED", self.name);
}
}
Hope this helps.
I have a bunch of classes that retain an instance of the scene and I'm starting to think this is going to cause memory management issues for me.
So I have Scene1, Scene2 and a HelperClass that has a property that retains the scene.
When I switch from Scene1 to Scene2 I want everything to be disposed.
I started thinking though am I going to have to manually release the helper class before I call the director to change the scene?
The helper class is retained by a layer that is a child of the scene.
The layer also has a property reference to the scene as a ccnode.
Am I doing this wrong? What is the best way to arrange these things so the memory gets free correctly.
The helper classes do things like creating bullets and adding them to the scene.
I have a similar setup with CCSprites that only exist at the scene level. I keep references to them in the helper classes.
once you add something to a container, a scene or spritesheet or whatever, release it, the container keeps a ref to it, then when you dispose of the container, it will be released.
Scene will not be released if you call push scene, only if you run a new one or call the replaceScene method of CCDirector.