I am designing a SpriteKit game in swift, and inside my gameplay SKScene I have a method called when I want to pause the game. It looks like this:
func pause() {
view?.paused = true
}
The game pauses perfectly, but after a seemingly arbitrary amount of time (1 second to 120 seconds), the game just unpauses/resumes gameplay, without ever calling my resume() method. I am aware that sprite kit resumes gameplay automatically upon the app becoming active, but I have set a breakpoint in applicationDidBecomeActive, and it is not called. Does anyone know why this is happening?
I know I could set my own paused property and check it every update loop, but I much prefer this elegant solution if I could get it to work!
The problem was my implementation of ADBannerViewDelegate. Here is the culprit:
func bannerView(banner: ADBannerView!, didFailToReceiveAdWithError error: NSError!) {
skView.paused = false
println("bannerView:didFailToReceiveAdWithError called inside GameViewController class")
}
I solved the problem by putting those println calls inside every method that had a .paused = false statement. Most of the time the banners load fine, but every once in a blue moon they fail and call that method.
Related
I can pause the game I have created no problem while I'm in the app. However, when I try and call the pause function I have created in the applicationWillResignActive function in App Delegate, it doesn't work, at all. So this is my code that I am trying to use in my App Delegate File.
func applicationWillResignActive(_ application: UIApplication) {
GamePlayScene.instance.isPaused = true
print("Game is Paused")
}
The print Statement gets called so I know the function is getting called. However, the game still stays active. I'm trying to call the same isPaused function that I am using in my GamePlayScene.
One thing that works is if I just use the normal pause feature in SpriteKit but, I can't find a way to resume the game after I open the app. For example, I can use this.
func applicationWillResignActive(_ application: UIApplication) {
pause()
print("Game is Paused")
}
This seems to work, but the whole app just opens up frozen and there is no way to keep going. What do y'all think?
Figured it out. Since I don't see anyone else that has the answer when involving a SpriteKit Game, I will post the answer. This is my code.
let notificationCenter = NotificationCenter.default
notificationCenter.addObserver(self, selector: #selector(appMovedCameBack), name: Notification.Name.UIApplicationDidBecomeActive, object: nil)
#objc func appMovedCameBack() {
print("App Came Back!")
self.scene?.isPaused = true
GamePlayController.instance.pauseBtn?.alpha = 0
createPausePanel()
}
I am calling the function where I created the PausePanel and adding it to the scene. That is the createPausePanel() function. I am also setting the scene.isPaused = true. This is how I solved my issue.
First time using SpriteKit - tapping a start button calls startLevel method in GameScene that has a player sprite fire two SKAction.run statements in a SKAction.sequence. The startLevel method has a completion block that triggers a message to the console and updates game state.
Tapping start displays the message, but the SKAction.run statements don't work unless I first pause program execution, and then click resume before tapping the start button. If I stop the level and tap a retry button, the same startLevel method is called, and it works fine without pausing execution.
Here's the method (player, marker, and door, are all optional objects that store their sprites; when tapping start, I verified that none of the sprites are nil, none of them are paused, and GameScene and SKView are not paused):
func startLevel(finished: () -> ()) {
guard let player = player,
let marker = marker,
let door = door else {
fatalError()
}
player.sprite.run(SKAction.sequence([
SKAction.run({ marker.pop(appear: true) }),
SKAction.run({ door.slide(open: true) }),
]))
finished()
}
Here's the GameScene code that calls startLevel:
startLevel(finished: { () -> () in
print("ALERT: startLevel finished")
levelStarted = true
})
I tried adding a run method completion block with a console message, and the message never prints. It seems that the player.sprite.run method is simply ignored (unless I pause execution first), and I don't understand why. Thanks for your help.
BTW - all the code above is outside GameScene’s update method, and the marker and door methods above are both used in didBegin() and didEnd(), and they work fine there.
In iOS 11 Apple made Spritekit Scenes and objects paused by default.
What I do to combat this is...
in my SKScene
override func didMove(to view: SKView) {
//this ensures that the scene is not paused
self.isPaused = false
}
and in each of my custom objects setup or init
self.isPaused = false
I have a watchOS 4 app which displays SpriteKit animations (SKActions) on top of the UI. Everything works fine in simulator and also on device first couple of times, then after some time when app is in background, and it is started, animations just freeze and completion block for the most long-lasting animation is not called. Any idea what might be the issue?
This is how I run my actions, caller is waiting for completion closure in order to hide the spritekit scene:
private func runActions(with icon: SKShapeNode?, completion: #escaping () -> Void) {
if let icon = icon, let scaleAction = scaleAction, let bg = background {
self.label?.run(fadeInOutAction)
icon.run(scaleAction)
icon.run(fadeInOutAction)
bg.run(backgroundAction, completion: completion)
} else {
completion()
}
}
And yes, I am aware that SKScene is paused when app moves to background. I am doing this in willActivate of my InterfaceController:
if scene.scene?.isPaused == true {
scene.scene?.isPaused = false
}
I want to emphasize that this works first always. It begins to fail after the app has been backgrounded for some time. Especially if I start the app from complication and try to immediately fire these animations, then this freezing happens.
Can I answer my own question? I guess I can? Here goes:
I finally solved this. It turns out that the WKInterfaceScene in WatchKit has ALSO an isPaused property that you need to turn false sometimes. So now in willActivate of my InterfaceController I will also check that and turn it false if it is true. Since I made this change, I haven't seen a single hiccup, freeze or anything weird anymore.
Case closed, I guess. I leave this here for future generations who might face this issue.
So I have a an app that uses interstitial ads. Specifically it is a a SpriteKit game written with Swift.
I have code setup that when the user presses the replay button from the game over scene an ad appears and then it changes back to the game scene to replay the game. Now where I am running into problems the scene changes while the interstitial ad is being displayed, sometimes this doesn't happen fast enough and user can tap the restart button again, causing the game to crash.
Is there a way to freeze the screen and ignore any taps while the ad is being called? And also to only have the scene change after the ad is dismissed?
The code when the restart button is pressed;
if restartButton.contains(pointOfTouch) {
score = 0
ballMovementSpeed = 2
displayAd()
delay(2.0) {
self.restartScene()
}
I am a bit confused as to where the interstitialDelegate is placed. I am trying to implement func interstitialDidDismissScreen(ad: GADInterstitial!) {} to trigger a change back to my game scene and nothing happens when I dismiss the ad.
I have tried placing it in override func didMove(to view: SKScene){} as well as when the restart button is pressed and still won't work. This is how i have the ad being called
fun loadAndShow() {
myAd = GADInterstitial()
let requestI = GADRequest()
myAd.setAdUnitID("adID")
requestI.testDevices = [kGADSimulatorID, "test device"]
myAd.delegate = self
myAd.load(requestI)
}
func interstitialDidReceiveAd(_ ad: GADInterstitial) {
if (self.myAd.isReady) {
myAd.present(fromRootViewController: self)
}
}
you can use delegate methods of admob so when interstitial is going to be shown you can remove the restart button or put a condition so that it would not work when ad is shown. Also to pause the game is also important if it is running using isPaused bool.
https://developers.google.com/admob/ios/ad-events
SpriteKit is automatically pausing my app when it goes into the background. This is fine, except it stays paused when the app becomes active again. I'm trying to un-pause the game in the appropriate application events, to no avail. I'm manually un-pausing the two SKViews and the game Scene that constitute the app. From inside the AppDelegate file:
func applicationWillEnterForeground(application: UIApplication) {
if let vw = self.window?.rootViewController {
let gc = vw as! GameViewController
let parView = gc.view as! SKView
parView.paused = false
gc.gameView.paused=false
gc.gameScene.paused=false
println("paused = \(gc.gameScene.paused)")
}
}
func applicationDidBecomeActive(application: UIApplication) {
if let vw = self.window?.rootViewController {
let gc = vw as! GameViewController
let parView = gc.view as! SKView
parView.paused = false
gc.gameView.paused=false
gc.gameScene.paused=false
println("paused = \(gc.gameScene.paused)")
}
}
At the end I print the pause state. If I hit the device's Home key and then return to the app, it prints false as desired. However, somewhere (not by my code) this is being immediately set back to true, and the game remains paused.
Update
I overrode the scene's paused property, and it is definitely being set back to true after I un-pause in the given events. Confusing, because from reading other questions, the typical behavior of sprite kit is to automatically un-pause the game when the app is reactivated. Yet it's doing the exact opposite for me.
Well, I basically gave up on trying to control whether the game pauses/resumes during the app's state changes. Instead I overrode the scene's paused property, so now I can just detect whenever sprite kit decides to pause or resume, and handle that accordingly. My suspicion is that my hierarchy of SKViews in my app might not be conventional, and maybe caused sprite kit difficulty in controlling the scene state, but that's only a guess.