質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.23%

画面をタッチしてリスタートさせたい

受付中

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 829

tetutaroz

score 12

前提・実現したいこと

「SpriteKitではじめる2Dゲームプログラミング」という本を読んでiOSゲームを勉強して
いる初心者です。
「HelpNagoyaSpeciality」というサンプルゲームをつくりました。
Swift5の環境で動作することは確認できました。

ただこのサンプルコードでは、ゲームオーバーしたあとのリスタートとするためのコードが
実装されておりません。画面をタッチするだけで簡単にリスタートするように改造したいと
思っていますが、具体的にどうやれば良いのかがわかりません。

実現したいことは、ゲームオーバー後に画面をタッチすると「GAME OVER」や落下してきた
スプライトを消せて、スコアも「0」に戻しリスタートさせたいと思っています。

考え方を含めどのようにすればよいのか具体的な方法を教えてもらえないでしょうか。

発生している問題・エラーメッセージ

エラーメッセージ

該当のソースコード

Swift5
import Foundation
import SpriteKit

class GameScene: SKScene, SKPhysicsContactDelegate {
    // どんぶり
    var bowl: SKSpriteNode?
    // タイマー
    var timer: Timer?
    // 落下判定用しシェイプ
    var lowestShape: SKShapeNode?
    // スコア用プロパティ
    var score = 0
    var scoreLabel: SKLabelNode?
    // 名古屋名物ごとのスコア
    var scoreList = [100, 200, 300, 500, 800, 1000, 1500]

    override func didMove(to view: SKView) {

        self.physicsWorld.gravity = CGVector(dx: 0.0, dy: -2.0)

        self.physicsWorld.contactDelegate = self

        // 背景スプライトの追加
        let background = SKSpriteNode(imageNamed: "background")
        background.position = CGPoint(x: self.size.width*0.5, y: self.size.height*0.5)
        background.size = self.size
        self.addChild(background)

        // 落下検知用シェイプノードを追加
        let lowestShape = SKShapeNode(rectOf: CGSize(width: self.size.width*3, height: 10))
        // 画面外に配置
        lowestShape.position = CGPoint(x: self.size.width*0.5, y: -10)

        // シェイプに合わせてPhysicsBodyを生成
        // シェイプの大きさで物理シミュレーションを行う
        let physicsBody = SKPhysicsBody(rectangleOf: lowestShape.frame.size)
        // 落下しないよう固定
        physicsBody.isDynamic = false
        // 衝突を検知する対象のビットマスクを指定
        // 名古屋名物との衝突を検知する
        physicsBody.contactTestBitMask = 0x1 << 1
        lowestShape.physicsBody = physicsBody
        self.addChild(lowestShape)
        self.lowestShape = lowestShape

        // どんぶりを追加
        let bowlTexture = SKTexture(imageNamed: "bowl")
        let bowl = SKSpriteNode(texture: bowlTexture)
        bowl.position = CGPoint(x: self.size.width*0.5, y: 100)
        bowl.size = CGSize(width: bowlTexture.size().width*0.5, height: bowlTexture.size().height*0.5)
        // テクスチャの不透過部分の形状で物理シミュレーションを行う
        bowl.physicsBody = SKPhysicsBody(texture: bowlTexture, size: bowl.size)
        // 落下しないよう固定
        bowl.physicsBody?.isDynamic = false
        self.bowl = bowl
        self.addChild(bowl)

        // スコア用ラベル
        var scoreLabel = SKLabelNode(fontNamed: "Helvetica")
        scoreLabel.position = CGPoint(x: self.size.width*0.92, y: self.size.height*0.78)
        scoreLabel.text = "¥0"
        scoreLabel.fontSize = 32
        scoreLabel.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.right // 右寄せ
        scoreLabel.fontColor = UIColor.green
        self.addChild(scoreLabel)
        self.scoreLabel = scoreLabel

        self.fallNagoyaSpecialty()

        // タイマーを生成
        self.timer = Timer.scheduledTimer(timeInterval: 3.0, target: self, selector: #selector(GameScene.fallNagoyaSpecialty), userInfo: nil, repeats: true)

    }

    // 名古屋名物を落下させる
    @objc func fallNagoyaSpecialty() {
        // 0〜6のランダムな整数を発生させる
        let index = Int(arc4random_uniform(7))
        // 選択された番号のテクスチャを読み込む
        let texture = SKTexture(imageNamed: "\(index)")
        // テクスチャからスプライトを生成する
        let Nagoyasprite = SKSpriteNode(texture: texture)
        Nagoyasprite.position = CGPoint(x: self.size.width*0.5, y: self.size.height)
        Nagoyasprite.size = CGSize(width: texture.size().width*0.5, height: texture.size().height*0.5)

        // テクスチャからPhysicsBodyを追加
        Nagoyasprite.physicsBody = SKPhysicsBody(texture: texture, size: Nagoyasprite.size)

        Nagoyasprite.physicsBody?.contactTestBitMask = 0x1 << 1 // 衝突を検知する対象のビットマスクを指定
        self.addChild(Nagoyasprite)
        // 落下した物に応じてスコアを加算する
        self.score += self.scoreList[index]
        // 金額のラベルを更新
        self.scoreLabel?.text = \(self.score)"

    }
    // 衝突が発生したときに呼ばれるメソッド
    func didBegin(_ contact: SKPhysicsContact) {

        // 衝突した一方が落下判定用シェイプだったら
        if contact.bodyA.node == self.lowestShape || contact.bodyB.node == self.lowestShape {
            // ゲームオーバースプライトを表示
            let GameOversprite = SKSpriteNode(imageNamed: "gameover")
            GameOversprite.position = CGPoint(x: self.size.width*0.5, y: self.size.height*0.5)
            self.addChild(GameOversprite)

            // アクションを停止させる
            self.isPaused = true
            // タイマーを止める
            self.timer?.invalidate()
        } 
    }

    // タッチ開始時に呼ばれるメソッド
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {


        for touch: AnyObject in touches {
            // シーン上のタッチされた位置を取得する
            let location = touch.location(in: self)
            // タッチされた位置にノードを移動させるアクションを作成する
            let action = SKAction.move(to: location, duration: 0.2)
            // どんぶりのスプライトでアクションを実行する
            self.bowl?.run(action)
        }
    }

    // ドラック時
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        for touch: AnyObject in touches {
            let location = touch.location(in: self)
            let action = SKAction.move(to: location, duration: 0.2)
            self.bowl?.run(action)
        }
    }
}

