工場裏のアーカイブス

素人によるiPhoneアプリ開発の学習記 あと機械学習とかM5Stackとか

SpriteKit Sceneとスケールモード

現在のXcode(本記事執筆時点:Version 11.3)では、昔作成した記事の時代と比べてSpriteKitのテンプレートの中身が大きく変わっており、SpriteKit Sceneファイル(以下SKSと表記)を用いて、まるでStoryboardのように様々なノードをシーン上にマウスで配置出来るようになったようです。

もちろん昔のように、SKSを用いずに全てコードだけでSpriteKitを扱うことも出来るようですが(こちらのサイトが非常に参考になります)、SKSは便利そうなので今後は使い方を学習し、またSpriteKitを活用したアプリを作成してみたいと考えています。

しかしその上で、現状ではiPhoneだけでも8から11 Pro Maxまで様々な機種が現役であり、もちろんiPadもありますので、サイズどころかアスペクト比まで異なるそれぞれの機種の画面において、SpriteKitのシーンの見え方を制御するために、改めてスケールモードの設定について確認しておきたいと思いました。

そこで本記事では、まずはSpriteKitのテンプレートに自前のSKSを追加してみます。そのSKSではシーンのサイズを1242 x 2208(iPhone 8 Plusのピクセルと同じ)として、同じサイズの背景画像をスプライトノードとして配置します。そしてスケールモードの設定によって、シーンの見え方が下表の機種でどのように変わってくるのかをシミュレーター上で実験してみます。

機種名 ポイント ピクセル アスペクト比
iPhone 8 Plus 414 x 736 1242 x 2208 9 : 16
iPhone 11 Pro Max 414 x 896 1242 x 2688 9 : 19.5
iPad Pro 12.9 1024 x 1366 2048 x 2732 3 : 4

 

SpriteKitのテンプレートに自前のSKSを追加

こちらは単なる手順のメモとなりますので、サクッとまとめます。

  • まずはXcodeの新規プロジェクト作成で「Game」から、SpriteKitのテンプレートプロジェクトを作成します。

f:id:fleron:20200104013736p:plain
f:id:fleron:20200104013740p:plain
 

  • プロジェクトが開いたら、デフォルトで存在する「GameScene.sks」「Actions.sks」「GameScene.swift」のファイル3つは不要なので削除します。

f:id:fleron:20200104013539p:plain
 

  • プロジェクトに自前のSKSを追加します。ファイルの追加画面で「SpriteKit Scene」を選択します。ファイル名は何でも良いですが、ここではデフォルトの「MyScene.sks」とします。

f:id:fleron:20200104014304p:plain
 

  • 続いてプロジェクトに、SKSceneのサブクラスとしてクラスファイルを追加します。こちらをSKSと紐付けますので、同じ名前にするのが良いと思います(ここでは「MyScene.swift」)。

f:id:fleron:20200104014619p:plain
 

  • クラスファイルが追加出来たら、中身を以下のように書き換えます。今回は後にSKS上でシーン上にスプライトノードを配置して、それを表示するだけですので、特にここでは何の処理も行いません。
import UIKit
import SpriteKit

class MyScene: SKScene {
    override func didMove(to view: SKView) {

    }
}

 

  • 続いて「MyScene.sks」のインスペクタで、以下のようにクラスファイル名を入力して紐付けをします。

f:id:fleron:20200104015821p:plain
 

  • ついでに「Color」でシーンの背景色も変えてみます(ここでは青色)。これは特に必要というわけではありませんが、後にシミュレーターを起動してテストをしてみた際に、自前のSKSからシーンが表示されていることを分かりやすくするためです。

f:id:fleron:20200104020535p:plain
 

  • 最後に、デフォルトで存在する「GameViewController.swift」を開いて、以下のようにシーンを生成する部分のファイル名を書き換えます。
if let scene = SKScene(fileNamed: "MyScene") {     //シーンのファイル名を“GameScene”から書き換え
        // Set the scale mode to scale to fit the window
        scene.scaleMode = .aspectFill
                
        // Present the scene
        view.presentScene(scene)
}

 

  • あとはプロジェクトをビルドして、どれかの機種のシミュレーターを起動すれば、先ほど設定したシーンの背景色(青色)一色の画面が表示されます。間違いなく自前のSKSからシーンが表示されていることが確認出来ました。

f:id:fleron:20200104022122p:plain
 
 

スケールモードによる、機種別のシーンの見え方(準備)

  • ここからが本題です。まずは「MyScene.sks」に戻り、インスペクタでシーンのサイズをiPhone 8 Plusのピクセル数と同じ1242 x 2208に変更します。

f:id:fleron:20200104135526p:plain
 

  • そしてシーンに配置するための、1242 x 2208サイズの背景画像を用意します。今回は以下のように1〜5の数字を上下左右および中央に書いた画像を用いて、スケールモードの設定によってシーンが拡大縮小される場合は、その様子が分かりやすくなるようにしました(画像は掲載用に縮小してありますが、実際は1242 x 2208サイズの物を用いています)。この画像をプロジェクトに追加しておきます(ここではファイル名を「background.png」とします)。

f:id:fleron:20200104140119p:plain
 

  • 「MyScene.sks」を表示した状態で、Xcodeの「+」ボタンをクリックすると、シーン上に配置するノードなどのオブジェクトを選択するウインドウが開きます。「Color Sprite」を選択して、ドラッグ&ドロップでシーンの中央に配置します。

