我想重置'并且'重新启动' GameScene就好像GameScene首次被调用一样。我已经看过不同的方法来做这件事,但每次我收到警告我都试图将节点添加到已经拥有父节点的父节点。但是,在我的代码中,我删除了所有现有节点,因此我对如何重置GameScene感到困惑。这就是我现在这样做的方法(当我想从头开始重新启动GameScene并在GameScene类中调用时,会调用此代码):
let scene = GameScene(size: self.size)
scene.scaleMode = .aspectFill
let animation = SKTransition.fade(withDuration: 1.0)
self.view?.presentScene(scene, transition: animation)
self.removeAllChildren()
self.removeAllActions()
self.scene?.removeFromParent()
1.Edited: 我意识到为什么我会收到此警告:"我试图将节点添加到已经存在的父节点有父母"是因为我拥有类外部场景的所有变量和全局变量。但是,现在当游戏重新开始时,游戏就在左下角。为什么会这样,我该如何解决这个问题? - FIXED
2.Edited: 现在一切正常,但现在我担心的是即使每个节点都被删除,deinit{}
也不会被调用fps不会随着时间的推移而下降。以下是我在GameViewController中设置场景的方法以及我的GameScene(每个与场景相关的实例基本上都是相关的):
import UIKit
import SpriteKit
import GameplayKit
var screenSize = CGSize()
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
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
screenSize = scene.size
// Present the scene
view.presentScene(scene)
}
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
然后我的GameScene基本上是:
import SpriteKit
import GameplayKit
class GameScene: SKScene, SKPhysicsContactDelegate {
//Declare and initialise variables and enumerations here
deinit{print("GameScene deinited")}
override func didMove(to view: SKView) {
//Setup scene and nodes
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
//Do other things depending on when and where you touch
//When I want to reset the GameScene
let newScene = GameScene(size: self.size)
newScene.anchorPoint = CGPoint(x: 0.5, y: 0.5)
newScene.scaleMode = self.scaleMode
let animation = SKTransition.fade(withDuration: 1.0)
self.view?.presentScene(newScene, transition: animation)
}
任何答案都将非常感谢:)
答案 0 :(得分:9)
如何重置场景?
您只需要随时再次呈现新的相同场景。所以,你做得很好。
可能出现泄漏问题?
此外,如果您的游戏没有泄漏,意味着没有强大的参考周期,您甚至不需要self.removeAllChildren()
和self.removeAllActions()
......当然,如果您明确要停止过渡动画开始前的动作,使用此方法是有意义的。关键是,当场景解除分配时,依赖于它的所有对象也应该/将要解除分配。
但是,如果你从一开始就不知道你在做什么,以及如何防止泄漏,例如。你在块中使用强自我,这是一个动作序列的一部分,它永远重复,然后你肯定有泄漏,self.removeAllActions()
可能有帮助(在许多情况下,但它不是一个最终的解决方案)。我建议一般性地阅读有关捕获列表和ARC的信息,因为了解这些情况是如何工作的。
场景是根节点
在场景上调用removeFromParent()
无效。场景是根节点,因此无法在当前上下文中删除它。如果你检查场景的父属性,你会发现它是零。当然可以将场景添加到另一个场景,但是在这种情况下,作为子场添加的场景将充当普通节点。
最后,如何呈现新场景?很容易,像这样:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let newScene = GameScene(size: self.size)
newScene.scaleMode = self.scaleMode
let animation = SKTransition.fade(withDuration: 1.0)
self.view?.presentScene(newScene, transition: animation)
}
如果某些内容对您不起作用,则可能是您有泄漏(意味着您的场景未被解除分配)。要检查这一点,GameScene中的某个地方会覆盖deinit方法,如下所示:
deinit{print("GameScene deinited")}
再向你解释一下......应该发生的是你应该呈现一个新场景,应该发生过渡,应该释放一个旧场景,你应该看到一个具有初始状态的新场景。
同样覆盖deinit只会告诉您场景是否正确解除分配。但它不会告诉你原因。您可以在代码中找到保留场景的内容。
答案 1 :(得分:0)
我可以想到有两种主要方法可以做到这一点。我这样做的主要方式是,如果游戏结束,(由于角色健康状况下降到零,或者它们与导致轮次结束的对象发生碰撞,或者时间到了或等等),当发生这种情况时我喜欢过渡到一个新的场景,这个场景是他们得分的总结屏幕,他们做了多远等等。
我这样做是通过在主GamePlay场景中设置一个bool变量来实现的。
var gameOver: Bool = false
然后在激活的代码中导致游戏结束设置变量= true。
在更新功能中检查gameOver == true并转换到GameOverScene。
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
// Initialize _lastUpdateTime if it has not already been
if (self.lastUpdateTime == 0) {
self.lastUpdateTime = currentTime
}
// Calculate time since last update
let dt = currentTime - self.lastUpdateTime
// Update entities
for entity in self.entities {
entity.update(deltaTime: dt)
}
self.lastUpdateTime = currentTime
if gameOver == true {
print("Game Over!")
let nextScene = GameOverScene(size: self.scene!.size)
nextScene.scaleMode = self.scaleMode
nextScene.backgroundColor = UIColor.black
self.view?.presentScene(nextScene, transition: SKTransition.fade(with: UIColor.black, duration: 1.5))
}
}
更新功能将检查每个帧渲染以查看游戏是否结束,如果发现它已经结束,它将执行您需要的任何操作,然后呈现下一个场景。
然后在GameOverScene上我放了一个按钮说“重试”,当他们点击它时再次触发GamePlayScene,运行视图DidLoad功能并从头开始设置GamePlayScene。
这是我如何处理它的一个例子。调用场景转换有几种不同的方法。如果它不能正常工作,你可以试试这个。
if node.name == "retryButton" {
if let scene = GameScene(fileNamed:"GameScene") {
// Configure the view.
let skView = self.view! as SKView
/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = true
/* Set the scale mode to scale to fit the window */
scene.scaleMode = .aspectFill
scene.size = skView.bounds.size
skView.presentScene(scene, transition: SKTransition.fade(withDuration: 2.0))
}
}
这是我在过渡时处理游戏的首选方法。
另一种方法是创建一个函数,重置在播放过程中发生变化的所有变量。然后你可以使用上面更新功能中的相同代码,但不是过渡,你可以在场景上创建一个标签。如果用户单击该标签,它将触发该功能以重置所有已更改的变量,重置玩家位置,停止所有操作,重置玩家健康状况等。取决于您在游戏过程中改变了多少事情它可能更实用,(正如我发现的那样),转换到新场景以提供摘要,然后通过按钮重新加载GamePlayScene。然后一切都会像第一次用户输入主GamePlayScene一样加载。