SpriteKit pause Scene (Swift4.2) - ios

I built a Game with a pause button which shows a new modal ViewController with a Pause menu over the current and pauses the scene with the following function (which is located in the scene and called via protocol and delegate):
func pause(isPaused: Bool) {
let pauseAction = SKAction.run {
self.view?.isPaused = isPaused
}
self.run(pauseAction)
}
Pausing the scene and showing the modal view controller works, but when I return to the scene by a unwind segue the scene doesn't unpause.
The pause button is in the same view as the scene.
For communication with the scene from the Pause menu, I use an unwind segue where I call the pause function via Protocol and delegate.
I present the pause menu with that:
#IBAction func PauseMenuButton(_ sender: Any) {
let vc = storyboard!.instantiateViewController(withIdentifier: "PauseMenuVC")
vc.providesPresentationContextTransitionStyle = true
vc.definesPresentationContext = true
vc.modalPresentationStyle = .overCurrentContext
present(vc, animated: true, completion: nil)
SceneIsPausedDelegate.pause(isPaused: true)
}
and go back to the scene with that :
#IBAction func unwindToGameVC(segue: UIStoryboardSegue) {
SceneIsPausedDelegate.pause(isPaused: false)
}
I think that the function is never called because it is in the scene since it is paused the is no execution.

SKView pause and SKScene pause behave differently
When an SKView is paused, it will not call the update functions on the scene
When an SKScene is paused, SKView will still call the update functions on the scene, and the scene will not call the update functions on the children nodes.
In your scenario, you are calling pause on the SKView. This means any action you run on your scene will not fire because update never happens.
I recommend either switching to SKScene pause, or not using an action to pause and unpause.

I changed:
func pause(isPaused: Bool) {
let pauseAction = SKAction.run {
self.view?.isPaused = isPaused
}
self.run(pauseAction)
}
to:
func pause(isPaused: Bool) {
self.view?.isPaused = isPaused
}
it wokes without an action.

Related

How to close a View Controller after Performing segue in Xcode?

My storyboard looks something like this:-
Main View Controller -> Game View Controller -> Game Result View Controller
I have performed a modal segue from Main V.C to the Game V.C. Now when I perform a modal segue from Game V.C. to Game Result V.C., the Game V.C. is not closing and since the Game V.C. detects if touches began, it crashes when it detects touch outside the Game V.C. after performing a modal segue to the Game Result V.C. I am not using a navigation View Controller. Could anyone please help me on how shall I close the Game V.C after performing a modal segue to the Game Result V.C.? Would appreciate your help! Thanks:)
**Game V.C.**
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
// First touch to start the game
if gameState == .ready {
startGame()
}
if let touchLocation = event?.allTouches?.first?.location(in: self.view) {
// Move the player to the new position
movePlayer(to: touchLocation)
// Move all enemies to the new position to trace the player
moveEnemies(to: touchLocation)
}
}
func gameOver() {
stopGame()
performSegue(withIdentifier: "2to3segue", sender: self)
}
Some tips:
Method 1
You can use Tap controller, let storyboard works as following
Main View Controller -> (tab-1) Game View Controller
\
-> (tab-2) Game Result View Controller
Hide tab bar, and change tab by program.
I suggest to use method 1.
Method 2
Dismiss Game View Controller, WITHOUT animation. And then present Game Result View Controller WITH animation.
But, I remember method 2 has some problems. I used it before, and finally I use method 1 now.
// Example:
// Two or more animations will produce problem.
v2.dismiss(animated: false, completion: nil);
v.present(v3, animated: true, completion: nil);
First dismiss the "v2" view controller and in completion block present "v3" view controller
v2.dismiss(animated: false, completion: {
v.present(v3, animated: true, completion: nil)
})
or
if v2 is your presented view controller then :
self.presentedViewController.dismiss(animated: false, completion: {
v.present(v3, animated: true, completion: nil)
})

