現代のモバイルゲーム開発者が向き合うべき ディスプレイ事情と、その対応方法
はじめに
現代では多くの人々がスマートフォンやタブレットを所持し、ゲームを楽しんでいることと思います。 モバイル向けのゲーム市場は大きく、世界では今も多くの開発者たちが iOS / Android 向けの商用ゲームを作っています。
さて、モバイルゲームの開発には、多くの開発者を悩ませるちょっと困った事情があります。 それは ディスプレイが非常に多くの多様性を持っている ことです。 正方形に近い形のタブレットもあれば細長い端末もあり、カメラがディスプレイの中に割り込んでいたり、 OS が専有する領域があったりと様々です。物理的な大きさもバラバラですね。 そのくせモバイル端末はタッチパネルで、ディスプレイに触れる UI になっているのです。 ゲーム開発を生業とする多くのエンジニア / アーティスト / ゲームデザイナーは、 これまでに何度も頭を抱えてきたことでしょう。
本稿では、2019 年現在のモバイルのディスプレイ事情を整理し、具体的な対応方法の例を示して、 ゲーム開発者の方々が設計・実装するのを手助けする資料をまとめます。
この記事を読むと得られるもの
- 現代において考慮すべき、主要なディスプレイ事情を知ることができます
- 画面のアスペクト比の変化に対して、ゲームデザインで考慮すべきことを確認できます
- Unity で 2D / 3D があるゲームを開発する際の、具体的な対応方法がわかります
筆者はゲーム開発をしているエンジニアなのでエンジニア視点での内容が多くなりますが、 UI デザイナーやゲームデザイナー、ディレクターの人にとっても役に立つものがあるかと思います。
また現代では iOS / Android 向けのゲーム開発において Unity が採用されることが多いため、 具体的な実装などは Unity をベースに話を進めますが、考え方は他のゲームエンジンを用いている場合でも有用でしょう。
対応後のイメージ
以下はアスペクト比やセーフエリアに応じて UI 配置やカメラを調整するよう対応した際の動作画面です:
現代のディスプレイ事情まとめ
問題は起きてから対処するよりも、事前に認識して回避する方が対応が楽です。 現代のモバイルディスプレイにおいて考慮しておくべき要素を図にまとめましたので、 ゲーム画面を設計する際にご利用ください:
トピックとしては以下があります:
- (1) アスペクト比
- (2) 角丸とノッチ
- (3) セーフエリア
(1) アスペクト比
TV アニメの多くが 4 : 3 から 16 : 9 に移行し始めたのは 2009 年頃でしょうか? 実際、2010 年以降は 16 : 9 というアスペクト比が世の中で最も多く見かけるディスプレイのサイズだったかと思います。
スマートフォンも 2017 年頃までは 16 : 9 が圧倒的に多数派だったのですが、 iPhone X が登場したあたりから、世の中に「長い」端末が多く現れるようになってきました。
以下に、主な端末のアスペクト比と発売時期を一覧します。 「1 : ?」の列は比較しやすいよう、短い辺に対して長い辺が何倍の長さかを示したものです:
端末 | アスペクト比 | 1 : ? | 発売時期 |
---|---|---|---|
iPhone 11 | 約 19.5 : 9 | 2.165 | 2019-09 |
iPhone X | 約 19.5 : 9 | 2.165 | 2017-11 |
iPhone 8 | 16 : 9 | 1.777 | 2017-09 |
iPhone 7 | 16 : 9 | 1.777 | 2016-09 |
iPhone 6 | 16 : 9 | 1.777 | 2014-09 |
iPhone 5 | 16 : 9 | 1.777 | 2012-09 |
iPhone 4S | 3 : 2 | 1.500 | 2011-10 |
iPad (3rd 〜 7th) | 4 : 3 | 1.333 | 2012〜2019 |
iPad Pro 11 inch | 約 4.3 : 3 | 1.432 | 2018-11 |
iPad Pro 12.9 inch (3rd) | 4 : 3 | 1.333 | 2018-11 |
端末 | アスペクト比 | 1 : ? | 発売時期 |
---|---|---|---|
Pixel 4 XL | 19 : 9 | 2.111 | 2019-10 |
Pixel 4 | 19 : 9 | 2.111 | 2019-10 |
Xperia 1 | 21 : 9 | 2.333 | 2019-06 |
Xperia 10 | 21 : 9 | 2.333 | 2019-03 |
Galaxy S10 | 19 : 9 | 2.111 | 2019-03 |
Pixel 3 XL | 18.5 : 9 | 2.055 | 2018-10 |
Pixel 3 | 18 : 9 | 2.000 | 2018-10 |
Galaxy S9 | 18.5 : 9 | 2.055 | 2018-05 |
Xperia XZ2 | 18 : 9 | 2.000 | 2018-05 |
Huawei Mate 10 lite | 18 : 9 | 2.000 | 2017-12 |
Xperia XZ1 | 16 : 9 | 1.777 | 2017-11 |
ZenFone 3 | 16 : 9 | 1.777 | 2017-03 |
Xperia Z5 | 16 : 9 | 1.777 | 2015-10 |
Galaxy S6 | 16 : 9 | 1.777 | 2015-04 |
- 初期の iPhone は 3 : 2 という変わったサイズでしたが、もはや昔のものになったので今は考慮しなくてよいでしょう
- iPad は昔から変わらず 4 : 3 という、昔の TV のアスペクト比と同じものになっています
- ただし 11 inch だけは若干アスペクト比が異なっています
- 2018 年くらいからは、ミドルクラスの Android でも 18 : 9 といった従来よりも長い端末が出てきました
- 2019 年にはついに映画と同等の 21 : 9 という、シネマスコープ サイズの Xperia が登場
- 2019 年も終わる現在のハイエンド機には iPhone 11 系と Pixel 4 がありますが、どちらも 2 : 1 を越えるサイズになっています
- ゲームの UI デザインは 16 : 9 を基準にして行うことが多かったと思いますが、 今後 16 : 9 の端末自体は少なくなりそう ということには留意しておかなければいけません
- 現状(筆者の知る限り)最も正方形に近いアスペクト比は iPad の 4 : 3 であり、
最も長いアスペクト比は Xperia 1 / Xperia 10 の 21 : 9 です
- 動的にレイアウトを調整する UI の配置や背景の見切れなどは、この 2 つのサイズでチェックして問題なければ 世の端末に対応できていると考えてよいでしょう
ちなみに Android の 画面分割モード でアプリを起動したりすると 1 : 1 のサイズで画面が描画されたりします が、 忙しい我々開発者は気が付かなかったことにしましょう。
(2) 角丸とノッチ
2 : 1 を越えるディスプレイに角丸とノッチ(画面上部の切り欠き)を持って 2017 年末に登場した iPhone X は、 世の UI デザイナーに衝撃を与えました。モバイルゲームの開発者であった僕も腰を抜かしたものです。
ノッチを初めて見たときは「流石は Apple…」という感想で、とは言え iPhone X は特殊なチャレンジモデルなのかな、 くらいに思っていたのですが この後 Android 端末にもノッチが流行りました。 「あ、Android にもノッチが…」とわたわたしていると、 画面の隅に穴の空いた パンチホール 方式なども出てきました。 (嘘だろ…?)
これらは 「ディスプレイのベゼルをできるだけ小さくしたい」 要件と 「とは言え前面にカメラやセンサーは必要である」 という要件が重なったために生まれた概念ですが、開発者として言えることは以下です:
- 安全な描画領域が保証されなくなった以上、単に画面端からのオフセットで UI を配置することはできなくなりました
- きちんと対応するには iOS / Android のシステムから セーフエリアのサイズを取得 して、動的調整をかける必要があります
(3) セーフエリア
ノッチとセットのトピックですが、iPhone X は セーフエリア という新しい概念も持ち込みました。 ノッチやパンチホールがある Android にもセーフエリアの概念はありますが、 iPhone X / iPhone 11 の場合は 消えたホームボタンの代わりに導入されたホームインジケータ(ホームバー) という存在にも注目です。
表示を OFF にすることもできるようですが、デフォルトでは画面中央下にバーが常駐しています。 このため、横画面の場合でも画面の下側がセーフエリア外になることには注意が必要です。 (モバイルゲームによくあるフッタなどの UI は影響を受けやすいでしょう)
固定アスペクト比や帯表示で楽はできないのか?
エンジニアは一般化を好み、例外を嫌います。 「楽をしたい」という美徳を持ったエンジニアなら、当然 「アスペクト比を固定したデザインにして、動的対応を無くせないのか?」 ということは考えるでしょう。
4 : 3 から 21 : 9 まで存在する現状では、概ね中間であり多数派である 16 : 9 を基準とすることが多いかと思います。 16 : 9 の画面領域を各端末に当てはめたイメージは、以下のようになります。 (赤い部分が 16 : 9 に収まらない範囲です):
- 実際、上記のような固定アスペクト(範囲外は帯で隠す)で良いという指針を貫くのであれば、考えることは非常に少なくなります。
実践的な手法として採用可能です
- (※ ただし単に黒塗りなどにすると Apple の審査ガイドラインに違反するため、目隠し部分に模様を入れるなどの対応は要ります)
- 競技性やプレイフィールの統一性が重要なゲームの場合は、むしろこのデザインが適していることもあります
- 例えば FPS やシューティングなどでは、視野が広いほどゲームが有利になります
- 操作範囲(タッチ領域)が変わるとプレイフィールが変わるシューティングなどでは、 世のゲームでも 16 : 9 に固定して範囲外を帯で隠しているものが多いです
- 長い端末で描画領域を中央にするか、操作性を考慮して上や下に寄せるか、などは一考の余地があります
ただし、ユーザというのはワガママなので、ユーザが 「自分の使っている端末で画面いっぱい(edge-to-edge)に描画されてほしい」 と考えるのは自然なことです。筆者としても、ユーザ目線でゲームを遊ぶ時は「帯表示が無い方がリッチで嬉しい」と感じます。 商用ゲームであれば、可能な限り画面全体を使ったデザインが推奨されるでしょう。
画面サイズを柔軟にしすぎると困る場合は、16 : 9 以外の部分に 「見えても見えなくても影響の少ない無難な要素」 を置くようにします。
3D カメラの見切れ or 見えなさすぎ問題
※ ここで言う「見切れ」という語は「見えてはいけないものが見えてしまう」という意味で使用しています
端末のサイズが異なる以上、固定アスペクト比にしない限りは 「端末によってゲーム画面の見える範囲が変わる」 ということは避けられません。 2D のゲームでも起こりうる問題ですが、より顕著な例としてここでは 3D の描画を取り上げます。
水平方向、または垂直方向のどちらかの視野を固定してしまうと、画面サイズが変わった時の視野に差が出やすくなります。 例えば Unity のデフォルトのカメラでは上下の視野 (Vertical FOV) が固定されるので、 「横が狭い端末で見える範囲が極端に狭くなってしまう」 という問題が起こりやすくなります。
これは逆に言うと 「横に広い端末の方が見える範囲が広くなり有利になってしまう」 ということでもあります。 ソロで楽しむゲームでは別にそれでよいかもしれませんが、マルチプレイの対戦ゲームなどでは考えものです。
以下は Unity のカメラをそのまま使用した場合と、 スクリプトで 「少なくとも画面内の水色のオブジェクトが収まるようにカメラの FOV を動的調整」 した場合の見え方の差です:
縦画面の場合は以下のようになります:
具体的な対応方法やスクリプトについては後述します。 ここでは 「大抵の場合、カメラの動的対応は要る」 ということを覚えておいていただければと思います。
世の商用ゲームを 4:3 と 21:9 で見比べてみる
ここまでは筆者が作成した画面を使って説明をしてきましたが、 ここいらで実際の世のゲームを異なるアスペクト比で見比べてみることにしましょう。 世の開発者の皆さんがどのような苦労をしているかが分かるかと思います。
アスペクト比の項で 「4 : 3 と 21 : 9 で破綻しないなら問題ない」 という旨を述べましたので、その 2 つのサイズで比較します。 家にたまたま iPad Pro 10.5 inch と Xperia 10 Plus があったので、こちらを使ってゲームをプレイし、 スクリーンショットを撮って比較していきます。
ファイアーエムブレムヒーローズ
まずは UI 多めの縦画面ゲームから見ていきましょう。
- 2017-02 にリリースされた、歴代 FE キャラが登場するゲームです
- 筆者はファイアーエムブレムシリーズが好きなので、このゲームはよく遊んでいました (風花雪月が出てからそっちばかりやるようになってしまいましたが…)
- 縦方向は 16 : 9 で固定、iPad の場合は無難に横を広げて対応、という方針です
- 対応のコスパが良く、理にかなったやり方だと思います
ちなみにこれは完全な余談ですが、筆者のお気に入りキャラは「蒼炎の軌跡」「暁の女神」に登場するワユです。
どうぶつの森 ポケットキャンプ
続いて 3D 描写のある縦画面のゲームを見ていきます。
- 21 : 9 では下側に黒帯が入っています。 19 : 9 以上は対応外として黒塗りしているようです
- 3D のカメラは横の視野が狭くなりすぎないように配慮がなされているように見えます
- 変わりに 21 : 9 端末では縦方向の視野が広くなっています
ポケモンマスターズ
2019-08 にリリースされた、比較的新しいゲームを見てみましょう。 権利表記などを見た感じ、Unity ではなく C++ で実装されてそうな気がするタイトルです。
- edge-to-edge の描画ですが、バトルなどでキャラがはみ出さないように調整されています
- 4 : 3 と 21 : 9 では、UI のリストのサイズが倍くらい変わるんですね…
ホームスケイプ
続いて横画面のゲームを見てみます。 まずはカジュアルなスリーマッチパズルから。
- 21 : 9 でもちゃんと edge-to-edge で描画されました
- iPad を最小の横幅として UI が組まれている印象です(横に広くなるほど余白が広くなる)
ミリシタ(アイドルマスター ミリオンライブ! シアターデイズ)
ビジュアルが重要な商材となっている、アイドルゲームを見てみましょう。
(21 : 9 の端末だと長過ぎて音ゲーが遊びにくいという問題がありますが、それはまた別のお話…)
- 初期は 「16 : 9 基準で SSR イラストが描かれているので、iPad だと絵の見える範囲が狭まる」
という iPad 勢に厳しい仕様でした
- その後、iPad でもイラストやゲーム中のライブ映像を 16 : 9 にできる(上下に帯を入れる)モードが実装されました
- タイトル画面のレイアウトが iPad とそれ以外で変わっている 点には注目です
- iPad 版は写真をナナメに配置したような絵になっています
- この 「写真をナナメに置く感じで iPad の絵が切れる範囲を緩和する」 という手法は良いアイディアだなと思いました
- (いつからかガチャ画面にもこのレイアウトが用いられるようになりました)
- UI は横幅を iPad 基準にしています
- 横に広い端末だと余白の広い印象になります
- 21 : 9 までは考慮されていなかったのか、背景の柄が切れている部分があります
- ライブ映像のカメラは、上下の視野を固定したカメラ になっています
- そのため、長い端末でプレイすると単純に横方向の見える範囲が広くなります
ちなみにこれは完全な余談ですが、筆者の担当アイドルは永吉昴です。
PUBG MOBILE
さて、ゲームの視野はゲームプレイの優位性に影響します。 ここで競技性の高いマルチプレイの対戦ゲームを見てみましょう。
- ゲーム画面では 横の視野が固定されている ことに注目です
- 横長端末でも、横が広くは見えないように調整されています。代わりに iPad では縦方向が広く見えます
- PUBG では上下の視野より左右の視野の方が情報としてゲームに与える影響が大きいため、 そこを統一して公平性を保つようにしている、ということだと思います
クラロワ(クラッシュ・ロワイヤル)
こちらは固定画面のゲームにはなりますが、縦持ちの対戦ゲームを見てみましょう。
- 4 : 3 と 21 : 9 で、思ったより受ける印象に差が無いように感じました。UI のレイアウトがうまいなと思います
- メインの対戦画面は、iPad では両サイドに無難な景色が描かれており、自然な印象のまま対応されています
GUI (2D HUD) の対応方法
※ ゲームの画面に浮くような情報表示は単に UI だったり GUI、HUD(Head-up Display)などと呼ばれますが、 ここでは GUI という語で統一します。
さて、世のゲーム画面を見て、GUI やカメラの対応イメージが掴めてきたかと思います。
ここからは具体的に、Unity で開発する際にどういった対応をすべきかを紹介していきます。 あくまで「筆者ならこのように実装するかな」といった例ですが、同じような境遇にある開発者さんの足がかりになればと思います。
と言いつつ、GUI の対応に関しては Unity 公式の記事があるので、こちらを参照すれば問題ないです:
Unity では 2017.2.1 から Screen.safeArea
が追加され、iOS のセーフエリアのサイズが取得できるようになりました。
初めは iOS のみの (それ以外では単にスクリーンサイズが返る) ものでしたが、
2018.3.0 以降は Android 9.0 に対応した端末からもセーフエリアが取得できるようになっているようです。
- Canvas は UI Scale Mode を
Scale With Screen Size
に指定します - Reference Resolution は UI をレイアウトする際の論理的な解像度です。16 : 9 の好みの値を入れるのでよいでしょう
- Screen Match Mode は自分は
Expand
にしていますが、Match Width or Height
にする方法もあります- 画面サイズを変動させて見比べてみると挙動の差が分かりやすいです
- セーフエリアによってサイズを変動させる Panel を一枚挟んで(図中
SafeAreaPanel
)、その配下に UI パーツを配置します SafeAreaPanel
には、セーフエリアを取得して動的に描画領域(Anchor)を変動させるスクリプトをアタッチします- 自分は以下のようなコードを書きました:
using UnityEngine;
namespace Alto.Util
{
public class SafeAreaCanvas : MonoBehaviour
{
RectTransform _panel;
Rect _lastSafeArea = new Rect(0, 0, 0, 0);
void Awake()
{
_panel = GetComponent<RectTransform>();
UpdateSafeArea();
}
void Update()
{
UpdateSafeArea();
}
void UpdateSafeArea()
{
Rect safeArea = Screen.safeArea;
if (safeArea == _lastSafeArea)
{
return;
}
_lastSafeArea = safeArea;
Vector2 anchorMin = safeArea.position;
Vector2 anchorMax = safeArea.position + safeArea.size;
anchorMin.x /= Screen.width;
anchorMin.y /= Screen.height;
anchorMax.x /= Screen.width;
anchorMax.y /= Screen.height;
_panel.anchorMin = anchorMin;
_panel.anchorMax = anchorMax;
}
}
}
以上で、GUI の描画領域がセーフエリア内に収まるようになります。
画面からはみ出すような UI パーツを配置したい場合は、SafeAreaPanel
の外側にはみ出すように配置すれば OK です。
余談: Unity の Device Simulator
今回の記事の動画やスクリーンショットを撮るのにも利用していますが、 Unity 2019.3 で preview 版として利用できる Device Simulator は便利でおすすめです。
基本的に端末の外観やセーフエリアをオーバレイ表示するようなシンプルなものですが、 そうそうこれが欲しかったんだ、という機能ですね。 安定版のリリースに期待です。
3D カメラの視野の調整方法
カメラを動的調整するには以下の方法があるかと思います:
- (a) 対象物が収まるようにカメラの FOV (Field of View) を調整する
- (b) 対象物が収まるようにカメラの座標を前後に移動させる
(a) はカメラの座標が変わらないので扱いやすい方法です。多くの場合はこちらの方法で問題ないかと思います。 ただし FOV を変えると画面端の歪み方が変わるので、それを嫌う場合(そしてカメラをあまり動かさないゲームの場合) は (b) のカメラ座標を変えるやり方も選択肢に入るでしょう。
自分は以下のようなコードを書きました:
using UnityEngine;
namespace Alto.Util
{
// 被写体の横幅がちょうどカメラに収まるよう、カメラの FOV を自動調整する
public class FovAdjuster : MonoBehaviour
{
[SerializeField] float aspectThresholdV = 9f;
[SerializeField] float aspectThresholdH = 16.0f;
[SerializeField] float subjectWidth = 20.0f;
[SerializeField] Vector3 subjectPos = new Vector3(0, 0, 0);
Camera _camera;
float _lastAspect;
void Start()
{
_camera = GetComponent<Camera>();
}
void Update()
{
AdjustCameraFOV();
}
void AdjustCameraFOV()
{
if (_lastAspect == _camera.aspect)
{
return;
}
_lastAspect = _camera.aspect;
_camera.fieldOfView = GetCameraFOVToFit(_camera, subjectWidth);
}
float GetCameraFOVToFit(Camera camera, float subjectWidth) {
if (camera == null || subjectWidth <= 0.0f)
{
return 179; // max value of field of view
}
float aspect = Mathf.Min(camera.aspect, aspectThresholdV / aspectThresholdH);
float frustumHeight = subjectWidth / aspect;
float distance = Vector3.Distance(camera.transform.position, subjectPos);
return 2.0f * Mathf.Atan(frustumHeight * 0.5f / distance) * Mathf.Rad2Deg;
}
}
}
このスクリプトをカメラにアタッチすると、実行中に FOV が自動調整されるようになります。
- パラメータは画面に収めたい対象物の幅と座標を
Subject Width
/Subject Pos
に指定します- 図中では Scene に置いた水色の板を目印にして、その幅と座標を指定しています
Aspect Threshold
はどの段階のアスペクト比から適用し始めるかというパラメータで、 指定したアスペクト比の時に対象物が画面にぴったり収まり始めるような挙動になります
サッと書いたコードなので使い勝手の問題だったり考慮漏れだったりがあるかもしれませんが、まあご参考まで。
ちなみに距離を変える版
(b) のカメラ座標を動的調整する版のコードは以下です:
using UnityEngine;
namespace Alto.Util
{
// Unity のカメラは上下の視野が固定で、画面が横に広くなると左右の視野が広くなる。
// (カメラの Field of View は Vertical FOV の意味。単位は degree)
// ForcalLengthAdjuster はどのアスペクト比でも一定の視野がカメラに収まるようにカメラの距離を調整する。
// (被写体の横幅がちょうどカメラに収まるような位置にカメラ位置を前後させる。FOV は一定に保つ)
public class FocalLengthAdjuster : MonoBehaviour
{
[SerializeField] float aspectThresholdV = 9f;
[SerializeField] float aspectThresholdH = 16.0f;
[SerializeField] float subjectWidth = 20.0f;
[SerializeField] Vector3 subjectPos = new Vector3(0, 0, 0);
Camera _camera;
float _lastAspect;
void Start()
{
_camera = GetComponent<Camera>();
}
void Update()
{
AdjustCameraDistance();
}
void AdjustCameraDistance()
{
if (_lastAspect == _camera.aspect)
{
return;
}
_lastAspect = _camera.aspect;
float cameraDistance = GetCameraDistanceToFit(_camera, subjectWidth);
Vector3 subjectToCamera = Vector3.Normalize(_camera.transform.position - subjectPos) * cameraDistance;
_camera.transform.position = subjectPos + subjectToCamera;
}
float GetCameraDistanceToFit(Camera camera, float subjectWidth) {
if (camera == null || subjectWidth <= 0.0f)
{
return 0.0f;
}
float aspect = Mathf.Min(camera.aspect, aspectThresholdV / aspectThresholdH);
float frustumHeight = subjectWidth / aspect;
return frustumHeight * 0.5f / Mathf.Tan(camera.fieldOfView * 0.5f * Mathf.Deg2Rad);
}
}
}
以上のような GUI とカメラの対応をすると、冒頭に示したような動作になります:
おわりに
本稿では、めくるめくモバイル端末のディスプレイ事情を整理し、ゲーム開発の上で考慮すべき点をまとめ、 Unity 上での具体的な対応例を示しました。
マルチディスプレイ対応はモバイルゲームを開発しようとした際に誰もが一度は直面する問題です。
この記事が、開発者の方々の悩める時間を節約するものになれば幸いです。