試したこと

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches {

この部分にやりたいことをコード化して実装してやればできると思いやってみようとしましたが
具体的な方法がわからずギブアップ状態です。

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • fuzzball

    2019/05/17 15:17

    コードは ``` で囲って下さい。 https://teratail.com/help#about-markdown

    キャンセル

  • 退会済みユーザー

    2019/05/20 09:13

    複数のユーザーから「やってほしいことだけを記載した丸投げの質問」という意見がありました
    「質問を編集する」ボタンから編集を行い、調査したこと・試したことを記入していただくと、回答が得られやすくなります。

回答 1

0

考え方を含めどのようにすればよいのか具体的な方法を教えてもらえないでしょうか。 

その本が手元にあるわけではないので、具体的な方法を提示することは無理があります。

考え方的には2パターンぐらいがあるのかなと思います。

方法1 シーンをまるごと差し替える

たぶんこっちの方が主流かなと思われます。楽ですし。

// ※注:擬似コードです
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    if ゲームオーバー中だったら {
        if let scene = SKScene(fileNamed: "GameplayScene") { // この行は適当です。とにかく自分と同じシーンを作り直します。
            self.view?.presentScene(scene)
        }
    } else {
        for touch: AnyObject in touches {
            // シーン上のタッチされた位置を取得する
            let location = touch.location(in: self)
            // タッチされた位置にノードを移動させるアクションを作成する
            let action = SKAction.move(to: location, duration: 0.2)
            // どんぶりのスプライトでアクションを実行する
            self.bowl?.run(action)
        }
    }
}

方法2 頑張って現状から初期状態にあらゆる状態を戻す処理を書く

方法1のシーン差し替えが仕様上うまくいかない場合はこちらになるのかなと。

// ※注:擬似コードです
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    if ゲームオーバー中だったら {
        GAME OVERのスプライトを消す
        落下してきたスプライトを消す
        スコアを0にする
        ゲームを再スタートさせる
    } else {
        for touch: AnyObject in touches {
            // シーン上のタッチされた位置を取得する
            let location = touch.location(in: self)
            // タッチされた位置にノードを移動させるアクションを作成する
            let action = SKAction.move(to: location, duration: 0.2)
            // どんぶりのスプライトでアクションを実行する
            self.bowl?.run(action)
        }
    }
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/05/18 10:22

    早々にアドバイスしていただきましてありがとうございます。
    アドバイスしていただいている方法1,2とも「ゲームオーバー中」の判定方法(条件)がわかりません。
    方法1のやり方が良いように思いますが、「シーンを丸ごと差し替える」具体的なやり方を教えてもらえないでしょうか。
    方法2の「GAME OVERのスプライトを消す」「落下してきたスプライトを消す」は
    「GameOversprite.removeFormParent()」「Nagoyasprite.removeFormParent()」でよろしいでしょうか。
    ソースコードは全文を載せるように変更しております。申し訳ありませんがよろしくお願いします。

    キャンセル

  • 2019/05/20 09:07

    > 「ゲームオーバー中」の判定方法(条件)がわかりません。

    まずは何でも良いので考えて実験してみてください。

    キャンセル

  • 2019/05/20 11:54 編集

    我々はHelpNagoyaSpecialityというゲームを詳しく知らない為、どんな条件になればゲームオーバーになるか分からない。プログラムだけ開示されても困るので、ゲーム画面を見せて下さると回答する側は非常に助かります。

    キャンセル

  • 2019/05/20 22:00

    takabosoftさん stdioさん アドバイスありがとうございます。もう少し色々試してから再度質問させてもらうようにします。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.23%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る