2
4

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 3 years have passed since last update.

【入門】iOS アプリ開発 #2【SpriteKit を使う】

Last updated at Posted at 2020-08-10

SpriteKit

iOS向けの2Dゲーム開発用として、SpriteKit というフレームワークが提供されている。

今回、古典的なピクセルアートのゲーム「パックマン」を作るため、スプライトのキャラクタをもっと簡単に扱えるようにしたい。

キャラクタは基本 16x16ドットで構成されており、テクスチャの切り替えアニメーションも考慮すると、オブジェクトで管理するのは大変そう。

そのため1つの大きなアセット画像を 16x16ドットで切り出して、テクスチャ番号で扱えるようにする。

スプライトもオブジェクト管理ではなく、スプライト管理番号で扱えるようなクラスを作成する。

Sprite Manager class の作成

スプライトを表示する API はシンプルに、

draw(0, x:100, y:50, texture:1)

とする。

また一定間隔(0.1s)でアニメーションする API は、

startAnimation(0, sequence: [1,2,3], timePerFrame: 0.1, repeat: true)

という感じにする。

このような API を持つ、次の CgAssetManager クラスを継承する CgSpriteManager クラスを作成する。

Asset Manager class の作成

テストで使用するアセット画像は下記で、これを 16x16ドットで切り出して、テクスチャ番号を割り当てる CgAssetManager クラスを作成する。

[spriteTest.png]

テクスチャ番号は左下から右上へ、下記のように割り当てる。

4 5 6 7
0 1 2 3

スプライト描画のプログラム

// Create a sprite manager object.
let sprite = CgSpriteManager(view: self, imageNamed: "spriteTest.png", width: 16, height: 16, maxNumber: 64)
        
// Draw a #0 sprite with #1 texture at (8,8) position.
sprite.draw(0, x: 8, y: 8, texture: 1)

CgSpriteManagerクラスは、"spriteTest.png" のアセット画像から 16x16ドットでテクスチャを切り出し、同時に最大64個のスプライトを描画するための管理番号を持つ SpriteManager オブジェクトを生成する。

SpriteManagerオブジェクトの draw API で、スプライト管理番号0とし、ピクセル座標(8, 8)にテクスチャ番号1のスプライトを描画する。

Background Manager class の作成

背景となるタイル描画も必要で、8x8ドットのピクセル文字も再現したい。

これらも同様に、大きな背景アセット画像から 8x8ドットのテクスチャを切り出し、テクスチャ番号で扱えるようにする。

背景のタイル描画 API は、

put(0, column: 2, row: 3, texture: 1)

とする。

また、ピクセル文字を表示する API は、

putString(0, column: 2, row: 4, string: "TEST")

という感じにする。

テストで使用する1つのアセット画像は下記で、SpriteManagerと同様に、8x8ドットに切り出してテクスチャ番号を割り当てる CgAssetManager クラスを継承して、CgBackgroundManager クラスを作成する。

[backgroundTest.png]

背景描画のプログラム

// Create a background manager object.
let background = CgBackgroundManager(view: self, imageNamed: "backgroundTest.png", width: 8, height: 8, maxNumber: 2)

// Draw a #0 background of (28x36) size at (14*8,18*8) position.
background.draw(0, x: 14*8, y: 18*8, columnsInWidth: 28, rowsInHeight: 36)

// Put a #1 texture on #0 background at (14,19).
background.put(0, column: 14, row: 19, texture: 1)

// Print text on #0 background at (8,18).
background.putString(0, column: 8, row: 18, string: "SPRITEKIT TEST", offset: -16*2 /* ASCII offset */)

CgBackgroundManagerクラスは、"backgroundTest.png" のアセット画像から 8x8ドットのテクスチャを切り出し、同時に最大2面の背景を描画するための管理番号を持つ BackgroundManager オブジェクトを生成する。

BackgroundManagerオブジェクトの draw API で、背景管理番号0座標(148, 188)に 28x36 のタイル面を生成・描画する。1つのタイルサイズは、テクスチャサイズの 8x8ドットとなる。

put API は、背景管理番号 0 のタイル位置(14, 19)にテクスチャ番号 1 のタイルを描画する。

