SKAction resumes when appDidBecomeActive() even when game is paused before appDidResignActive() - ios

I have an iOS SpriteKit game, which has a moving node. When you press the pause button, the node.isPaused = true line gets called. But if you press pause, then minimize the app, and relaunch it, the node gets back to moving, even under the transparent "Pause" menu.
I have tried the proposition from the following answer: Answer1. But, even if I call my scene's pauseGame method in any state, the function gets called, but the node restarts moving even under the pause menu.
I have also tried the answer from: Answer2. But the CBApplicationDidBecomeActive() isn't even getting called.
And I have even tried using scene?.view?.isPaused = true in the methods of link 1, but it doesn't work.
Please help.
I know there are lots of similar questions out there, but none of them seem to help solve what I have. (and even, the question from the 2nd link didn't even get an accepted answer)

Change pauseGame() and resumeGame() methods to look like this:
private func pauseGame() {
node.speed = 0.0
gameState = .paused
pauseLayer.run(SKAction.fadeAlpha(to: 0.5, duration: 1))
}
private func resumeGame() {
pauseLayer.run(.fadeOut(withDuration: 1))
node.speed = 1.0
gameState = .inProgress
}
Note that I am using speed property instead of isPaused property... That is all the difference you need to make. Can't really say why this happens because this behaviour is not documented, but I have seen it already, and been writing about it probably more in detail in some of my previous posts.

Related

Pausing one animation while keeping another — BAFluidView Swift

Okay...this is a long shot. Here's goes...
Background:
For a long time, I've been trying to figure out how to animate a wave horizontally across the screen while a long press is active. When the long press is interrupted or ends, the wave pauses in place, while continuing to ripple.
I've struggled to find a way to implement this. And then I recently stumbled across BAFluidView. I was able to get the wave looking as I'd like it to:
The wave animates nicely. I can control when it starts. But, I can't pause it's motion across the screen while allowing the actual wave to continue.
About me:
I'm new to this.
I don't know much (almost none) Objective-C
The library was written in Objective-C
My ask:
Does anyone see a way for me to pause this damn wave?
If not here, what am I missing that could make this so much easier?
Here's a link to the repo containing BAFluidView's code.
And here's a link to the wave shown below. It's not the app I'm working on, but it's an attempt to figure out how to pause this wave.
Thanks!
I think pause animation is not possible but you remove current on going animation with below method.
[myView.layer removeAllAnimations];
Try WaveAnimationView instead. It is written in Swift and you can set the progress to any value between 0.0-1.0.
In case of animating the process of increasing or decreasing the progress of an instance of WaveAnimationView, I use a repeat timer, which looks like
Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true) { Timer in
if wave.progress == *yourvalue*{
Timer.invalidate()
return}
wave.progress += 0.01
}
Or you can try UIView.animate() method, which I didn't experiment though.

GameplayKit Not Pausing When Scene Is Paused

I have a GKAgent2D governing the movement of an SKNode enemy that moves towards a destination.
When I pause the SKScene that the agent is moving in, the enemy node seems to continue to move without showing such movement. All of the visible movements stop (AKA the enemy stops moving). If I wait 5 seconds then unpause the scene, it's as if the enemy never stopped moving and it pops to where it would have been had I not paused the scene, and it continues to move from that point as normal.
I looked into the sample demo game that Apple had created for users to use called DemoBots and they implement a "fix" similar to what I am using by simply leaving the update:(NSTimeInterval *)currentTime method with a return. Here is what my code currently looks like:
- (void)update:(NSTimeInterval)currentTime {
[super update:currentTime];
if (_lastUpdateTime == 0) _lastUpdateTime = currentTime;
float delta = currentTime - _lastUpdateTime;
_lastUpdateTime = currentTime;
// Stop updating if the `worldNode` is paused.
if (worldNode.paused) { return };
for (GKComponentSystem *componentSystem in componentSystems) {
[componentSystem updateWithDeltaTime:deltaTime];
}
}
But I am unable to dive deeper and find out what else they are doing to ensure that the GKGoal objectives that are currently in effect are stopped right when the pause happens. I even logged the agentDidUpdate method and it stops firing when I pause the scene, so I am really not sure how it continues to move.
If someone knows the answer please let me know. Thank you!
UPDATE: I even tried pausing the individual nodes that have the goals set to move:
- (void)didPauseScene {
worldNode.paused = YES;
/*
<< Animations >>
*/
for (OEnemy *enemy /*my subclass*/ in enemyArray) {
enemy.paused = YES;
}
}
But this still did not stop the GKGoals from continuing as they would without pausing the scene..
UPDATE 2: The ONLY solution that stops the agents cold is literally removing the agent systems purpose:
if (worldNode.paused) {
self.agentSystem = nil;
return;
}
This is a very sad solution as I would hope there is a more elegant / appropriate way of stopping the goals without removing everything completely from the scene. The other issue however is even with this, resetting the goals upon unpausing creates the same issue of the jumping to the position they would be in if no pausing had occurred.
Agents will continue along their last known valid velocity if another goal isn't altering it. If you'd like the agents to stop you could try using GKGoal's goalToReachTargetSpeed:.
For those where the GKGoal goalToReachTargetSpeed: did not work.
Removing my MovementComponent from the entity worked for me:
let movementComponent = entity.component(ofType: MovementComponent.self)!
entity.removeComponent(ofType: MovementComponent.self)
componentSystem.removeComponent(moveComponent)
When you resume your game, make sure you create a new instance of your movement component and add it to the entity again:
let movementComponent = MovementComponent()
entity.addComponent(movementComponent)
componentSystem.addComponent(moveComponent)
When using a GKComponentSystem to update your movement component, also make sure that you remove and add the movement component accordingly.

