前提・実現したいこと
「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ページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
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)
}
}
}
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.23%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
質問への追記・修正、ベストアンサー選択の依頼
fuzzball
2019/05/17 15:17
コードは ``` で囲って下さい。 https://teratail.com/help#about-markdown
2019/05/20 09:13
複数のユーザーから「やってほしいことだけを記載した丸投げの質問」という意見がありました
「質問を編集する」ボタンから編集を行い、調査したこと・試したことを記入していただくと、回答が得られやすくなります。