SpriteKitで2D横スクロールゲームをつくろう（その２）

![Xcode-14.1](https://img.shields.io/badge/Xcode-14.1-brightgreen) ![iOS-16.0](https://img.shields.io/badge/iOS-16.0-brightgreen) 

## はじめに
[前回](https://qiita.com/am10/private/df42c1f0091e6c4705e2)の続きです。

今回はステージを作っていきます。

## 素材準備
背景（ちょっと適切なサイズがわからない。。。）

![background](https://user-images.githubusercontent.com/34936885/205078912-82c2dec2-d2c3-4c57-a23c-2fb45f3add58.jpg)

床ブロック

![tile_block](https://user-images.githubusercontent.com/34936885/205078848-2c4bdc61-dbc9-45f1-b961-48d475d72677.jpg)

## 実装
今回も SKScene の実装のみです。今回は全部コードで実装してますが SpriteKit Scene などのファイルを追加すれば GUI でも操作可能です。

<img width="300" alt="sprite_files" src="https://user-images.githubusercontent.com/34936885/205233749-1ae010ee-0d5f-4d25-8c43-7f7e1b21fe5c.png">

<img width="500" alt="editor" src="https://user-images.githubusercontent.com/34936885/205233766-a5677380-f4dd-4a4d-b82e-82f27bb15e76.png">

### 背景表示
背景を表示します。

```swift
final class GameScene: SKScene {

    override func didMove(to view: SKView) {
        let backNode = SKSpriteNode(imageNamed: "background")
        backNode.size = frame.size
        backNode.position = .init(x: frame.midX, y: frame.midY)
        addChild(backNode)
    }
}
```

こんな感じです。

<img width="300" alt="background" src="https://user-images.githubusercontent.com/34936885/205079004-7cc5ec24-e021-4512-81fb-26a153ac6cb8.png">

### 床表示
`SKTileMapNode` を使って床を作ります。

```swift
final class GameScene: SKScene {

    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), (6, 2),
        (7, 0), (7, 1), (7, 2), (7, 3),
        (8, 0), (8, 1),
        (9, 0), (9, 1),
        (10, 0), (10, 1),
        (11, 0), (11, 1),
        (12, 0), (12, 1),
        (13, 0), (13, 1),
        (14, 0), (14, 1),
        (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),
        (44, 0), (44, 1),
        (45, 0), (45, 1),
        (46, 0), (46, 1),
        (47, 0), (47, 1),
        (48, 0), (48, 1),
        (49, 0), (49, 1),
    ]

    override func didMove(to view: SKView) {
        let tileSize = CGSize(width: 32, height: 32)
        let group = SKTileGroup(tileDefinition: .init(texture: .init(imageNamed: "tile_block"), size: tileSize))
        let tileSet = SKTileSet(tileGroups: [group])
        let tileMap = SKTileMapNode(tileSet: tileSet, columns: tilePositions.last!.column + 1, rows: 4, tileSize: tileSize)
        tilePositions.forEach {
            tileMap.setTileGroup(group, forColumn: $0.column, row: $0.row)
        }
        tileMap.position = .zero
        tileMap.anchorPoint = .zero
        addChild(tileMap)

        // physicsBody設定
        tilePositions.forEach {
            let node = SKSpriteNode()
            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
            tileMap.addChild(node)
        }
    }
}
```

こんな感じです。

<img width="300" alt="floor" src="https://user-images.githubusercontent.com/34936885/205079126-1170bd18-0cb1-4f1a-842b-cefb76cc6eb5.png">

ちょっと解説。`tilePositions` がタイルの位置を表していて（0, 0）が左下のタイルになります。下記で設定する `columns` と `rows` は横と縦の最大値になります。今回は横に 50, 縦に（最大で）4 並べるのでその数値をそれぞれ設定しています。

```swift
let tileMap = SKTileMapNode(tileSet: tileSet, columns: tilePositions.last!.column + 1, rows: 4, tileSize: tileSize)
```

下記の設定は床を画面の左下起点に置きたかったのでそれぞれ `.zero` を設定しています。

```swift
tileMap.position = .zero
tileMap.anchorPoint = .zero
```

下記の処理は `SKTileMapNode` にいい感じに `physicsBody` を設定する方法がない（たぶん）のでタイル１つ１つに `physicsBody` 設定しています。

```swift
tilePositions.forEach {
    let node = SKSpriteNode()
    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
    tileMap.addChild(node)
}
```

## おわりに
前回のと合わせるとこんな感じになります。

![game](https://user-images.githubusercontent.com/34936885/205079386-f17f40f8-5aa1-41fb-aad3-246892e908fa.gif)

偶然にもどこかでみたことのあるようなステージになってしまいました:open_mouth:

次回は横スクロールできるようにします！

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

## まとめ
* [その１](https://qiita.com/am10/items/df42c1f0091e6c4705e2)
* [その２](https://qiita.com/am10/items/d53d1cdf1b973a9f56cc)
* [その３](https://qiita.com/am10/items/8729f77f3ee512a86052)
* [その４](https://qiita.com/am10/items/90e3a2f58ae88e4ad6c4)
* [その５](https://qiita.com/am10/items/deacceda00c034ac29bb)