2017-01-14 9 views
2

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::私はなぜ気づいこれは私が(私は最初からGameSceneを再起動するときに、このコードが呼び出され、それがGameSceneクラス内で呼び出される)今それを行う方法です私はこの警告を受け取りました: "私はクラスの外のシーンのための変数とグローバル変数としてすべての変数を持っていたので、"私はすでに親を持っている親にノードを追加しようとしています。しかし、ゲームが再開すると、ゲームは左下に表示されます。なぜこれが当てはまり、どうすればこの問題を解決できますか? -

2.EditedをFIXED:すべてが今正常に動作しますが、今の私の懸念は、すべてのノードが削除されていてもdeinit{}がないと呼ばれ、fpsのは、時間の経過とともに低下しないということです。

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) 
} 
ここで私は(関連があるので、基本的にはすべてのシーンに関連するすべてのインスタンス)のシーンを設定するために、私のGameSceneで私GameViewControllerに持っているものです

どのような答えも大歓迎です:)

+0

「親がすでに存在する親にノードを追加しようとしています」ということについて、あなたはいくつか混乱していると言います:)これは、古いシーンが割り当て解除されず、ノードをグローバルに格納していて、現在のシーンにそれでも、これはちょうど推測です。あなたの古いscceneが適切に割り当て解除され、実際に別の親にノードを追加しようとしているかもしれません。だから、より多くの情報が必要です。あなたはそのエラーが発生したときにシーンにノードを追加していますか? – Whirlwind

+0

ゲームシーンファイルのすべての変数がクラス外に定義されている場合(このエラーが発生しているノードを含む)は問題ありませんか?それが問題だろうか? –

+0

^これはうまくいきましたが、ゲームが画面の左下隅でゲームを再開すると、今すぐですか?これをどうやって解決するのですか? –

答えて

8

シーンをリセットするには?

必要に応じていつでも新しい、同じシーンを再度表示するだけで済みます。それで、あなたはそれをうまくやっています。

リークの問題が考えられますか?また

、あなたはゲーム内のリークを持っていない場合は、強い参照サイクルを意味していない、あなたが明示的に遷移アニメーションを開始する前にアクションを停止したい場合は、あなたも、もちろん... self.removeAllChildren()self.removeAllActions()を必要としませんこの方法を使用することは理にかなっています。要点は、シーンの割り当てが解除されると、それに依存するすべてのオブジェクトも解放されるべきです。

まだ、あなたがやっていることと漏れからの防止方法を最初から分かっていない場合、あなたは永遠に繰り返されるアクションシーケンスの一部であるブロックに強い自己を使用していますが、確かに漏れがあり、self.removeAllActions()が助けになることがあります(多くの場合、究極の解決策ではありません)。キャプチャリストとARCについて一般的に読むことをお勧めします。なぜなら、これらの状況のた​​めにすべての機能が動作するかどうかを知ることが有用な場合があるからです。

シーンは、ルートノード自体は効果がありませんシーンにremoveFromParent()を呼び出す

です。シーンはルートノードなので、現在のコンテキストでは削除できません。シーンの親プロパティをチェックすると、それはnilであることがわかります。もちろん、別のシーンにシーンを追加することは可能ですが、その場合、子として追加されたシーンは通常のノードとして動作します。

最後に、新しいシーンを紹介する方法は?簡単には、次のように:

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ファイルからシーンを作成してください。ところで、私はあなたの質問の1つですでにこれを説明したと思う:) – Whirlwind

+0

それは本当にありがとう、それは動作します!あなたの他の質問に答えるには:いいえ、まだSKScenesについての質問はしていませんが、私の最初の質問の1つはanchorPointについてでしたが、これとは異なります。だからあなたはちょっと正しいです:)とにかくSpriteKitをよく知ってくれてありがとう。 –

0

これを行うには2つの主な方法があります。私がこれを行う主な方法は、ゲームが終了した場合(キャラクターの健康状態がゼロになるか、ラウンドを終了させるオブジェクトに衝突するか、時間がかかったかなど)、そのとき私はスコアの要約画面である新しいシーンに移行するのが好きです。

このように、メインのGamePlayシーンにbool変数を設定することでこれを行います。

var gameOver: Bool = false 

次に、ゲームを終了させるために発火するコードで、その変数= trueを設定します。

update関数で、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)) 
      } 
     } 

これは、トランジションオーバーのゲームを処理するための私の好ましい方法です。

もう1つの方法は、再生中に変更されたすべての変数をリセットする関数を作成することです。上記のUpdate関数から同じコードを使用できますが、トランジションの代わりにシーンにラベルを作成することができます。ユーザーがラベルをクリックすると、変更されたすべての変数がリセットされ、プレイヤーの位置がリセットされ、すべてのアクションが停止され、プレイヤーの健康状態がリセットされます。ゲームプレイ中に変更するアイテムの数おそらくもっと現実的です(私が見つけたように)、新しいシーンに移行して要約を出し、ボタンを使ってGamePlaySceneをリロードします。その後、ユーザーがメインのGamePlaySceneに初めて入ったときと同じようにすべてがロードされます。

+0

私の問題は、ゲームの場面がリセットされたときの左手の角のゲームの位置です。リトライボタンをクリックすると、これが発生します。再試行ボタンをクリックしたときに使用するコードであなたの投稿を編集してください。私は何か面白いことがないことを確認してください。 –

+0

@ J.Treutleinリトライボタンの実装方法の例を追加しました。また、更新ステートメントに存在するものと同じ遷移コードを使用して、シーンの名前を変更することもできます。 – crpDave

+0

本当に助けてくれてありがとうございますが、Wh​​irlwindの答えは実際に問題を解決しました。とにかくおかげさまで、これは同じ問題を抱えている他の人にとって有益です。 –

関連する問題