上下文
虽然有些游戏选择放弃暂停菜单 – 假设因为游戏时间较短,如Don’t Grind – 我个人认为暂停游戏是一项关键功能,并希望学习如何在Swift 3中为SpriteKit实现它.
我已经看到尝试使用UIAlertController这样做,但是我 – 也许是错误的 – 相信更好的选择是在当前的SKView上覆盖SKView.
我看了Apple的DemoBots,看看我是否能弄清楚他们是如何暂停比赛的.但是,在我的设备上下载并运行它后,它会导致错误,所以我不想跟风.但是,如果有人可以彻底解释过多的文件,如“LevelScene Pause”,“SceneManager”,“SceneOperation”等,以及它们如何协同工作,那也很酷.
题
如何在GameScene上叠加SKView来制作暂停菜单?
最低工作实例
M.W.E.,
StackOverflow SpriteKit with Menu,是一个准确回答情境的准系统“游戏”.请回答有关M.W.E.的问题.
更新
以下是M.W.E的修改版本. “GameScene”文件.
它考虑为要暂停的元素添加主节点,为暂停菜单添加另一个节点.
暂停菜单工作时,即使gameNode.isPaused = true,背景仍然有效. (尝试点击最左边的蓝色精灵).
//
// GameScene.swift
// StackOverflow
//
// Created by Sumner on 1/17/17.
// Copyright © 2017 Sumner. All rights reserved.
//
import SpriteKit
import GameplayKit
class GameScene: SKScene {
var cam: SKCameraNode!
var sprite = SKSpriteNode(imageNamed: "sprite")
var sprite2 = SKSpriteNode(imageNamed: "sprite2")
let pauseLabel = SKLabelNode(text: "Pause!")
/*
*
* START: NEW CODE
*
*/
let gameNode = SKNode()
var pauseMenuSprite: SKShapeNode!
let pauseMenuTitleLabel = SKLabelNode(text: "Pause Menu")
let pauseMenuContinueLabel = SKLabelNode(text: "Resume game?")
let pauseMenuToMainMenuLabel = SKLabelNode(text: "Main Menu?")
/*
*
* END: NEW CODE
*
*/
var timeStart: Date!
init(size: CGSize, difficulty: String) {
super.init(size: size)
gameDifficulty = difficulty
timeStart = Date()
/*
*
* START: NEW CODE
*
*/
pauseMenuSprite = SKShapeNode(rectOf: size)
/*
*
* END: NEW CODE
*
*/
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func didMove(to view: SKView) {
backgroundColor = SKColor.white
print("Game starting with \(gameDifficulty) difficulty")
// Scale Sprites
sprite.setScale(0.3)
sprite2.setScale(0.3)
sprite.position = CGPoint(x: size.width/4,y: size.height/2)
sprite2.position = CGPoint(x: size.width/4 * 3,y: size.height/2)
/*
*
* START: NEW CODE
*
*/
gameNode.addChild(sprite)
gameNode.addChild(sprite2)
addChild(gameNode)
/*
*
* END: NEW CODE
*
*/
if gameDifficulty == "hard" {
let sprite3 = SKSpriteNode(imageNamed: "sprite")
sprite3.setScale(0.3)
sprite3.position = CGPoint(x: size.width/4 * 2,y: size.height/2)
addChild(sprite3)
}
pauseLabel.fontColor = SKColor.black
pauseLabel.position = CGPoint(x: size.width/4 * 2,y: size.height/4)
addChild(pauseLabel)
}
func touchDown(atPoint pos : CGPoint) {
}
func touchMoved(toPoint pos : CGPoint) {
}
func touchUp(atPoint pos : CGPoint) {
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches { self.touchDown(atPoint: t.location(in: self)) }
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches { self.touchMoved(toPoint: t.location(in: self)) }
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
let touchLocation = touch!.location(in: self)
let pausedTouchLocation = touch?.location(in: pauseMenuSprite)
if sprite.contains(touchLocation) {
print("You tapped the blue sprite")
/*
let alert = UIAlertController(title: "Alert", message: "Message", preferredStyle: UIAlertControllerStyle.alert)
let action = UIAlertAction(title: "Ok", style: .default) { action in
// Handle when button is clicked
let reveal = SKTransition.doorsOpenVertical(withDuration: 0.5)
let menuScene = MenuScene(size: self.size)
self.view?.presentScene(menuScene, transition: reveal)
}
alert.addAction(action)
if let vc = self.scene?.view?.window?.rootViewController {
vc.present(alert, animated: true, completion: nil)
}
*/
}
if sprite2.contains(touchLocation) {
print("You tapped the purple sprite")
let now = Date()
let howLong = now.timeIntervalSinceReferenceDate - timeStart.timeIntervalSinceReferenceDate
let reveal = SKTransition.doorsOpenVertical(withDuration: 0.5)
let scoreScene = ScoreScene(size: self.size, score: howLong)
self.view?.presentScene(scoreScene, transition: reveal)
}
/*
*
* START: NEW CODE
*
*/
if pauseMenuContinueLabel.contains(pausedTouchLocation!) {
pauseMenuSprite.removeFromParent()
pauseMenuSprite.removeAllChildren()
gameNode.isPaused = true
}
if pauseMenuToMainMenuLabel.contains(pausedTouchLocation!) {
let reveal = SKTransition.doorsOpenVertical(withDuration: 0.5)
let menuScene = MenuScene(size: self.size)
self.view?.presentScene(menuScene, transition: reveal)
}
if pauseLabel.contains(touchLocation) {
print("pause")
setParametersForPauseMenu(size: size)
addChild(pauseMenuSprite)
gameNode.isPaused = true
}
/*
*
* END: NEW CODE
*
*/
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches { self.touchUp(atPoint: t.location(in: self)) }
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
/*
*
* START: NEW CODE
*
*/
func setParametersForPauseMenu(size: CGSize) {
pauseMenuSprite.fillColor = SKColor.white
pauseMenuSprite.alpha = 0.85
pauseMenuSprite.position = CGPoint(x: size.width / 2, y: size.height / 2)
pauseMenuSprite.zPosition = 100
pauseMenuTitleLabel.fontColor = SKColor.black
pauseMenuContinueLabel.fontColor = SKColor.black
pauseMenuToMainMenuLabel.fontColor = SKColor.black
pauseMenuTitleLabel.position = CGPoint(x: 0 ,y: size.height / 2 - pauseMenuSprite.frame.size.height / 6 )
pauseMenuContinueLabel.position = CGPoint(x: 0 ,y: size.height / 2 - pauseMenuSprite.frame.size.height / 6 * 4 )
pauseMenuToMainMenuLabel.position = CGPoint(x: 0 ,y: size.height / 2 - pauseMenuSprite.frame.size.height / 6 * 5)
pauseMenuSprite.addChild(pauseMenuTitleLabel)
pauseMenuSprite.addChild(pauseMenuContinueLabel)
pauseMenuSprite.addChild(pauseMenuToMainMenuLabel)
}
/*
*
* END: NEW CODE
*
*/
}
最佳答案 我在游戏场景中暂停游戏的问题已经挣扎了一段时间.
正如其他几个人在评论中所建议的那样,建立一个“暂停场景”,以便在游戏暂停时转换为一个有效的解决方案.这种方法避免了在游戏暂停或动画在唤醒时跳过时游戏场景中定时器触发可能遇到的问题.
为了实现暂停场景,我使用UIViewController的自定义子类来处理场景转换.
在我的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)
}
在MyGameScene类(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
}
这是我的PauseScene实现.当用户点按暂停场景中的任何位置时,此版本将取消暂停,但终止当前游戏的endGameButton除外:
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
(pauseButton实际上更像是一个横幅,用于通知用户此版本中的暂停状态)