5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

iOSAdvent Calendar 2022

Day 9

SpriteKitで2D横スクロールゲームをつくろう(その5)

Last updated at Posted at 2022-12-08

Xcode-14.1 iOS-16.0

はじめに

前回の続きです。

今回はついにゴール作成です:sunglasses:

実装

ゴール判定は迷ったのですが指定のブロックに接触したらゴールしたことにします。

横に 50 個ブロックをならべているので最後の3つのブロックを判定に使います。

クリア画面作成

クリア画面を作成します。

final class ClearScene: SKScene {

    private let jumpTextures: [SKTexture] = [
        .init(imageNamed: "j1"),
        .init(imageNamed: "j2"),
    ]

    override func didMove(to view: SKView) {
        backgroundColor = .init(red: 51/255, green: 51/255, blue: 51/255, alpha: 1.0)

        let label = SKLabelNode(text: "Clear")
        label.fontName = "PixelMplus10-Bold"
        label.fontColor = .white
        label.position = .init(x: frame.midX, y: frame.midY)
        addChild(label)

        let floor = SKShapeNode(rectOf: .init(width: 1000, height: 10))
        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)

        let player = SKSpriteNode(texture: .init(imageNamed: "c1"), size: .init(width: 32, height: 32))
        player.position = .init(x: frame.midX, y: 20)
        player.physicsBody = .init(texture: player.texture!, size: player.size)
        player.physicsBody?.allowsRotation = false
        addChild(player)

        let action = SKAction.group([
            .animate(with: jumpTextures, timePerFrame: 0.2),
            .sequence([
                .wait(forDuration: 0.2),
                .applyImpulse(.init(dx: 0, dy: 13), duration: 0.2)
            ])
        ])
        player.run(.repeatForever(.sequence([
            action,
            .wait(forDuration: 0.5)
        ])))

        if let snow = SKEmitterNode(fileNamed: "Snow") {
            snow.position = .init(x: size.width/2, y: size.height)
            addChild(snow)
        }
    }
}

こんな感じです。

clear

ゴール判定

GameScenedidMove のタイルの physicsBody 設定処理を下記のように修正します。

tilePositions.forEach {
    let node = SKSpriteNode()
    node.name = "floor"
    node.position = .init(x: CGFloat($0.column) * tileSize.width + tileSize.width/2,
                          y: CGFloat($0.row) * tileSize.height + tileSize.height/2)
    node.physicsBody = .init(rectangleOf: tileSize)
    node.physicsBody?.isDynamic = false
    // ここ追加
    if $0.column > tilePositions.last!.column - 3 {
        node.physicsBody?.categoryBitMask = 0b0011
        node.name = "goal"
    } else {
        node.physicsBody?.categoryBitMask = 0b0100
    }
    tileMap.addChild(node)
}

あとは SKPhysicsContactDelegate の衝突判定で下記の遷移処理を追加すれば完成です。

if nodeA.name == "goal" || nodeB.name == "goal" {
    view?.presentScene(ClearScene(size: size), transition: .fade(withDuration: 2.0))
}

微調整

敵の数など微調整していきます。

敵が1体はさみしいので3体表示。

let enemyPositions: [CGFloat] = [400, 800, 950]
enemyPositions.forEach { x in
    let enemy = makeEnemy()
    enemy.position = .init(x: x, y: frame.midY)
    addChild(enemy)
    enemy.run(makeEnemyAction())
}

ブロック数修正。

private let tilePositions: [(column: Int, row: Int)] = [
        (0, 0), (0, 1),
        (1, 0), (1, 1),
        (2, 0), (2, 1),
        (3, 0), (3, 1),
        (4, 0), (4, 1),
        (5, 0), (5, 1),
        (6, 0), (6, 1),
        (7, 0), (7, 1),
        (8, 0), (8, 1),
        (9, 0), (9, 1),
        (10, 0), (10, 1),
        (11, 0), (11, 1),
        (12, 0), (12, 1),
        (13, 0), (13, 1), (13, 2),
        (14, 0), (14, 1), (14, 2), (14, 3),
        (15, 0), (15, 1),
        (16, 0), (16, 1),
        (17, 0), (17, 1),
        (18, 0), (18, 1),
        (19, 0), (19, 1),
        (20, 0), (20, 1),
        (21, 0), (21, 1),
        (22, 0), (22, 1),
        (23, 0), (23, 1),
        (24, 0), (24, 1),
        (25, 0), (25, 1),
        (26, 0), (26, 1),
        (27, 0), (27, 1),
        (28, 0), (28, 1),
        (29, 0), (29, 1),
        (30, 0), (30, 1),
        (31, 0), (31, 1),
        (32, 0), (32, 1),
        (33, 0), (33, 1),
        (34, 0), (34, 1),
        (35, 0), (35, 1),
        (36, 0), (36, 1),
        (37, 0), (37, 1),
        (38, 0), (38, 1),
        (39, 0), (39, 1),
        (40, 0), (40, 1),
        (41, 0), (41, 1),
        (42, 0), (42, 1),
        (43, 0), (43, 1), (43, 2),
        (44, 0), (44, 1), (44, 2), (44, 3),
        (45, 0), (45, 1), (45, 2), (45, 3), (45, 4),
        (46, 0), (46, 1), (46, 2), (46, 3), (46, 4), (46, 5),
        (47, 0), (47, 1),
        (48, 0), (48, 1),
        (49, 0), (49, 1),
    ]

// ここのrowsも修正
let tileMap = SKTileMapNode(tileSet: tileSet, columns: tilePositions.last!.column + 1, rows: 6, tileSize: tileSize)

プレイヤーの開始位置修正。

player.position = .init(x: frame.midX, y: frame.midY)

完成品はこんな感じです。

おわりに

これで SpriteKit を使った 2D 横スクロールゲームは完成です:confetti_ball:

あとは SE とか付けるともっとそれっぽくなると思います。SKAudioNode 使うとできそう。SKNode はその他にも色々あるので下記記事を参考にどうぞ。

SKNodeまとめ(Swift)

SpriteKit あまりさわったことないのでもっといい方法あればぜひ教えて下さい:bow:

まとめ

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?