20200810のSwiftに関する記事は3件です。

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

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座標(14*8, 18*8)に 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のサイズを (28*8, 36*8)ドットに変更している。

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)
    }
}

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】オプショナル束縛構文の使い方

1.はじめに


前回オプショナル型の基本についての記事を書きましたが、今回はオプショナル型を実装するうえで、コードを読みやすく、スマートに書くことができる、オプショナル束縛構文について解説しようと思います。
前回の記事はこちらです。
(https://qiita.com/0901_yasyun/items/d64986bdfe917d90382c)

2.オプショナル束縛構文とは


まず以下のif文の条件を見てください。

let year : Int? = Int("2000")   
if let y = year {       //ここのif文の書き方に注目
    print("ハレー彗星は\(y + 61)年に来る")
}else{
    print("エラー")
}

この条件の書き方は、if文とwhile文の条件部にのみ記述することができる、特別な構文です。これをオプショナル構文、またはif-let文と呼びます。
このコードだと、もし変数yearがnilではない値を持っていたら、if文の条件判定は真となります。

3.コードを書く上でのメリット


通常通りであれば、まずオプショナル型の式の値がnilなのかどうかをチェックし、その後にまったくの別で、その値を使用したコードを書いていくことになります。

しかしこの書き方を使うことで、オプショナル型の式の値がnilではなかった場合、その値をチェックしたif文のthen節で、そのまま使うことができます。

4.オプショナル束縛構文の使い方


基本的な構文の使い方は、2で示したコードの通りです。
気を付けることとしては、2のコードにおいてyearの値がnilであれば、if文の条件は偽でelse節が実行されますが、その場合定数yはelse節では使うことができません。

オプショナル束縛構文の条件が成立した場合、条件部で定義された変数などはthen節でしか参照することはできません。これを利用してこの変数などに、元のオプショナル型の変数などと同じ名前を使うことができます。以下のコードを見てください。

let halley : Int? = Int("2000") //定数halleyはInt?型
if var halley = halley {        //変数halleyはInt型であり、then節内でのみ有効
    print("ハレー彗星は\(halley)年に来た")
    halley += 61
    print("次は\(halley)年だと予想される")
}

なお、先ほども述べましたが、オプショナル束縛はif文やwhile文の条件部に記述したときのみ、特別な意味を持つので注意してください。

5.オプショナル束縛と条件式


複数のオプショナル型の値を使って処理を行いたい場合、if-let文をネストすることもできますが、カンマで区切って複数の条件を並べることができます。以下のコードを見てください。

if let sapporo = Int("1972"), let nagano = Int("1998") {
    print("\(nagano - sapporo) years.")
}

オプショナル型の値だけでなく、その他の条件もカンマで区切って記述することができます。

var nagano = 1998
if nagano < 2000, let tokyo = Int("2020"), tokyo > nagano {
    print("\(tokyo - nagano)")     //22と出力される
}

条件を複数書いた場合、このコードだとnaganoが2000未満の場合のみ、カンマの右側のオプショナル束縛の評価に進みます。オプショナル式がnilではなかった場合、定数tokyoにInt型の値が代入され、さらに右の条件式が評価されます。

このように、一番左側から条件の評価が始まり、条件式が偽になるか、オプショナル式がnilだった時点で評価は終了し、ifの条件は偽となります。
また、左側で値が代入された定数や変数を、右側の式で使うこともできます。

おわりに


今回はコードをきれいに書く上で必須ともいえる、オプショナル束縛構文の使い方について解説しました。コードの可読性を高めることは、自分の書いたコードを世の中に発信する際にとても重要なことなので、ぜひこの書き方を利用してみてください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Udemyのswiftコース(英語のやつ)でiOSアプリ開発を学ぶ その4

勉強するコースはこれです:point_down:
https://www.udemy.com/course/ios-13-app-development-bootcamp/

進捗

セクション4の途中まで終了(全部で36セクション)(ひとまずベイビーステップですよね)
サイコロのアプリを作りつつ、その中で変数(variables)と配列(arrays)について学習した。

感想

配列や変数の基礎については知っているつもりだけど、良いおさらいでした:man_tone1:
今まで自分がしてきた学習は日本語での本や授業の内容だったので、英語で学ぶとまた新鮮な部分がありますね。
「英語だとこう言うのか!」とか「英語だったらこういう風に解説すればいいのか!」とか目から鱗な内容もありました。
すでにswiftに詳しい人でも受講すると英語の勉強になっていいかも。
また、英単語や英語での表現に詳しければ英語で内容を調べやすいので、開発中に分からないところでつまづいたときにこの学習経験が役に立ちそうな気がする。

ジョークは、Angela先生から昇給(a raise)と配列(arrays)をかけたジョークがnice jokeだった!
section 4で出てくるので、このコース始めた人はぜひチェックしてほしい!
笑点だったら座布団1枚もらえるやつでした:thumbsup:

今後の予定

できる範囲でコースを進める。
実際のアプリ開発よりも学習することのほうに時間をかけるかも。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む