次はプレイヤーを動かす処理を実装しようと思います。「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」というクラスを使うと衝突時の挙動設定や衝突時のコールバックメソッドの設定ができます。しかし衝突相手の設定などが少しややこしいので、今回は上記のように地道に計算する形にしました。
iOS 9の最新機能で自動ルート検索を簡単にゲームに組み込む
iOS 10 SDKの新機能SiriKit、音声認識、iMessage拡張を自作アプリに生かすには
スマホ世代でも分かるMacの基本的な使い方&Xcodeをインストールする手順Copyright © ITmedia, Inc. All Rights Reserved.