'Play on' feature is an in-app purchase? SpriteKit/Swift

I'm near the end of developing a game, and long story short, the game will be over when an object crashes into one of the obstacles. I've got that part down and the game runs really well by itself, but there is one more step I am looking to add in.
I would like to add in an in-app purchase of a 'play on' feature, that is, starting where the user's game originally ended so they can continue on. I'm okay with in-app purchases on the whole, but I guess what I want to know is how does one make it possible to 'play on' following the in app purchase? I'm just looking for something basic I can build on.
I'm a Stack newbie, as I've only created an account today (but I've been programming for a little while and this site has helped me so many times), so I'm sorry if there has been a duplicate thread made elsewhere. I did look around for an hour or so before deciding to post (and Google was no help).
As a general stack overflow rule you should always post some of your own code or what code you have played around with.
I am actually also looking to integrate a playOn button in my game. Now I haven't actually found the perfect solution for this yet but hopefully this helps you get on the right track.
Step 1:
How have you programmed your game over scenario?
Are you just pausing the scene, are you pausing nodes or are you removing all children from your scene?
The way I pause my scene is by creating a worldNode and than adding all objects that I need paused to the worldNode.
You can read my two answered questions for more detail
Keeping the game paused after app become active?
Sprite moves two places after being paused and then unpaused
This way when I pause my game I dont actually pause the scene which gives me more flexibility adding pauseMenus etc. Furthermore its seems smoother than pausing the skView.
I also call pause when the player died, which means I could resume the enemies/obstacles from where they left of if I call resume.
So my game over method looks like this
func gameOver() {
pause() // call pause method to pause worldNode etc
//show game over screen including playOn button
}
Step 2:
Now in regards to respawning the player, it depends on how he is positioned, how far he can move etc.
If your player is mostly in the same area than you can probably just respawn you player manually once "PlayOn" is pressed and than resume the game as if it was just paused.
So once your playOn button is pressed you can call a method like so
func playOnPressed() {
// Remove current player
// Doesnt have to be called, you could just change the position
player.removeFromParent()
// Add player manually again to scene or just reposition him
...
// Remove obstacle that killed player
// haven't found a great solution for this yet
// You could make the player not receive damage for 5 seconds to make sure you dont die immediately after playOn is pressed
// Call resume method, maybe with delay if needed
resume()
}
If your player position could be all over the screen, as in my game, I have been playing around with a few things so far.
I created a position property to track the player position
playerPosition = CGPoint!
and than in my scene update method I constantly update this method to the
actual position of the player.
override func update(currentTime: CFTimeInterval) {
if gameOver = false {
playerPosition = player.position
}
}
I than have been playing around with the "playOnPressed" method,
func playOnPressed() {
// Remove current player
//Doesnt have to be called, you could just change the positioon
player.removeFromParent()
// Add player manually again to scene or just reposition him
...
player = SKSpriteNode(...
player.position.x = playerPosition.x - 40 // adjust this so it doesnt spawn where he died but maybe a bit further back
player.position.y = playerPosition.y // adjust if needed
// Remove obstacle that killed player
// haven't found a great solution for this yet
// You could make the player not receive damage for 5 seconds to make sure you dont die immediately after playOn is pressed
// Call resume method, with delay if needed
resume()
}
I hope this helps you playing around with your playOn button, if someone has a better way I would also appreciate it immensely.

Running a function when the game becomes active iOS - SpriteKit

In my game in swift / SpriteKit, I have a problem when the user quits the app and opens it up again. If the game is on the pause menu and everything is paused, when the game is reloaded the game is unpaused but the labels on the pause menu do not disappear. How do I keep the game paused when I reopen it? I have tried
func applicationWillEnterForeground(application: UIApplication) {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
let scene = GameScene(size: CGSize(width: 2048, height: 1356))
scene.view?.paused = true
}
But this is not doing anything. I have also tried pause() but that messes things up. Any help is appreciated.
You want to use the applicationWillResignActive function to pause your scene, by doing something like scene.speed = 0 which will properly pause your scene (don't know why, but it doesn't quite work just using pause). Here you can also display your pause menus (which you appear to be doing).
Then you want to use the applicationDidBecomeActive function to continue your game. Here you do your checks to see if (guessing here) your "resume" button is pressed, and in the response method for that, set the scene.speed = 1. Here your response method, do anything that requires starting back up (animations, actions, hiding your pause menu(s), or music).
I will say, these are the two functions that I use, you are free to use whichever two functions you would like that have a on/off kind of thing. I've found these to be very reliable.

How to effectively pause a game in spritekit?

This seems like such a basic concept but there is really no good answer.
I currently have a pretty basic setup, in my game's scene, I have an enum gameState which is currently either inGame or gamePaused.
I have my button set up, and I have it so pressing simply toggles gameState.
Then, to make it work, I have two separate update methods: One for when gamePaused, one for when inGame.
This works for 95% of my game, as the updates in my game can be pretty readily started and stopped without any issues. There are some issues I cannot tackle though.
First and foremost are actions. I use SKActions for a bit of my project (some movement, scaling, fading...) and this method does NOT pause them. This can be catastrophic in certain points. Secondly, this doesn't handle particles, physics, and a few other things that are not directly associated to my update methods.
The only clue I have is self.view.paused = YES, but that isn't going to work either.
First off, this DOES fix my actions, particles, and physics problem... kinda. But This question suggests that SKActions aren't actually PAUSED by this, but actually STOPPED completely. Is this true? If it is, then it won't work smoothly either because my actions are thrown out of whack.
Also, pausing the view seems to do what it says: stop everything. EVERYTHING. Newbie question here, but how am I supposed to get any code to run at that point? It's all cut off. Is this where I need subViews? I have never used a subView yet, but it sounds like its what I want in this case.
Simply put, there are a lot of unanswered questions for me, and I don't know how to proceed. I'm hoping there's some 'standard procedure' for pausing in Sprite Kit, but with pausing the view halting actions I truly have no idea where to proceed.
Should I move away from using actions? Is my idea of a pause subView sound?
I don't want to babble, all I want to know is how you go about pausing in your average Sprite Kit project. Additional info provided upon request.
Pausing a node will pause (not stop) the node's actions, and i suppose pause will also be applied to all children of the paused node in the same way. All other unpaused nodes will continue to run their actions to completion.
You should have a "game layer" node with all game nodes in them that you want to pause, and then just pause that "game layer" node. Then have a "pause game menu" node where you add all the pause menu stuff you need which will continue to function normally even if the game layer is paused.
Be very sure you do not use performSelector or NSTimer or GCD or any other scheduling methods besides actions or the scene's update: method because those will not adhere to a node's paused state.
Pausing the view effectively freezes everything in it, pausing the scene according to some will not pause the update: calls - but I have not verified this myself. If this were the case you can always check inside the update method whether a given node that you send a message to is paused and if so, decide not to send certain messages to that node.
I just called a map function on all children of the scene and set the paused property to true
self.children.map{($0 as SKNode).paused = false}
By setting it to true you can easily undo the freeze.
EDIT:
Better use a for-loop to iterate over the children.
for node in self.children as [SKNode] {
node.paused = false
}
I can tell you what I did in my game:
Pause the SKView on -applicationWillResignActive: (or equivalent, using NSNotifications),
Un-pause the SKView on -applicationDidBecomeActive: (or equivalent, using NSNotifications),
For the actual game scene only, I set a boolean flag _isPaused when resigning active, and if it is true I just skip the -update: method (frame updates). (I also show the "paused" menu when the user resumes the app).
...But my game is a simple puzzle game, and there's no long actions that should continue past the pause/resume. I can not confirm right now that all actions are aborted, but I think it's not the case.
I also used self.children.map{($0 as SKNode).paused = false} and it does work if you create a var layer of type SKSpriteNode and add it on top of the scene. But I also have an NSTimer I'm using to spawn sprites on the scene. Everything currently on the scene is paused, but sprites keep appearing and moving across the screen. It is not pausing the spawning.
I tried pausing the NSTimer when I call the above code by setting repeats to false, then setting it to true when I remove the layer but it doesn't work. I also tried self.scene?.view?.paused = true but that freezes everything and the layer I create does not even appear onscreen.
You need to set the delegate for the View!
func PauseGame(){
self.scene.view.paused = true
}
fun playGame(){
self.scene.pause = false
}
This function is work perfectly if you are not using the NSTimer to call a function. If you use NSTimer your view will pause perfectly but when you play the game again by play game functionality you see that all the functions that use the NSTimer function that runs in the back end and your screen full with your sprite-kits. So when you use NSTimer in the function you have to first pause the NSTimer functions also after that this function work perfectly.
//For Pause
func pauseGame() {
scene?.view?.paused = true
}
//For play.
func playGame() {
scene?.view?.paused = false
}

Resources