Swift perform segue without clicking button

I'm making a small game in SpriteKit where I have a bool value stored that checks if it's the first time the user plays my game. If it is, I want the app to redirect him to another viewcontroller where he enters his character-name.
However, I'm having trouble getting my app to do so.
In my "GameViewController.swift" I have this code:
override func viewDidLoad() {
super.viewDidLoad()
checkGame()
if let view = self.view as! SKView? {
// Load the SKScene from 'GameScene.sks'
if let scene = SKScene(fileNamed: "GameScene") {
// 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
}
}
func checkGame() {
//Check game
let firstPlay:Bool = appData.bool(forKey: "\(GameData.firstPlay)")
print(firstPlay)
if firstPlay == false {
print("Heading for setup")
self.performSegue(withIdentifier: "GoToSetup", sender: nil)
}
}
However, the "setup viewcontroller" isn't loaded. I can manage to tricker the segue with a button-click, but that's not what I want. I want it to happen as soon as the view is loaded.
Any ideas?
I don't know what you meant by "setup viewcontroller" isn't loaded", but usually we don't perform segues in the viewDidLoad. At this time, the view has just been loaded into memory and has not appeared yet. Performing a segue at this time is kind of a weird thing to do.
I recommend you to perform the segue in viewDidAppear():
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated: animated)
performSegue(withIdentifier: "GoToSetup", sender: nil)
}
You can programmatically call a segue with the following:
self.performSegueWithIdentifier("segueId", sender: self);
As it was said, in viewDidLoad stage UIViewController can't be done any UI-interaction, and performSegue to, because view is not at the view hierarchy yet. You can do this at viewDidAppear(animated:Bool).

How can I dismiss a ViewController from my GameScene.swift?

QUESTION: How can I dismiss a ViewController from my GameScene.swift ?
SITUTATION: I have 2 VCs in my SpriteKit Game, like so:
ViewController.swift ----Press Play-----> GameViewController
When the player loses, I want to dismiss the GameViewController so the player can press play again. I check for the player's loss in my GameScene.swift and would like to dismiss the GameVC from there.
N.B.: Googled this without success.
WHAT I TRIED:
1) Creating a gameVC instance in my GameScene.swift and dismissing it like so:
let gameVC = GameViewController()
gameVC.dismissViewController(false,completion: nil)
2) Doing:
self.view.window!.rootViewController?.dismissViewControllerAnimated(false, completion: nil)
Those don't work for obvious reasons ^^
You don't want to "grab" the existing instance: https://pragprog.com/articles/tell-dont-ask
You need to either hand GameScene a reference to the view controller so it can dismiss it, or use the delegate pattern to communicate backwards to a controlling object that the VC should be dismissed/dismiss itself.
A simple example… you can add a GameViewController property to GameScene, then dismiss the VC at the appropriate time:
class GameScene: SKScene {
var gameVC: GameViewController?
func gameDidEnd() {
gameVC?.dismissViewControllerAnimated(true) {
// if desired, do any cleanup after the VC is dismissed
}
}
}
Then, just set this property when creating the GameScene object in the first place:
if let gameScene = GameScene(fileNamed: "MyScene") {
gameScene.gameVC = someGameVC
}
This simple approach will tightly couple GameScene and GameViewController, making it a bit more difficult if you ever want to use one of these objects without the other. But for this simple use case, it may be fine.
I've follow some of your discussion. I want to add some code, because usually I prefeer to work with one ViewController or two and many SKScene and SKNode, but in this case could be useful to have a currentViewController reference:
class MyModelScene: SKScene {
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
var currentViewController : MyModelViewController! = MyModelViewController()
// MyModelViewController is a customized UIViewController
override func didMoveToView(view: SKView) {
super.didMoveToView(view)
print("---")
print("∙ \(NSStringFromClass(self.dynamicType))")
print("---")
}
}
class Level1Scene: MyModelScene {
...
}
In the UIViewController:
class PreloadViewController: MyModelViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let scene = Level1Scene(fileNamed:"Level1Scene") {
// Configure the view.
let skView = self.view as! SKView
skView.showsFPS = true
skView.showsPhysics = true
skView.showsNodeCount = true
skView.ignoresSiblingOrder = true
/* Set the scale mode to scale to fit the window */
scene.scaleMode = .ResizeFill
scene.currentViewController = self
skView.presentScene(scene)
}
}
}
With this code , you've always a currentViewController reference in your SKScene and you can check if it's the correct viewController you want to dismiss or not.

