はじめに
SpriteKit を使って 2D 横スクロールゲームをつくってみます。なぜ Unity を使わないんだ?と疑問を持つ方もいるかもしれませんがとくに意味はありません。なんか面白そうなので SpriteKit を使ってみます。
記事は3つくらいになる予定です(たぶん)。
今回は歩いたり跳ねたりするプレイヤーを実装していきます。
素材準備
クリスマスも近いのでチキンをプレイヤーにします
歩き用
c1 | c2 |
---|---|
ジャンプ用
j1 | j2 |
---|---|
実装
View の表示については下記をご参考ください。本記事でやるのは基本的に SKScene の実装のみです。
表示
とりあえず画面にプレイヤーを表示します。
final class GameScene: SKScene {
private let player = SKSpriteNode(texture: .init(imageNamed: "c1"), size: .init(width: 32, height: 32))
override func didMove(to view: SKView) {
backgroundColor = .init(red: 51/255, green: 51/255, blue: 51/255, alpha: 1.0)
let floor = SKShapeNode(rectOf: .init(width: 1000, height: 10))
floor.name = "floor"
floor.position = .init(x: frame.midX, y: 0)
floor.fillColor = .white
floor.physicsBody = .init(rectangleOf: .init(width: 1000, height: 10))
floor.physicsBody?.isDynamic = false
addChild(floor)
player.position = .init(x: frame.midX, y: frame.midY)
player.physicsBody = .init(texture: player.texture!, size: player.size)
// ころがらないように設定
player.physicsBody?.allowsRotation = false
addChild(player)
}
}
こんな感じです。
歩行
歩行はボタンを押し続けている限り動作を継続したいので状態を保持して状態判定でプレイヤーを移動させるようにしました(都度アニメーションをつけるとカクカクしたので repeatForever
にしてます)。
// プレイヤーの状態
enum PlayerStatus {
/// 走行中
case running
/// ジャンプ準備中
case prepareJump
/// ジャンプ中
case jumping
/// 停止中
case pause
}
final class GameScene: SKScene {
// 追加
private var status = PlayerStatus.pause
private let runTextures: [SKTexture] = [
.init(imageNamed: "c2"),
.init(imageNamed: "c1"),
]
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
status = .running
// アニメーション開始
let action = SKAction.animate(with: runTextures, timePerFrame: 0.2)
player.run(.repeatForever(action), withKey: "running")
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
// アニメーション停止
player.removeAction(forKey: "running")
status = .pause
}
override func update(_ currentTime: TimeInterval) {
switch status {
case .running:
// プレイヤーを移動させる
player.physicsBody?.velocity = CGVector(dx: 120, dy: 0)
default:
break
}
}
}
こんな感じです。
ジャンプ
2段ジャンプを防止しようと思ったらわりとややこしくなりました。
final class GameScene: SKScene {
// 追加
private let jumpTextures: [SKTexture] = [
.init(imageNamed: "j1"),
.init(imageNamed: "j2"),
]
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if status == .jumping || status == .prepareJump {
return
}
status = .prepareJump
et action = SKAction.group([
.animate(with: jumpTextures, timePerFrame: 0.2),
.sequence([
.wait(forDuration: 0.2),
.applyImpulse(.init(dx: 2, dy: 13), duration: 0.2)
])
])
player.run(action) {
self.player.run(.setTexture(self.runTextures[1]))
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
self.status = .jumping
}
}
override func update(_ currentTime: TimeInterval) {
switch status {
case .jumping:
if player.physicsBody?.velocity.dy == 0 {
status = .pause
}
default:
break
}
}
}
こんな感じです。
おわりに
てきとーにボタン配置するとこんな感じになります。
次回はステージを作ります!
SpriteKit あまりさわったことないのでもっといい方法あればぜひ教えて下さい