putString API では、背景管理番号 0 のタイル位置(8, 18)に "SPRITEKIT TEST" のタイルを描画する。先頭文字 "S" の ASCIIコードは 83 となり、offset: -16*2 を引いた 51番のテクスチャを描画する仕組みとなる。

#テスト・プログラム

GitHub に公開しているテスト・プログラムを実行すると、以下のような画面が表示され、赤モンスターがアニメーションしながら移動する。

Asset/Sprite/Background Manager クラスは、SpritekitManager.swift ファイルにコーディングしている。コメント入れて 500行未満。

GameViewController.swift では、GameSceneのサイズを (288, 368)ドットに変更している。

class GameViewController: UIViewController {

    private var scene: SKScene!
    
    override func viewDidLoad() {
       super.viewDidLoad()
       
       if let view = self.view as! SKView? {
           
           let size = CGSize(width: 28*8, height: 36*8)
           scene = GameScene(size: size)
           
           // Set background color to black
           scene.backgroundColor = UIColor.black

           // Set the scale mode to scale to fit the window
           scene.scaleMode = .aspectFit
           
           // Present the scene
           view.presentScene(scene)
           
           view.ignoresSiblingOrder = true
           
           view.showsFPS = true
           view.showsNodeCount = true
       }
    }

GameScene.swift の didMove関数で Managerオブジェクトの生成と描画、update関数で赤モンスターを移動させている。

class GameScene: SKScene {
    
    private var sprite: CgSpriteManager!
    private var background: CgCustomBackground!
    
    override func didMove(to view: SKView) {

        // Create sprite and background objects.
        sprite = CgSpriteManager(view: self, imageNamed: "spriteTest.png", width: 16, height: 16, maxNumber: 64)
        background = CgCustomBackground(view: self, imageNamed: "backgroundTest.png", width: 8, height: 8, maxNumber: 2)

        // Draw cherries.
        sprite.draw(0, x: 8, y: 8, texture: 3)
        sprite.draw(1, x: 16*13+8, y: 8, texture: 3)
        sprite.draw(2, x: 8, y: 16*17+8, texture: 3)
        sprite.draw(3, x: 16*13+8, y: 16*17+8, texture: 3)

        // Draw and animate a Pacman.
        sprite.setPosition(4, x: 13*8+8, y: 16*12)
        sprite.setRotation(4, radians: CGFloat(90.0 * .pi / 180.0))
        sprite.startAnimation(4, sequence: [0,1,2], timePerFrame: 0.1, repeat: true)
        
        // Draw grids on #0 background.
        background.draw(0, x: 14*8, y: 18*8, columnsInWidth: 28, rowsInHeight: 36)
        background.setDepth(0, zPosition: 0)

        for y in 0 ..< 18  {
            for x in 0 ..< 14  {
                background.put(0, column: x*2, row: y*2, columnsInwidth: 2, rowsInHeight: 2, textures: [4,5,6,7])
            }
        }

        // Print text on #1 background.
        background.draw(1, x: 14*8, y: 18*8, columnsInWidth: 28, rowsInHeight: 36)
        background.setDepth(1, zPosition: 1)

        let asciiOffset = -16*2
        background.putString(1, column: 8, row: 18, string: "SPRITEKIT TEST", offset: asciiOffset)
 
        // Put a #63 texture on #1 background.
        background.put(1, column: 14, row: 19, texture: 128)
    }
    
    private var x: CGFloat = 0
    private var dx: CGFloat = 0

    override func update(_ currentTime: TimeInterval) {
        // Called before each frame is rendered

        // Move and animate a Ghost.
        if x == 0 {
            dx = 1
            sprite.startAnimation(5, sequence: [4,5], timePerFrame: 0.1, repeat: true)
        } else if x == 28*8 {
            dx = -1
            sprite.startAnimation(5, sequence: [6,7], timePerFrame: 0.1, repeat: true)
        }
        
        x += dx
        sprite.setPosition(5, x: x, y: 16*6)
    }
}

#参考

#次の記事
[【入門】iOS アプリ開発 #3【Sound を再生する】]
(https://qiita.com/KIKU_CHU/items/01a45a3d4698058bd512)

2
4
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
2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?