

Angry Birdsのような背景を動かすことで進んでるように見せるゲームを考えています。

地面の障害物（アイテム・ここではサンゴcoral）に衝突したら、プレイヤーは画面中央に自動的に戻ってしまうようにしたいのですがどうしたらいいでしょうか？

おそらく衝突の瞬間に`removeTo`を使えばいいと思うのですが、、  
教えてください！

[code]

    import SpriteKit
    
    
    class GameScene: SKScene, SKPhysicsContactDelegate {
        // MARK: - 定数定義
        /// 定数
        struct Constants {
            /// Player画像
            static let PlayerImages = ["shrimp01", "shrimp02", "shrimp03", "shrimp04"]
        }
    
        /// 衝突の判定につかうBitMask
        struct ColliderType {
            /// プレイキャラに設定するカテゴリ
            static let Player: UInt32 = (1 << 0)
            /// 天井・地面に設定するカテゴリ
            static let World: UInt32  = (1 << 1)
            /// サンゴに設定するカテゴリ
            static let Coral: UInt32  = (1 << 2)
            /// スコア加算用SKNodeに設定するカテゴリ
            static let Score: UInt32  = (1 << 3)
            /// スコア加算用SKNodeに衝突した際に設定するカテゴリ
            static let None: UInt32   = (1 << 4)
        }
    
        // MARK: - 変数定義
        /// プレイキャラ以外の移動オブジェクトを追加する空ノード
        var baseNode: SKNode!
        /// サンゴ関連のオブジェクトを追加する空ノード(リスタート時に活用)
        var coralNode: SKNode!
    
        /// プレイキャラ
        var player: SKSpriteNode!
    
        /// スコアを表示するラベル
        var scoreLabelNode: SKLabelNode!
        /// スコアの内部変数
        var score: UInt32!
    
        // MARK: - 関数定義
    
        override func didMoveToView(view: SKView) {
            // 変数の初期化
            score = 0
    
            // 物理シミュレーションを設定
            self.physicsWorld.gravity = CGVector(dx: 0.0, dy: -2.0)
            self.physicsWorld.contactDelegate = self
    
            // 全ノードの親となるノードを生成
            baseNode = SKNode()
            baseNode.speed = 1.0
            self.addChild(baseNode)
    
            // 障害物を追加するノードを生成
            coralNode = SKNode()
            baseNode.addChild(coralNode)
    
            // 背景画像を構築
            self.setupBackgroundSea()
    
            // 天井と地面を構築
            self.setupCeilingAndLand()
            // プレイキャラを構築
            self.setupPlayer()
            // 障害物のサンゴを構築
            self.setupCoral()
            // スコアラベルの構築
            self.setupScoreLabel()
        }
    
        /// 背景画像を構築
        func setupBackgroundSea() {
            // 背景画像を読み込む
            let texture = SKTexture(imageNamed: "background")
            texture.filteringMode = .Nearest
    
            // 必要な画像枚数を算出
            let needNumber = 2.0 + (self.frame.size.width / texture.size().width)
    
            // 左に画像一枚分移動アニメーションを作成
            let moveAnim = SKAction.moveByX(-texture.size().width, y: 0.0, duration: NSTimeInterval(texture.size().width / 10.0))
            // 元の位置に戻すアニメーションを作成
            let resetAnim = SKAction.moveByX(texture.size().width, y: 0.0, duration: 0.0)
            // 移動して元に戻すアニメーションを繰り返すアニメーションを作成
            let repeatForeverAnim = SKAction.repeatActionForever(SKAction.sequence([moveAnim, resetAnim]))
    
            // 画像の配置とアニメーションを設定
            for var i:CGFloat = 0; i < needNumber; ++i {
                // SKTextureからSKSpriteNodeを作成
                let sprite = SKSpriteNode(texture: texture)
                // 一番奥に配置
                sprite.zPosition = -100.0
                // 画像の初期位置を設定
                sprite.position = CGPoint(x: i * sprite.size.width, y: self.frame.size.height / 2.0)
                // アニメーションを設定
                sprite.runAction(repeatForeverAnim)
                // 親ノードに追加
                baseNode.addChild(sprite)
            }
        }
    
    
        func setupBackground(){
        // 背景画像のスプライトを配置
        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)}
    
    
        /// 天井と地面を構築
        func setupland(){
            // 背景画像のスプライトを配置
            let land = SKSpriteNode(imageNamed: "land")
            land.position = CGPoint(x: self.size.width*0.5, y: self.size.height*0.5)
            land.size = self.size
            self.addChild(land)}
    
        /// 天井と地面を構築
        func ceiling(){
            // 背景画像のスプライトを配置
            let ceiling = SKSpriteNode(imageNamed: "ceiling")
            ceiling.position = CGPoint(x: self.size.width*0.5, y: self.size.height*0.5)
            ceiling.size = self.size
            self.addChild(ceiling)}
    
    
    
        func setupCeilingAndLand() {
            // 地面画像を読み込み
            let land = SKTexture(imageNamed: "land")
            land.filteringMode = .Nearest
    
            // 必要な画像枚数を算出
            var needNumber = 2.0 + (self.frame.size.width / land.size().width)
    
            // 左に画像一枚分移動アニメーションを作成
            let moveLandAnim = SKAction.moveByX(-land.size().width, y: 0.0, duration:NSTimeInterval(land.size().width / 100.0))
            // 元の位置に戻すアニメーションを作成
            let resetLandAnim = SKAction.moveByX(land.size().width, y: 0.0, duration: 0.0)
            // 移動して元に戻すアニメーションを繰り返すアニメーションを作成
            let repeatForeverLandAnim = SKAction.repeatActionForever(SKAction.sequence([moveLandAnim, resetLandAnim]))
    
            // 画像の配置とアニメーションを設定
            for var i:CGFloat = 0.0; i < needNumber; ++i {
                // SKTextureからSKSpriteNodeを作成
                let sprite = SKSpriteNode(texture: land)
                // 画像の初期位置を設定
                sprite.position = CGPoint(x: i * sprite.size.width, y: sprite.size.height / 2.0)
    
                // 画像に物理シミュレーションを設定
                sprite.physicsBody = SKPhysicsBody(texture: land, size: land.size())
                sprite.physicsBody?.dynamic = false
                sprite.physicsBody?.categoryBitMask = ColliderType.World
                // アニメーションを設定
                sprite.runAction(repeatForeverLandAnim)
                // 親ノードに追加
                baseNode.addChild(sprite)
            }
    
            // 天井画像を読み込み
            let ceiling = SKTexture(imageNamed: "ceiling")
            ceiling.filteringMode = .Nearest
    
            // 必要な画像枚数を算出
            needNumber = 2.0 + self.frame.size.width / ceiling.size().width
    
            // 画像の配置とアニメーションを設定
            for var i:CGFloat = 0.0; i < needNumber; i++ {
                // SKTextureからSKSpriteNodeを作成
                let sprite = SKSpriteNode(texture: ceiling)
                // 画像の初期位置を設定
                sprite.position = CGPoint(x: i * sprite.size.width, y: self.frame.size.height - sprite.size.height / 2.0)
    
                // 画像に物理シミュレーションを設定
                sprite.physicsBody = SKPhysicsBody(texture: ceiling, size: ceiling.size())
                sprite.physicsBody?.dynamic = false
                sprite.physicsBody?.categoryBitMask = ColliderType.World
                // アニメーションを設定
                sprite.runAction(repeatForeverLandAnim)
                // 親ノードに追加
                baseNode.addChild(sprite)
            }
        }
    
        /// プレイヤーを構築
        func setupPlayer() {
            // Playerのパラパラアニメーション作成に必要なSKTextureクラスの配列を定義
            var playerTexture = [SKTexture]()
    
            // パラパラアニメーションに必要な画像を読み込む
            for imageName in Constants.PlayerImages {
                let texture = SKTexture(imageNamed: imageName)
                texture.filteringMode = .Linear
                playerTexture.append(texture)
            }
    
            // キャラクターのアニメーションをパラパラ漫画のように切り替える
            let playerAnimation = SKAction.animateWithTextures(playerTexture, timePerFrame: 0.2)
            // パラパラアニメーションをループさせる
            let loopAnimation = SKAction.repeatActionForever(playerAnimation)
    
            // キャラクターを生成
            player = SKSpriteNode(texture: playerTexture[0])
            // 初期表示位置を設定
            player.position = CGPoint(x: self.frame.size.width * 0.35, y: self.frame.size.height * 0.6)
            // アニメーションを設定
            player.runAction(loopAnimation)
    
            // 物理シミュレーションを設定
            player.physicsBody = SKPhysicsBody(texture: playerTexture[0], size: playerTexture[0].size())
            player.physicsBody?.dynamic = true
            player.physicsBody?.allowsRotation = false
    
            // 自分自身にPlayerカテゴリを設定
            player.physicsBody?.categoryBitMask = ColliderType.Player
            // 衝突判定相手にWorldとCoralを設定
            player.physicsBody?.collisionBitMask = ColliderType.World | ColliderType.Coral
            player.physicsBody?.contactTestBitMask = ColliderType.World | ColliderType.Coral
    
            self.addChild(player)
        }
    
    
    
    
        ///  障害物のサンゴを構築
        func setupCoral() {
            // サンゴ画像を読み込み
            let coralUnder = SKTexture(imageNamed: "coral_under")
            coralUnder.filteringMode = .Linear
            let coralAbove = SKTexture(imageNamed: "coral_above")
            coralAbove.filteringMode = .Linear
    
            // 移動する距離を算出
            let distanceToMove = CGFloat(self.frame.size.width + 2.0 * coralUnder.size().width)
    
            // 画面外まで移動するアニメーションを作成
            let moveAnim = SKAction.moveByX(-distanceToMove, y: 0.0, duration:NSTimeInterval(distanceToMove / 100.0))
            // 自身を取り除くアニメーションを作成
            let removeAnim = SKAction.removeFromParent()
            // 2つのアニメーションを順に実行するアニメーションを作成
            let coralAnim = SKAction.sequence([moveAnim, removeAnim])
    
            // サンゴを生成するメソッドを呼び出すアニメーションを作成
            let newCoralAnim = SKAction.runBlock({
                // サンゴに関するノードを乗せるノードを作成
                let coral = SKNode()
                coral.position = CGPoint(x: self.frame.size.width + coralUnder.size().width * 2, y: 0.0)
                coral.zPosition = -40.0
    
                // 地面から伸びるサンゴのy座標を算出
                let height = UInt32(self.frame.size.width / 12)
                let y = CGFloat(arc4random_uniform(height * 2) + height)
    
    
                // 地面から伸びるサンゴを作成
                let under = SKSpriteNode(texture: coralUnder)
    
                under.position = CGPoint(x: 0.0, y: y)
                under.zRotation = CGFloat(M_PI / 2)
    
                // サンゴに物理シミュレーションを設定
                under.physicsBody = SKPhysicsBody(texture: coralUnder, size: under.size)
                under.physicsBody?.dynamic = false
                under.physicsBody?.categoryBitMask = ColliderType.Coral
                under.physicsBody?.contactTestBitMask = ColliderType.Player
                coral.addChild(under)
    
    
                // スコアをカウントアップするノードを作成
                let scoreNode = SKNode()
                scoreNode.position = CGPoint(x: 0.0, y: y)
    
                // スコアノードに物理シミュレーションを設定
                scoreNode.physicsBody = SKPhysicsBody(rectangleOfSize: CGSize(width: 5, height: self.frame.size.width))
                scoreNode.physicsBody?.dynamic = false
                scoreNode.physicsBody?.categoryBitMask = ColliderType.Score
                scoreNode.physicsBody?.contactTestBitMask = ColliderType.Player
                coral.addChild(scoreNode)
                coral.runAction(coralAnim)
    
                self.coralNode.addChild(coral)
    
    
    
            })
            // 一定間隔待つアニメーションを作成
            let delayAnim = SKAction.waitForDuration(2.5)
            // 上記2つを永遠と繰り返すアニメーションを作成
            let repeatForeverAnim = SKAction.repeatActionForever(SKAction.sequence([newCoralAnim, delayAnim]))
            // この画面で実行
            self.runAction(repeatForeverAnim)
        }
    
        /// スコアラベルを構築
        func setupScoreLabel() {
            // フォント名"Arial Bold"でラベルを作成
            scoreLabelNode = SKLabelNode(fontNamed: "Arial Bold")
            // フォント色を黄色に設定
            scoreLabelNode.fontColor = UIColor.blackColor()
            // 表示位置を設定
            scoreLabelNode.position = CGPoint(x: self.frame.width / 2.0, y: self.frame.size.height * 0.9)
            // 最前面に表示
            scoreLabelNode.zPosition = 100.0
            // スコアを表示
            scoreLabelNode.text = String(score)
    
            self.addChild(scoreLabelNode)
        }
    
        override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
            // ゲーム進行中のとき
            if 0.0 < baseNode.speed {
                for touch in touches {
                    _ = touch.locationInNode(self)
                    // プレイヤーに加えられている力をゼロにする
                    player.physicsBody?.velocity = CGVector.zero
                    // プレイヤーにy軸方向へ力を加える
                    player.physicsBody?.applyImpulse(CGVector(dx: 0.0, dy: 23.0))
                }
            } else if baseNode.speed == 0.0 && player.speed == 0.0 {
                // ゲームオーバー時はリスタート
                // 障害物を全て取り除く
                coralNode.removeAllChildren()
    
                // スコアをリセット
                score = 0
                scoreLabelNode.text = String(score)
    
                // プレイキャラを再配置
                player.position = CGPoint(x: self.frame.size.width * 0.35, y: self.frame.size.height * 0.6)
                player.physicsBody?.velocity = CGVector.zero
                player.physicsBody?.collisionBitMask = ColliderType.World | ColliderType.Coral
                player.zRotation = 0.0
    
                // アニメーションを開始
                player.speed = 1.0
                baseNode.speed = 1.0
            }
        }
    
        override func update(currentTime: CFTimeInterval) {
            /* Called before each frame is rendered */
        }
    
        // MARK: - SKPhysicsContactDelegateプロトコルの実装
        /// 衝突開始時のイベントハンドラ
        func didBeginContact(contact: SKPhysicsContact) {
            // 既にゲームオーバー状態の場合
            if baseNode.speed <= 0.0 {
                return
            }
    
            let rawScoreType = ColliderType.Score
            let rawNoneType = ColliderType.None
    
            if (contact.bodyA.categoryBitMask & rawScoreType) == rawScoreType ||
               (contact.bodyB.categoryBitMask & rawScoreType) == rawScoreType {
                let move = SKAction.moveTo(CGPoint(x: 25.5, y: 100.0) ,duration: 1.5)
    
    
    
    
    
                // スコアを加算しラベルに反映
                score = score + 1
                scoreLabelNode.text = String(score)
    
    
    
                            // スコアラベルをアニメーション
                let scaleUpAnim = SKAction.scaleTo(1.5, duration: 0.1)
                let scaleDownAnim = SKAction.scaleTo(1.0, duration: 0.1)
                scoreLabelNode.runAction(SKAction.sequence([scaleUpAnim, scaleDownAnim]))
    
                // スコアカウントアップに設定されているcontactTestBitMaskを変更
                if (contact.bodyA.categoryBitMask & rawScoreType) == rawScoreType {
                    contact.bodyA.categoryBitMask = ColliderType.None
                    contact.bodyA.contactTestBitMask = ColliderType.None
                } else {
                    contact.bodyB.categoryBitMask = ColliderType.None
                    contact.bodyB.contactTestBitMask = ColliderType.None
                }
            }
        }
    }
    
