Adding Game Center Leaderboard - swift

I am new to Swift and am having trouble adding a Game Center leaderboard into my Sprite Kit game. I created a button for the leaderboard but it is in an SKScene and it seems that the code for Game Center needs to be inside a View Controller? I have added the code into my single View Controller, but now I'm not sure where to go. Again, I am very new to this and completely lost -- any help would be greatly appreciated.

you could create a cocoa touch class with the UIViewController Subclass. In the viewdidLoad you could use something like this:
if let view = self.view as! SKView? {
// Load the SKScene from 'GameScene.sks'
if let scene = SKScene(fileNamed: "Level1") {
// Configure the scene
[...]
// Present the scene
view.presentScene(scene)
}
Don't forget to import GameKit.

Related

SpriteKit screen size problem with swift5

I try to learn SpriteKit with Xcode 12.4 and swift5.
When I made the first project and tried to run the simulator, it showed me a screen like in the below image.
You can see that screen is not full-size. So, I've changed the size of the attribute inspector in GameScene.sks but nothing changed.
GameViewController code is like below, I didn't change anything... How can I make my GameScene fullscreen on a simulator of iPhone 11?
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
if let scene = SKScene(fileNamed: "GameScene") {
scene.scaleMode = .aspectFill
view.presentScene(scene)
}
}
}
}

Going to the previous scene SpriteKit

I'm making a game. On the main menu I have a button for "Start Game" "High Scores" "Options" "Store".
I have various scenes for the different levels. Each of those scenes has a "pause button" which takes you to the "Options" scene. The Options scene has a "BACK" button.
How do I get that "BACK" button to return to what have scene called it?
Right now the "BACK" button only has the ability to return to the menu ie SceneSkip in my code, regardless of what scene called it:
func back(){
let sceneSkip = SceneSkip(fileNamed: "SceneSkip")
sceneSkip?.scaleMode = .aspectFill
self.view?.presentScene(sceneSkip!, transition: SKTransition.fade(withDuration: 0.5))
}
So I've been working on a game in my spare time and I have solved pretty much exactly what you're doing. Here are a couple things I learned in the process.
1) When you present a scene, your scene's didMove(to: view) method is going to be called. Usually you do your initialization in that method so if you are just pausing, and you present the scene again, you're likely going to mess up the game state.
2) Because of number 1, you can't really present a new scene each time the user wants to pause.
So... the solution I've been using, which is the result of a number of google searches, experimenting and etc, is to actually use a separate view for all my popup/pause type scenes. I set it up like this in my GameViewController viewWillLayoutSubviews where skView is the main view:
let psize = view.bounds
popup = SKView.init(frame: psize)
skView.addSubview(popup!)
popup?.allowsTransparency=true
popup?.isHidden=true
Then, later when any scene anywhere in the game wants to add a popup, I added the following functions:
func showPopupScene(_ scene : SKScene) {
(self.view as? SKView)?.scene?.isPaused = true
popup?.isHidden=false
popup?.presentScene(scene)
}
func closePopup() {
popup?.isHidden=true
if let v=view as? SKView {
(v.scene as? PopupNotify)?.onPopupClosed()
v.scene?.isPaused=false
}
}
Now, any scene can create a scene, and show it as a popup with showPopupScene. The popup scene then needs to call closePopup and the game returns to the scene where it left off.
Other items:
I wanted my game scenes to behave correctly with pausing popups as well as when they are paused from coming out of background etc... so I override isPaused:
override var isPaused : Bool {
get {
guard let v = self.view?.window?.rootViewController as? GameViewController else {
return super.isPaused
}
guard let p = v.popup else { return super.isPaused }
return super.isPaused || !p.isHidden
}
set(newPaused) {
super.isPaused = newPaused
physicsWorld.speed = newPaused ? 0 : 1.0
}
}
Also, the PopupNotify protocol for my scenes helped where I wanted scenes to be aware that the popup was closed in case they needed to make any changes according to whatever the popup was showing.
Hope I'm not forgetting anything, but the combination of these additions has provided pretty easy popup/pause management.
I think that your are going about "levels" in a difficult way.
What I suggest you do is...
Have a scene for your menu which transitions to your GameScene.
In your GameScene you load all generic objects that are common to the game regardless of the level (such as gameHud, score labels, pause buttons, options buttons etc.)
you then load your level scene into GameScene based on a parameter (levelID).
this way none of your generic objects have to be created in multiple scenes greatly reducing the chance of redundancy errors.
Completely optional portion
I don't like to switch scenes in the middle of a game for pausing or options. it creates to many opportunities for something to go wrong (failing to load data, failing to save locations, score, animations etc.).
I create my pause and option dialogs as SKSpriteNodes and pause the game and display the dialog over top of the GameScene. That way when I am done with the dialog I can just remove it, and unpause the game and everything goes back to how it was without having to load the scene all over.