After programmatically segueing, The ViewController has not been dismissed

When you press a button a custom segue is performed to start a game:
self.performSegueWithIdentifier("segueToGame", sender: nil)
I'm using sprite kit. I have settings in the GameViewController and GameScene such that you play until you lose and when you do the gameScene sends a message to the view controller telling it to segue back to the initial view controller. I do this as follows:
let notificationCenter = NSNotificationCenter.defaultCenter()
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
/*
All code to set the scene is there
*/
skView.presentScene(scene)
notificationCenter.addObserver(self, selector: "segueToHomeScreen", name: "segueToHomeScreen", object: nil)}
override func viewDidDisappear(animated: Bool) {
if self.isBeingDismissed() {
println("Dismissed Vc")
}else {
println("still here")
}
}
func segueToHomeScreen(){
self.performSegueWithIdentifier("segueToHomeScreen", sender: nil)
}
in my GameScene the code I used when you lose the game is:
func segueToHomeScreen() {
notificationCenter.postNotificationName("segueToHomeScreen", object: nil)
}
The problem is the GameScene has not completely stopped when I segue back to the home screen. I found this out by two things. One, the viewDidDisapper function you see above, I get a log message of "still here". Two, in gameScene I used this
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
println("Scene On")
}
When you lose the game and you are back at the home screen this function is still running. How can I stop the ViewController and GameScene from running after performing the segue to the home screen?
Mot sure if this is related but I also get an error message:
Warning: Attempt to present * on * whose view is not in the window hierarchy!

Switch between SKScene and UIViewControllers without memory rising

I have 3 UIViewControllers. The first controller - Menu Game. The second - Game. The third - Game Over controller.
In the second controller I have SKView with one scene of my game.
I created segue in storyboard between the button (Start Game) in 1-st controller and 2-nd controller. And segue between 3-rd controller (tap gesture) and 1-st controller. All segues are "Show".
In 2-nd controller I create follow:
override func viewDidLoad() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: "showGameOverViewController", name: "showGameOverViewController", object: nil)
}
func showGameOverViewController() -> Void {
performSegueWithIdentifier("gameOverSegue", sender: nil)
}
Then when I need to switch from scene (in GameScene.swift) to 3-rd controller I call follow:
self.view?.presentScene(nil)
NSNotificationCenter.defaultCenter().postNotificationName("showGameOverViewController", object: nil)
This way works, but works bad.. When I tap the button in 1-st controller the 2-nd controller then follow method is calling and the scene is initialising:
override func viewDidLoad() {
let skView = self.view as SKView
if skView.scene == nil {
let gameScene = GameScene(size: skView.bounds.size)
gameScene.scaleMode = .AspectFill
skView.presentScene(gameScene)
}
}
So I have a problem. When I finish my game, go to 1-st controller again and tap Start Game button then memory of my application is rising!
And when I go from Scene to 3-rd controller then the memory of my app is rising. The rise of memory happens again and again.
I chose this way because my SKS editor doesn't works and I decided not to create Menu Scene and Game Over Scene programmatically. I thought to create Menu in Interface Builder would be better and faster and I had decided to try go this way. And I'm interested in the answer to solve my problem.
Please help me.. How to switch between SKScene and UIViewControllers without memory rising?

Resources