[/code]


CC BY-SA 3.0
2016-02-27T05:32:35.093
22588
2016-03-29T03:51:26.650
2016-02-27T16:02:25.087
10492
13961
question
ios xcode swift2 spritekit
SpriteKitでプレイヤーが障害物に衝突したときに自動的に画面中央に戻す方法を知りたい


ソースコードを見る限り、衝突判定の基礎的な部分もプレイヤーを移動させる部分も出来ているように思われます。あとは、これらをちゃんと組み合わせて使うだけで良いのではないでしょうか？

・あなたが書かれているソースコードで、衝突判定を行っている部分

[code]

    if (contact.bodyA.categoryBitMask & rawScoreType) == rawScoreType ||
        (contact.bodyB.categoryBitMask & rawScoreType) == rawScoreType {
    
[/code]

これはスコア対象のノードとの当り判定になっているので、同じようなやり方で障害物との当り判定を追加します。

・あなたが書かれているソースコードでプレイヤーを移動させている部分  
（touchesBegan()のなかにありました）

[code]

    // プレイキャラを再配置
    player.position = CGPoint(x: self.frame.size.width * 0.35, y: self.frame.size.height * 0.6)
    player.physicsBody?.velocity = CGVector.zero
    player.physicsBody?.collisionBitMask = ColliderType.World | ColliderType.Coral
    player.zRotation = 0.0
    
[/code]

障害物と当たったときの処理で、この部分と同じように処理を行えば、プレイヤーを中央に戻す事が出来ると思われます。


CC BY-SA 3.0
2016-02-28T02:52:56.513
22612
2016-02-28T02:52:56.513
7459
22588
answer