Pause an SKAction in Spritekit with Swift
You should run an action with key:
square.runAction(SKAction.repeatActionForever(moveSequence), withKey:"moving")
Then, use action's speed property to pause it:
if let action = square.actionForKey("moving") {
action.speed = 0
}
or to unpause it:
action.speed = 1
Swift 3: Making a Pause Menu in SpriteKit by overlaying a SKView?
I struggled with the problem of pausing the game within the game scene for a while.
As several others have suggested in the comments, building a "pause scene" to transition into when the game is paused and then out of is an effective solution. This approach avoids problems you might run into with timers firing within the game scene while the game is paused or animation skips when waking up.
To implement a pause scene, I use a custom subclass of UIViewController
to handle scene transitions.
Within my CustomViewController
:
var sceneForGame: MyGameScene? //scene to handle gameplay
var paused: PauseScene? //scene to appear when paused
...
// presentPauseScene() and unpauseGame() handle the transition from game to pause and back
func presentPauseScene() {
//transition the outgoing scene
let transitionFadeLength = 0.30
let transitionFadeColor = UIColor.white
let pauseTransition = SKTransition.fade(with: transitionFadeColor, duration: transitionFadeLength)
pauseTransition.pausesOutgoingScene = true
let currentSKView = view as! SKView
currentSKView.presentScene(paused!, transition: pauseTransition)
}
func unpauseGame() {
let transitionFadeLength = 0.30
let transitionFadeColor = UIColor.white
let unpauseTransition = SKTransition.fade(with: transitionFadeColor, duration: transitionFadeLength)
unpauseTransition.pausesIncomingScene = false
let currentSKView = view as! SKView
currentSKView.presentScene(sceneForGame!, transition: unpauseTransition)
}
Within MyGameScene
class (subclass of SKScene
):
var parentViewController: CustomViewController? // ref to the managing view controller
...
// invoke this func when you want to pause
func setScenePause() {
parentViewController?.presentPauseScene()
self.isPaused = true
}
...
// you may need a snippet like this in your game scene's didMove(toView: ) to wake up when you come back to the game
else if self.isPaused {
self.isPaused = false
}
This is my PauseScene
implementation. This version will unpause when the user taps anywhere in the pause scene, except for an endGameButton
, which terminates the current game:
struct PauseNames {
static let endGameButton = "ENDGAME"
static let pausedButton = "PAUSE"
}
class PauseScene: SKScene {
var center : CGPoint?
var pauseButton: SKSpriteNode?
var endGameButton: SKSpriteNode?
var parentViewController: CustomViewController?
override func didMove(to view: SKView) {
setUpScene()
}
func setUpScene() {
self.backgroundColor = SKColor.white
self.center = CGPoint(x: self.size.width / 2, y: self.size.height / 2)
self.isUserInteractionEnabled = false
setUpSceneNodes()
showPauseEndButtons()
} // end setup scene
func setUpSceneNodes() {
let buttonScale: CGFloat = 0.5
let smallButtonScale: CGFloat = 0.25
let pauseOffset = //some CGPoint
let endGameOffset = //some CGPoint
pauseButton = SKSpriteNode(imageNamed: PauseNames.pausedButton)
pauseButton?.name = PauseNames.pausedButton
pauseButton?.anchorPoint = CGPoint(x: 0.5, y: 0.5)
pauseButton?.position = self.center! + pauseOffset
pauseButton?.alpha = 0
pauseButton?.setScale(buttonScale)
endGameButton = SKSpriteNode(imageNamed: PauseNames.endGameButton)
endGameButton?.name = PauseNames.pausedButton
endGameButton?.anchorPoint = CGPoint(x: 0.5, y: 0.5)
endGameButton?.position = self.center! + endGameOffset
endGameButton?.alpha = 0
endGameButton?.setScale(smallButtonScale)
}
func showPauseEndButtons() {
let buttonFadeInTime = 0.25
let pauseDelay = 1.0
self.addChild(pauseButton!)
self.addChild(endGameButton!)
pauseButton?.run(SKAction.fadeIn(withDuration: buttonFadeInTime))
endGameButton?.run(SKAction.fadeIn(withDuration: buttonFadeInTime))
self.run(SKAction.sequence([
SKAction.wait(forDuration: pauseDelay),
SKAction.run{ self.isUserInteractionEnabled = true }]))
}
func endGamePressed() {
// add confrim logic
parentViewController?.endGame()
}
func unpausePress() {
parentViewController?.unpauseGame()
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let touchLocation = touch.location(in: self)
if endGameButton!.contains(touchLocation) {
endGamePressed()
return
}
else {
unpausePress()
}
} // end for each touch
} // end touchesBegan
override func update(_ currentTime: TimeInterval) {
/* Called before each frame is rendered */
}
} //end class PauseScene
(The pauseButton
is really more of a banner to inform the user of the pause state in this version)
SpriteKit pause Scene (Swift4.2)
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.
SpriteKit: run action while scene is paused
The best way, one Apple also uses in "DemoBots", is to create a world node that you pause instead of the scene.
Create a worldNode property
class GameScene: SKScene {
let worldNode = SKNode()
}
add it to the scene in didMoveToView
addChild(worldNode)
and than add everything you need paused to the worldNode. This includes actions that are normally run by the scene (eg. timers, enemy spawning etc)
worldNode.addChild(someNode)
worldNode.run(someSKAction)
Than in your pause func you say
worldNode.isPaused = true
physicsWorld.speed = 0
and in resume
worldNode.isPaused = false
physicsWorld.speed = 1
You can also add an extra check in your Update function if you have stuff there that you want to ignore when paused.
override func update(_ currentTime: CFTimeInterval) {
guard !worldNode.isPaused else { return }
// your code
}
This way it's much easier to add your paused label or other UI when your game is paused because you haven't actually paused the scene. You can also run any action you want, unless that action is added to the worldNode or to a child of worldNode.
Hope this helps
Stopping an running SKAction - Sprite Kit
You just have to use either:
something.paused = false // or true
to pause actions on the nodesomething.removeAllActions()
to definitely remove actions associated to the node- name your action when launching
something.runAction(action,withKey:"action1")
and thensomething.removeActionForKey("action1")
to remove a given action
You may also change the speed if needed.
Stopping/Pausing a sound that is set to repeat forever in SpriteKit Swift 3
Always good practice to show some code on stack overflow, with your current question we can only take guesses.
If you are using SKActions for sound you can give the repeat action a key and remove that action later. Create the key like so
class GameScene: SKScene {
let soundKey = "RemoveSoundKey" // this avoids typos
}
and than change your action to this
let repeatAction = SKAction.repeatActionForever(YOURSOUNDACTION)
run(repeatAction, withKey: soundKey)
Than you can say
removeAction(forKey: soundKey)
when you want to stop the repeat action.
Note: If you run the action on a node e.g
player.run(repeatAction...)
dont forget to call the remove action on the node
player.removeAction(forKey: soundKey)
Hope this helps
How to use/implement pause() to pause a SKAudioNode?
SKAudioNode
s are SKNode
s and so are able to run
any SKAction
. So get a pause action and ask the node to run it (in Swift):
let audio : SKAudioNode
...
let pause = SKAction.pause()
audio.run(pause)
or shorter:
audio.run(SKAction.pause())
SKAction resumes when appDidBecomeActive() even when game is paused before appDidResignActive()
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.
Pause all ongoing asyncAfter() delays in SpriteKit game
If you're inside of a SKScene class just have the scene run a delay in the form of an SKAction. The action will pause and resume when you pause/unpause either the view or scene.
func spawnMeteorite(timeInterval:Double) {
run(SKAction.wait(forDuration: timeInterval), completion: {
self.spawnMeteorite(timeInterval: timeInterval * 0.9)
})
print("Spawn Meteorite")
}
Related Topics
How to Hide the Navigationbar When Embedding Swiftui in Uikit
Example of Dispatch_Once in Swift
Swift Property Observer in Protocol Extension
Define MACos Window Size Using Swiftui
How to Hide the Navigation Back Button in Swiftui
Using Guard with a Non-Optional Value Assignment
Clearing Uiwebview's Cache in Swift
Can You Give Uistackview Borders
How to Get User Input in Apple's Swift Language in a Command Line Tool
Swiftui .Rotationeffect() Framing and Offsetting
Fbsdkapplicationdelegate Application Openurl:Sourceapplication:Annotation Deprecated
Swift - Get Nsbundle for Test Target
How to Link Xctest Dependency to Production/Main Target
Po Swift String "Unresolved Identifier"
Count Number of Decimal Places in a Float (Or Decimal) in Swift
Use of Undeclared Type 'Viewcontroller' When Unit Testing My Own Viewcontroller in Swift