2017-01-14 9 views
2

Я хочу «перезагрузить» и «перезапустить» GameScene, так что, как будто GameScene был сначала вызван. Я смотрел на различные методы для этого, но каждый раз, когда я получаю предупреждение, что я пытаюсь добавить узел к родительскому, у которого уже есть родитель. Однако в моем коде я удаляю все мои существующие узлы, поэтому я действительно запутался в том, как сбросить GameScene. Вот как я это делаю сейчас (этот код вызывается, когда я хочу, чтобы перезапустить GameScene с нуля, и это называется в классе GameScene):Swift 3 (SpriteKit): Сессия 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{} не называется, хотя каждый узел удаляется и кадр не падает с течением времени. Вот что у меня в 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

О «попытке добавить узел к родительскому, у которого уже есть родительский элемент», я бы сказал, что вы сделали какой-то беспорядок :) Это может произойти, если старая сцена не освобождена, а вы храните узлы глобально и пытаетесь их добавить к текущей сцене. Тем не менее, это просто догадка, может быть, ваш старый sccene будет освобожден должным образом, и вы действительно пытаетесь добавить узел к другому родителю. Таким образом, требуется больше информации. В какой момент вы добавляете узел в сцену, когда получаете эту ошибку? – Whirlwind

+0

Хорошо, если все переменные в файле сцены игры определены вне класса (включая узлы, которые получают эту ошибку)? Это может быть проблема? –

+0

^Это сработало, но теперь, когда игра перезапустила игру в левом нижнем углу экрана? Как это исправить? –

ответ

8

Как сбросить сцену?

Вам просто нужно представить новую, ту же сцену, когда захотите. Итак, вы все хорошо.

Возможные проблемы с утечкой?

Кроме того, если у вас нет утечек в вашей игре, не означает, что нет сильных опорных циклов, вам даже не нужно self.removeAllChildren() и self.removeAllActions() ... Конечно, если вы явно хотите, чтобы остановить действия до перехода анимации начинается, использование этого метода имеет смысл. Дело в том, что когда сцена освобождается, все объекты, которые зависят от нее, должны/будут также освобождаться.

Тем не менее, если вы не знаете с самого начала, что вы делаете, и как предотвратить утечку, например. вы используете сильное «я» в блоке, которое является частью последовательности действий, которая повторяется навсегда, тогда у вас наверняка есть утечка, и self.removeAllActions() может помочь (во многих случаях, но это не окончательное решение). Я бы рекомендовал прочитать о списках захвата и ARC в целом, потому что может быть полезно знать, как все это работает только из-за этих ситуаций.

Сцена является корневым узлом

не Calling 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 просто скажет вам, если сцена освобождена должным образом или нет. Но это не скажет вам, почему. Это зависит от вас, чтобы найти, что в вашем коде сохраняя сцену.

+0

Это работает, но по какой-то причине теперь, когда игра перезапускается, игра находится в левом нижнем углу. –

+0

@ J.Treutlein Код на основе кода вашего вопроса. Означает, что сцена не создана из файла sks, что означает, что она имеет значение anchorPoint равное 0.0, 0.0. Поэтому я не знаю, чего вы действительно хотите, но если вы хотите, чтобы ваши узлы по умолчанию добавлялись в середину экрана, либо замените якорь сцены на 0.5, 0.5, либо создайте сцену из файла sks. Кстати, я думаю, что я объяснил это уже в одном из ваших вопросов :) – Whirlwind

+0

Большое спасибо за это, что работает! Чтобы ответить на ваш другой вопрос: Нет, я еще не задал вопрос о SKScenes, но да, один из моих первых вопросов касался якорных точек, но отличается от этого. Так что ты прав. :) Спасибо за помощь, так или иначе, вы хорошо знаете SpriteKit. –

0

Есть два основных способа, которые я могу придумать, чтобы сделать это. Основной путь, который я делаю, это то, что если игра закончилась (из-за того, что здоровье персонажа падает до нуля или они сталкиваются с объектом, который заставляет раунд заканчиваться, или время вверх или что-то еще), когда это происходит Мне нравится переход на новую сцену, которая является сводным экраном их оценки, как далеко они сделали это и т. Д.

Я делаю это, имея переменную bool в главной сцене GamePlay.

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, запустив функцию ViewDrawLoad и настроив 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)) 
      } 
     } 

Это мой предпочтительный метод обработки игры по переходам.

Другим способом было бы создать функцию, которая сбрасывает все переменные, которые были изменены во время игры. Затем вы можете использовать тот же код из функции Update выше, но вместо перехода вы можете создать ярлык на сцене. Если пользователь нажмет на ярлык, он погасит эту функцию, чтобы сбросить все измененные переменные, сбросить местоположение игроков, остановить все действия, сбросить здоровье игроков и т. Д. В зависимости от того, сколько вещей вы меняете в ходе игрового процесса вероятно, будет более практичным (как я уже нашел), перейти на новую сцену, чтобы дать резюме, а затем перезагрузить GamePlayScene с помощью кнопки. Тогда все будет загружаться так же, как в первый раз, когда пользователь ввел этот основной GamePlayScene.

+0

Моя проблема теперь в том, что позиции игры в левом углу, когда игровая сцена сбрасывается. Когда они нажимают кнопку повтора, это происходит. Можете ли вы отредактировать свой пост с помощью кода, который вы используете, когда нажата кнопка повтора, чтобы убедиться, что у меня нет чего-то смешного? –

+0

@ J.Treutlein Я только что отредактировал, чтобы добавить пример того, как я реализовал эту кнопку повтора. Вы также можете использовать тот же самый код перехода, который существует в инструкции обновления, и изменить имя сцены. – crpDave

+0

Большое спасибо за помощь, но ответ Вихря действительно решил проблему. В любом случае, это полезно для других людей с одинаковой проблемой. –

 Смежные вопросы

  • Нет связанных вопросов^_^