Adding SKScene to UIViewController gives me errors

What I want
An infinite background that randomly adds objects (in my cases little stars) to the middle of the screen. Then those objects needs to move out of the screen with a certain speed. This way I want to give the user the feeling they are moving "into space", seeing stars they are passing.
What I have
A single view application with a UIViewController class.
Problem
I think the right way is to add a SKScene which will do the task I want. However, adding the SKScene class next to UIViewController gives me an error: "multiple inheritance from classes 'UIViewController' and 'SKScene'". I need to add the SKScene to an existence UIViewController, in order to call some important functions. When searching for this on Stackoverflow I came across this: SKScene in UIViewController But I can not call certain functions like "override func update(current time: CFTimeInterval){}", because the answer doesn't show how to implement the SKScene class. This answer deletes the whole UIViewController class: Adding an SKScene to a UIViewController?. The other answers are not helping me.
Question
What is the correct way of adding a SKScene class to an existence UIViewController class, so I can accomplish what I want?
Edit: Came across this on 9gag and its exactly what I want: http://9gag.com/gag/aWmZAPZ Those stars are coming from left top, the respawning should be in the middle of the screen at my app.
Certainly, it's possible. Instead of directly add SKScene to UIViewController, you need to warp SKScene by SKView first, and then add SKView to UIViewController
import UIKit
import SpriteKit
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Configure the view.
// Core:- convert the base view/UIView of UIViewController to SKView
let rootView = self.view as! SKView
// skView.showsFPS = true
// skView.showsNodeCount = true
/* Sprite Kit applies additional optimizations to improve rendering performance */
rootView.ignoresSiblingOrder = true
rootView.frameInterval = 1
//init your SKScene
let rootMenuScene = YourScene(size: rootView.bounds.size)
rootMenuScene.scaleMode = .resizeFill
//warp in SKView
rootView.presentScene(rootMenuScene)
}
...
}

Segues between gameScene.sks

I'm beginner in Swift and SpriteKit.
I have gameViewController with code:
if let scene = IntroScene(fileNamed:"IntroScene") {
// Configure the view.
let skView = self.view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
skView.ignoresSiblingOrder = true
scene.scaleMode = .AspectFill
skView.presentScene(scene)
}
}
My app starting with this scene. I have some code with simple animation for this scene in other viewController:
let introAnimationFadeIn = SKAction.fadeInWithDuration(0.05)
let introAnimation = SKAction.animateWithTextures([introAnimation1, introAnimation2, introAnimation3, introAnimation4, introAnimation5], timePerFrame: 1.5)
let sequenceIntroAnimation = SKAction.sequence([
introAnimationFadeIn,
introAnimation])
intro.runAction(sequenceIntroAnimation)
How I can create segue to other GameScene after animation complete?
I want to create main menu in my project. Can I create segue to other GameScene, when SKSpriteNode touched? Or create button would be better?
I know that segue can be only with viewControllers, but I don't know how names segues between game scenes correct. And sorry for my English.
If you want to go from one scene to another, you don't use segues. Segues are transitions between view controllers, not SKScenes.
To present another scene, just get an SKView and call presentScene!
let scene = YourSceneClass(fileNamed:"Name of your scene that is going to present")!
view.presentScene(scene) // view is an SKView
And it seems like you want to do an animation before presenting the scene. You don't need SKActions for this. Why not use SKTransition instead?
You can use this code to transition to another scene with fade:
// again, view is an SKView
view.presentScene(scene, transition: SKTransition.fadeWithDuration(1))
Easy as that!