f:id:fleron:20200104141324p:plain
 

  • 先ほどの背景画像「background.png」をプロジェクトに追加済みであれば、Spriteのインスペクタで「Texture」を操作すると名前が表示されますので、選択すればシーン上に表示されます。

f:id:fleron:20200104141815p:plain
 

  • あとは「GameViewController.swift」でスケールモードを設定している部分を色々書き換えて、それぞれの機種のシミュレーター上でシーンの見え方がどう変わるかを実験して行きます。デフォルトでは「aspectFill」の設定となっていますが、他に「aspectFit」「fill」「resizeFill」が設定出来ます。
if let scene = SKScene(fileNamed: "MyScene") {     
        // Set the scale mode to scale to fit the window
        scene.scaleMode = .aspectFill   //スケールモードの設定。ここの部分を書き換える。
                
        // Present the scene
        view.presentScene(scene)
}

 
 

スケールモードによる、機種別のシーンの見え方(結果)

まずは実験対象とする機種の一覧表を再掲します。

機種名 ポイント ピクセル アスペクト比
iPhone 8 Plus 414 x 736 1242 x 2208 9 : 16
iPhone 11 Pro Max 414 x 896 1242 x 2688 9 : 19.5
iPad Pro 12.9 1024 x 1366 2048 x 2732 3 : 4

早速それぞれの結果(シミュレータの画面キャプチャ)を見て行きたいと思います。
 

aspectFillの場合

iPhone 8 Plus iPhone 11 Pro Max iPad Pro 12.9
f:id:fleron:20200104143855p:plain f:id:fleron:20200104144027p:plain f:id:fleron:20200104144044p:plain

まずはデフォルトのaspectFillの場合です。aspectFillではシーンのアスペクト比は保たれたまま、画面全体を隙間なく埋めるように表示されます。そのためシーンが一方向に拡大縮小されて見た目が変わってしまうことは無いのですが、画面に収まらず見切れてしまう部分が生じる場合があります。

今回はiPhone 8 Plusのピクセルに合わせた画像ですので、この機種ではもちろん画像通りの表示が得られますが、11 Pro MaxおよびiPadでは端の部分が見切れてしまっています(更に実際の11 Pro Maxでは、1の数字は一部がノッチに隠れてしまいます)。
 

aspectFitの場合

iPhone 8 Plus iPhone 11 Pro Max iPad Pro 12.9
f:id:fleron:20200104143706p:plain f:id:fleron:20200104143717p:plain f:id:fleron:20200104143835p:plain

続いてaspectFitの場合です。aspectFitではシーンのアスペクト比は保たれたまま、シーン全体が画面内に収まるように表示されます。その代わり何も表示されない余り部分が画面に生じる場合があります。

実際に11 Pro MaxおよびiPadでは、シーンからはみ出した画面の余り部分が真っ黒に表示されています(本ブログの背景が黒いので分かりづらいかもしれませんが)。
 

fillの場合

iPhone 8 Plus iPhone 11 Pro Max iPad Pro 12.9
f:id:fleron:20200104151208p:plain f:id:fleron:20200104151222p:plain f:id:fleron:20200104151254p:plain

続いてfillの場合です。fillではシーン全体が画面にピッタリ合うよう、縦横方向に拡大縮小されて表示されます(アスペクト比は保たれません)。画面の見切れや余り部分などは生じませんが、特にキャラクターなどを描いたスプライトを表示する場合は、見た目が大きく変わってしまうかもしれません(また、こちらでも11 Pro Maxでは、1の数字は一部がノッチに隠れてしまいます)。
 

resizeFillの場合

iPhone 8 Plus iPhone 11 Pro Max iPad Pro 12.9
f:id:fleron:20200104152432p:plain f:id:fleron:20200104152452p:plain f:id:fleron:20200104152517p:plain

最後にresizeFillの場合ですが、これまでと異なり8 Plusでも画像通りの表示とならず、中央の5の数字周辺部分だけが大きく表示されています。どうやらresizeFillではシーンのスケーリングは行われず、ビューのサイズ(通常は機種一覧表の「ポイント」と合致)に切り抜かれて表示されるようです。

例えば8 Plusの場合1242 x 2208サイズのシーン全体のうち、その中央の414 x 736サイズの部分だけが切り抜かれるので、このような表示となります。他の機種についても同様です。

ここで、試しに「MyScene.sks」でシーンのサイズは1242 x 2208のまま、背景画像のSpriteのサイズだけを400 x 700(8 Plusのビューよりわずかに小さい値)に変更して、再度resizeFillで機種別の表示を見てみます。
f:id:fleron:20200104170424p:plain
 
結果は以下の通りです。

iPhone 8 Plus iPhone 11 Pro Max iPad Pro 12.9
f:id:fleron:20200104171435p:plain f:id:fleron:20200104171447p:plain f:id:fleron:20200104171458p:plain

今度はSpriteのサイズがいずれの機種のビューよりも小さいですので、Spriteは400 x 700のまま表示され、余った部分はシーンの背景色(ここでは青色)で表示されます。
 
以上、スケールモードの設定によって、同じシーンの見え方が機種別にどのように異なってくるのかを確認しました。これらのスケールモードは、毎回これを設定すればOKという正解があるわけではなく、作成するアプリの内容や、対応する機種(iPhoneのみか、iPadもカバーするか)などによって適宜使い分ける必要がありそうです。「GameViewController.swift」内で機種や画面サイズの情報をチェックし、それによってどのスケールモードを設定するか条件分岐させる方法もあるようですので、今後は実際のアプリ開発を通じて更に色々と調べて行きたいと思います。