次はプレイヤーを動かす処理を実装しようと思います。「GameScene.swift」を以下のように修正してください。
class GameScene: SKScene { let player = SKShapeNode(circleOfRadius: 10) override func didMove(to view: SKView) { player.fillColor = UIColor(red: 0.93, green: 0.96, blue: 0.00, alpha: 1.0) addChild(player) } // ここから今回追加分 override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { touches.forEach { let point = $0.location(in: self) player.removeAllActions() let path = CGMutablePath() path.move(to: CGPoint()) path.addLine(to: CGPoint(x: point.x - player.position.x, y: point.y - player.position.y)) player.run(SKAction.follow(path, speed: 50.0)) } } // ここまで今回追加分 }
これでタッチした位置に向かってプレイヤーが移動するようになりました。
プレイヤーの移動は「SKAction」というクラスを使いました。SKActionはSpriteKitの機能の1つで、SKNodeをアニメーションさせることができるクラスです。今回は移動に利用しましたが、オブジェクトの回転や色の段階的変化にも使えます。
次は、鬼の出現と移動の部分を実装します。鬼は5秒に一度、画面の右端から出現する仕様にします。後半になるに従って鬼の数が増えて難易度が上がっていきます。
今回は移動処理をSKActionで実装します。「GameScene.swift」を下のように修正してください。
class GameScene: SKScene { let player = SKShapeNode(circleOfRadius: 10) // ここから今回追加分 var enemies = [SKShapeNode]() var timer: Timer? var prevTime: TimeInterval = 0 // ここまで今回追加分 override func didMove(to view: SKView) { player.fillColor = UIColor(red: 0.93, green: 0.96, blue: 0.00, alpha: 1.0) addChild(player) // ここから今回追加分 setCreateEnemyTimer() physicsWorld.gravity = CGVector() // これがないと鬼が下に落下してしまう // ここまで今回追加分 } // 省略 // ここから今回追加分 func setCreateEnemyTimer() { timer?.invalidate() // 5秒に一度、createEnemyを呼び出す処理 timer = Timer.scheduledTimer(timeInterval: 5.0, target: self, selector: #selector(GameScene.createEnemy), userInfo: nil, repeats: true) timer?.fire() } func createEnemy() { let enemy = SKShapeNode(circleOfRadius: 10) enemy.position.x = size.width / 2 enemy.fillColor = UIColor(red: 0.94, green: 0.14, blue: 0.08, alpha: 1.0) enemy.physicsBody = SKPhysicsBody(circleOfRadius: enemy.frame.width / 2) addChild(enemy) enemies.append(enemy) } override func update(_ currentTime: TimeInterval) { if prevTime == 0 { prevTime = currentTime } // プレイヤーの位置が変わるので、1秒に一度移動方向を調整する if Int(currentTime) != Int(prevTime) { enemies.forEach { $0.removeAllActions() let path = CGMutablePath() path.move(to: CGPoint()) path.addLine(to: CGPoint(x: player.position.x - $0.position.x, y: player.position.y - $0.position.y)) $0.run(SKAction.follow(path, speed: 50.0)) } } prevTime = currentTime } // ここまで今回追加分 }
アプリを起動をすると、敵がプレイヤーに向かっていくのが分かります。
次は鬼に捕まったときの処理を実装します。鬼とプレイヤーが衝突した時点でゲーム終了となり、何秒逃げたかを画面上に表示するようにします。
「GameScene.swift」を下のように修正してください。
class GameScene: SKScene { let player = SKShapeNode(circleOfRadius: 10) var enemies = [SKShapeNode]() var timer: Timer? var prevTime: TimeInterval = 0 // ここから今回追加分 var startTime: TimeInterval = 0 var isGameFinished = false // ここまで今回追加分 // 省略 override func update(_ currentTime: TimeInterval) { if prevTime == 0 { prevTime = currentTime startTime = currentTime // 今回追加分 } if Int(currentTime) != Int(prevTime) { enemies.forEach { $0.removeAllActions() let path = CGMutablePath() path.move(to: CGPoint()) path.addLine(to: CGPoint(x: player.position.x - $0.position.x, y: player.position.y - $0.position.y)) $0.run(SKAction.follow(path, speed: 50.0)) } } // ここから今回追加分 if !isGameFinished { for enemy in enemies { let dx = enemy.position.x - player.position.x let dy = enemy.position.y - player.position.y if sqrt(dx*dx + dy*dy) < player.frame.width / 2 + enemy.frame.width / 2 { isGameFinished = true timer?.invalidate() let label = SKLabelNode(text: "記録:\(Int(currentTime - startTime))秒") label.fontSize = 80 label.position = CGPoint(x: 0, y: -100) addChild(label) break } } } // ここまで今回追加分 prevTime = currentTime } }
鬼に捕まったときに記録が表示されるようになりました。
今回はプレイヤーと鬼の衝突判定を以下のように実装しました。
let dx = enemy.position.x - player.position.x let dy = enemy.position.y - player.position.y if sqrt(dx*dx + dy*dy) < player.frame.width / 2 + enemy.frame.width / 2 { // 省略 }
実は衝突判定はSpriteKitでも用意されています。「SKPhysicsBody」というクラスを使うと衝突時の挙動設定や衝突時のコールバックメソッドの設定ができます。しかし衝突相手の設定などが少しややこしいので、今回は上記のように地道に計算する形にしました。
Copyright © ITmedia, Inc. All Rights Reserved.