Swift: Deallocate GameScene after transition to new scene?

So I've read several questions on this but most are in Objective-C and I haven't found any that address/answer this directly.
I am new to programming here so please explain any suggestions really thoroughly.
I need to understand how to deallocate my GameScene after game over is reached. The reason I need to do this is because after game over, I transition to a new SKScene (the game over scene) and when I transition back to my GameScene, either GameScene is not reset entirely or I'm generating a new GameScene each time.
I say this because My frames per second count goes down every time I get game over and transition back to the GameScene, so that if I do it enough times I can't even play the game anymore. This is a problem.
I don't understand why this happens because when I transition to my game over scene, I remove everything from my GameScene:
self.viewController!.performSegueWithIdentifier("goToGameOver", sender: nil)
self.removeAllChildren()
self.removeAllActions()
self.scene?.removeFromParent()
I also read here: After Closing SKScene, Memory Remains High that removing the SKView that contains an SKScene deallocates the SKScene, so when both my GameScene and game over scenes are left, I remove BOTH views from their superviews like this:
override func viewDidDisappear(animated: Bool) {
print("its gone")
self.view.removeFromSuperview()
}
But nothing has any affect on the decreasing frames per second count and the game keeps lagging. I don't understand what I'm not doing and can't tell whether the game over scene is the problem or I'm just not deallocating GameScene.
How do I deallocate the GameScene? What else should I be doing?
EDIT:
My node count and draws count remain about the same every time I transition back to GameScene, so that doesn't seem to be a problem. I tried testing whether deinit was called when transitioning between scenes (I didn't have a deinit before) using this within the GameScene class:
} //<**End of GAMESCENE**
deinit {
print("Deinit was called")
}
And nothing is printed so deinit is not being called.
EDIT 2:
#alex_p Here is what I have in my GameViewController-
class GameViewController: UIViewController, SKProductsRequestDelegate, SKPaymentTransactionObserver {
var scene1: GameScene?
var scene2: GameScene?
var skView: SKView?
// Action to present next Scene
#IBAction func nextSceneAction(sender: AnyObject) {
print("Next scene")
// Create new GameScene object
scene2 = GameScene(fileNamed:"GameScene")
// Present scene2 object that replace the scene1 object
skView!.presentScene(scene2) //ERROR ON THIS LINE
scene1 = nil
}
And I call this method from my GameScene like this:
self.viewController.nextSceneAction(self)
I copied yours exactly but I am receiving the error unexpectedly found nil while unwrapping an Optional value on the line commented on above. Deinit in not being called.
In my project, I used the following mechanism and it worked well. All my scenes SKScene objects were optional variables. When i need the new scene to show, i create it and present at SKView. When i need to display the new scene, I set the previous object scene object to nil, this immediately reducing the reference count by 1, and becouse of at this moment no one object is not use my scene, the reference count becomes zero and scene was deleted.
The SKScene object is a ordinary class object and ARC works with them like with all reference type objects. You only need to monitor the number of references to the scene. All are finished with the various resources I was start in deinit of SKScene object
The simple example:
At UIViewController we have optional objects of GameScene:
class GameViewController: UIViewController {
var scene1: GameScene?
var scene2: GameScene?
var skView: SKView?
// Action to present next Scene
#IBAction func nextSceneAction(sender: AnyObject) {
print("Next scene")
// Create new GameScene object
scene2 = GameScene(fileNamed:"GameScene")
// Present scene2 object that replace the scene1 object
skView!.presentScene(scene2)
scene = nil
}
override func viewDidLoad() {
super.viewDidLoad()
// Create GameScene object
scene = GameScene(fileNamed:"GameScene")
skView = (self.view as! SKView)
skView!.showsFPS = true
skView!.showsNodeCount = true
skView!.ignoresSiblingOrder = true
scene!.scaleMode = .AspectFill
// Present current scene
skView!.presentScene(scene)
}
}
At GameScene in deinit print some text to show that it object will be deleted:
class GameScene: SKScene {
...
deinit {
print("Deinit scene")
}
}
Debug output after push the nextSceneAction button:
Next scene
Deinit scene