GameProgrammar's Night

ゲームプログラム系の覚え書き

UnrealEngine4で2Dゲームを作ろう! 「2Dゲームを作るために必要なこと 基本編」

 2Dゲームとは直接関係のない話題ばかりになっているので、いったん2Dについて一通りまとめて、「UnrealEngine4で2Dゲームを作ろう!」シリーズはいったん締めたいと思います。
 UE4に関する話題の更新は、まだネタもあるので続けますのでご安心(?)を。

 ドットバイドット表示設定やスプライトについて紹介する「基本編」と、2DゲームにおけるUE4の機能について紹介する「機能編」の2本立てでお送りします。
 対象バージョンは、UE4.10以上(Windows)です。

ドットバイドットで表示する

 ドットバイドット表示とは、ディスプレイの1ピクセルとゲームの1ピクセルのサイズをぴったり合わせた表示のことです。ドットバイドット表示にしないと、スプライトがボケたりズレたりします。スプライトのボケズレはポリゴンよりかなり目立つため、できれば避けたい所です。
 UE4のドットバイドット表示は、ウィンドウ・カメラ・PixelPerUnitの設定を適切に行うことで実現できます。

ウィンドウの設定

ウインドウの設定(UE4.11以降)

 UE4.11で、ウインドウサイズやフルスクリーン設定がBPノードとして公開されました。
 GameUserSettingsを取得して、SetScreenResolution/SetFullscreenModeで設定し、最後にApplySettingsを読んで設定を適用します。

f:id:katze_7514:20160405163329j:plain

ウィンドウサイズ設定(UE4.10以前)

 ウィンドウサイズ設定の仕方は、いくつかありますがコンソールコマンドを使う方法をお勧めします。

f:id:katze_7514:20160304152924j:plain

 ExecuteConsoleCommandノードを使い「r.SetRes 幅x高さ」とコマンドを入れます。幅x高さの後にwだと「ウィンドウモード」、fだと「フルスクリーンモード」になります。

 このコマンドを最初にロードされるLevelやGameModeのBeginPlayで呼ぶことで、ゲーム起動時にウィンドウサイズが設定されます。

フルスクリーンへの対応(UE4.10以前)

 ドキュメントにちゃんと書かれていないのですが、UE4は2種類のフルスクリーンをサポートしています。
 1つはディスプレイサイズも設定したウィンドウサイズに変更するフルスクリーン(以下単にフルスクリーン)、1つはウィンドウサイズを現在のディスプレイサイズに拡大するウィンドウフルスクリーン*1、です。

 これらの切り替えは、ウィンドウサイズの設定と同じようにコンソールコマンドで切り替えを行います。

f:id:katze_7514:20160304154639j:plain

 「r.FullScreenMode 0」ならフルスクリーン、「r.FullScreenMode 1」ならウィンドウフルスクリーンとなります*2
 このコマンドは「r.SetRes」コマンドの前に呼ぶ必要があります。

 フルスクリーンとウィンドウフルスクリーンの主な違いは以下の通りです。

フルスクリーン ウィンドウフルスクリーン
アスペクト比 ディスプレイのアスペクト比になる カメラの設定に従う
DPIスケーリング ウィンドウサイズが使われる ディスプレイサイズが使われる

 特にDPIスケーリングの違いは忘れがちなので、ちゃんとDPIスケーリングのカーブを設定しておきましょう。

ウインドウリサイズへの対応(4.14以降)

 常にドットバイドットで表示するために、ウィンドウの最大化やマウスドラッグによるウィンドウサイズ変更をできないようにします。
 [ProjectSettings] → [Description] → [Settings]にて、最大化・リサイズなどの有効無効が設定できるようになりました。
f:id:katze_7514:20161126230328p:plain

ウィンドウリサイズへの対応(4.13以前)

 UE4.13以前で、実現するにはC++を利用する必要があります。
 BPから呼び出すのことのできるC++関数を定義します。関数の中身は以下の通りです。

#include <windows.h>

void UCommonBPFunctionLibrary::SetupSpriteGameWindow()
{
    // Editor起動の時は何もしない
    if(GIsEditor) return;
	
    // MainWindowのウインドウハンドルを取得する
    HWND hWnd = NULL;
    TSharedPtr<SWindow> MainWindow = GEngine->GameViewport->GetWindow();
    if(MainWindow.IsValid() && MainWindow->GetNativeWindow().IsValid())
    {
        hWnd = static_cast<HWND>(MainWindow->GetNativeWindow()->GetOSWindowHandle());
    }

    if(hWnd)
    {
        LONG Style = ::GetWindowLong(hWnd, GWL_STYLE);
	// ドラッグによるサイズ変更と、最大化ボタンを無効にする
	Style &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
	::SetWindowLong(hWnd, GWL_STYLE, Style);
	::ShowWindow(hWnd, SW_SHOW);
    }
}

 ビルド設定に、Slate/SlateCoreモジュールを追加してください。

 この関数をウィンドウモードに変更した際に呼び出すことで、ウィンドウリサイズを無効にすることができます。
f:id:katze_7514:20160304175033j:plain

 C++の関数をBPに公開する方法は、「UE4 C++コードをブループリントで使えるようにする(関数ライブラリー編) - Let's Enjoy Unreal Engine」あたりを参照してください。

カメラの設定

f:id:katze_7514:20160304143622j:plain
 カメラで重要な設定は、図の緑線を引いた4つの項目です。

項目 意味 設定値
ProjectionMode 射影モードを設定する Orthographic(正射影)
OrthWidth 正射影の時のカメラの範囲とする幅(UEUnit単位) 画面幅のピクセル数を入れる。図だと幅1280ピクセル
ConstrainAscpectRatio アスペクト比を維持するかどうか チェックを入れる
AspectRation アスペクト比 画面幅÷画面高さ の値を入れる。直接計算式を入れて良い

PixelPerUnitの設定

 最後にPiexlPerUnit(以下PPU)の設定を行います。PPUとは、名前の通り、1ユニット単位のピクセル数を設定する項目です。
 使うスプライトシステムごとに設定する必要がありますが、とりあえず、1.0を入れておくのが一番簡単です。
 1.0にすれば1ユニットが1ピクセルになりますので、座標の値がピクセルと一致します。様々な計算が楽になります。
 もし、1.0以外の値を使う時は、カメラのOrthWidthの値も忘れずに変更してください。OrthWidthの正確な算出式は、「画面幅(ピクセル)×PPU」です。

 本記事で紹介する2つのスプライトシステムでのPPU設定場所は次の通りです。

Paper2D

 [プロジェクト設定]→[エディタ]→[Paper2D Import]→[Default Piexel Per Unreal Unit]

f:id:katze_7514:20160304182119j:plain

 スプライトを作る時のデフォルト値として設定しておくのが簡単です。
 スプライトごと、個別に設定したい場合は、設定したいPaper2D Spriteを開いて、[Sprite]→[Pixel Per Unit]で設定できます。

f:id:katze_7514:20160304183414j:plain

SpriteStudio

 [SsPlayerComponent]→[Sprite Studio Render Settings]→[UUPer Pixel]

f:id:katze_7514:20160304182445j:plain

 こちらは、1ピクセルが何ユニットかという設定なので、Paper2Dとは逆値を設定する必要がありますので、両方使う場合はご注意ください。

2Dの描画(Sprite)

 UE4で、2D(スプライト)描画を行うには、現状2つのシステムがあります。
 UE4が公式に用意しているPaper2Dシステムと、WebTechnology社が制作ししてるSpriteStudioシステムです。
 正直な所、どちらも一長一短です。共存は問題なくできますので用途に合わせて使い分けます。

Paper2D

docs.unrealengine.com

 まずは公式の方から、細かい使い方はドキュメントを見ていただくとして、ここでは概要を紹介します。
 Paper2Dの機能は大きく2つあります。Paper2D Sprite と Paper2D Flipbook です。

Paper2D Sprite

 名前の通りSpriteを描画する基本機能です。Paper2DのシステムはまずテクスチャからSpriteを作ってから操作を行います。
 1枚のSpriteを任意の設定で描画します。

Paper2D Flipbook

 任意枚数のPaper2D Spriteを指定した時間で連続再生する機能です。いわゆるパラパラアニメを実現する機能です。

長所

 UE4公式なので、UE4の機能との親和性が高いです。マテリアルも任意に設定できますし、ダイナミックマテリアルインスタンスも使えます。
 最近、新機能の追加が行われていませんが、これからの機能追加も期待できます。

短所

 間接アニメを簡単に作ることはできません。間接アニメとは、2D絵を腕・胴・脚といった部分に分解して、個別に動かすことでアニメーションさせる手法です。Flipbookはレイヤーに対応していませんし、キーフレームには1つのSpriteしか設定できませんので、単純なパラパラアニメしか作ることができません。

その他

 他にも、マップチップを使用して任意サイズのスプライトを作るTileMap機能もあります。

SpriteStudio

www.webtech.co.jp

 もう一つはSpriteStudioです。2Dアニメ制作ツールですが、パラパラアニメを作るのはもちろんのこと、特に関節アニメ制作に力が入っているツールです。
 インディーライセンスならばすべて無料で使うことができます。Webサイトを見ると半年だけとありますが、半年ごとにライセンスを取り直して良いという公式の見解があります。

 SpriteStudioそのものは、2Dアニメを制作するためのエディタです。エディタで作ったデータを読み込んでUE4上で使うにはSpriteStudioプラグインを使います。

github.com

 SpriteStudioプラグインは完全に無料だそうです。プラグイン開発はWebTechnology社の認可を受けて、あのhistoria社が行っていますのでプラグインの出来も問題ありません。
 エディタの使い方、プラグインの使い方は、各公式サイトのドキュメントを参照してください。

長所
  • 間接アニメを作ることができます。
  • 元からゲーム用途を想定して作られているため、なかなかかゆい所に手が届く出来です。アニメーションエディタとしても優秀ですし、PGとしてはアニメーションにデータを埋め込んで、それをイベント(OnSsUserData)として簡単に取れるので連携がしやすいです。
短所

 マテリアルの設定が自由にできないことです。基本的にSpriteStudioプラグインが用意しているデフォルトマテリアルしか使うことができません。一応、任意のマテリアルを設定する仕組みはありますが、ダイナミックマテリアルインスタンスに対応してないため、使い勝手がよくありません。

Flasher向けに

 Flashを触った経験があるとSpriteStudioのエディタはちょっと戸惑うと思います。戸惑いの原因はアニメを作る時の考え方の違いです。
 Flashは「ある時間になんの絵がどこにあるか」という考え方ですが、SpriteStudioは「ある絵がある時間にどこにあるか」という考え方で作られています。
 SpriteStudioエディタのレイヤーに見える部分はFlash的なレイヤーではありません。ビットマップ(MovieClip)シンボルを指していると考えてください。

f:id:katze_7514:20160304193235j:plain

Spriteシステムの使いわけ

 基本的にはSpriteStudioを使うようにして、カスタムマテリアルが必要ならばPaper2Dを使うのが良いと思います。

描画順

 スプライトの描画順は、カメラに対する距離を調整することで行います。
 見下ろし(XY平面)ならZ値、横スクロール(XZ平面)ならY値を調整することで、描画順をコントロールします。
 見下ろしの場合は、スプライトのpivotをベースライン(Spriteの一番下のライン)に設定することで違和感の少ない描画順を設定することができます。

 描画順を設定するのは、Spriteだけにするのをお勧めします。Actorごと動かしてしまうとCollisionも動きますので、調整が大変になります。

描画順ソート設定

[プロジェクト設定]→[Rendering]→[透過性] で設定します。

f:id:katze_7514:20151104165220j:plain

項目 意味 設定値
Translucent Sort Policy 奥行きソートのアルゴリズム [Sort Along Axis]にします。指定した軸の値でソートする設定です。2Dゲームの場合はこれで十分です
Translucent Sort Axis アルゴリズムを[Sort Along Axis]にした場合のソート軸 見下ろしの場合はZ軸になりますので、(x, y, z)=(0.0, 0.0, -1.0) と値を入れておきます。

SpriteActor(Pawn)

 最小の機能を持ったSpriteActor(Pawn)は、空のActor(Pawn)にCollisionComponent/Sprite系Componentを追加して実装します。

f:id:katze_7514:20160304201919j:plain

 図では、FlipbookとSsPlayer(SpriteStdioのComponent)を両方いれてますが、実際には必要な方だけいれてください。
 Sprite系Componentは、必ずCollisionComponentの子供にします。そうしないとCollsionヒットした際のブロック処理が上手く動きません。
 もしCollisionComponentを使わずに、Spriteに設定したコリジョンを使う時は、Sprite系Componentをルートに配置します。

 もちろん、他に必要なComponetがあれば追加します。おそらく移動系とサウンド系は追加することになると思います。

フレームレート(FPS)の設定

 UE4は可変フレームレートで実装するのが基本になります。そのため、Paper2DもSpriteStudioも可変フレームレートで動作するようになっていますので、UE4の機能を使って実装している限りは特に気にする必要はありません。2Dアニメーションは、3Dのモーションのような補間はされませんので、極端にフレームレートが落ちた場合はアニメーションのフレームが飛んでカクカクすることはあります。
 とはいえ、FPSをコントロールしたいと思うこともあると思いますので、その方法を紹介します。

最大FPSの設定

 デフォルトでは際限なくFPSが上がるようになっていますが、FPSが高すぎると処理負荷が無駄に上がることもありますので、想定している動作FPSで上限を付けておくと良いと思います。

UE4.10以前

 最大FPSの設定は、コンソールコマンドで「t.MaxFPS 設定したいFPS」と実行します。

f:id:katze_7514:20160304203705j:plain

 図の例だと、最大FPSを60としています。

UE4.11

 UE4.11では、BPノードでも設定できるようになりました。SetFrameRateLimitノードで設定できます。
f:id:katze_7514:20160405164749j:plain

 図に入れ忘れましたがApplySettingsを最後に呼ぶのをお忘れなく。

FPSの固定

 FPSの固定は、UE4.10では正しく動いていません*3。UE4.11では修正され動くようになりました。

 FPS固定の設定場所は、[プロジェクト設定]→[エンジン]→[基本設定]→[Framerate] です。

f:id:katze_7514:20160304222212j:plain

 UseFixedFrameRateにチェックを入れるとFPSが固定される設定となります。

 また、起動引数に「-BENCHMARK -FPS=60」とやると固定できるとされていた仕組みもありますが、こちらはタイムステップの固定*4という意味に変わったようです。そのため、フレームレート固定としては使えません。

*1:仮想フルスクリーンとも呼ばれます

*2:ちなみに、この引数はEWindowMode列挙型に連動しています

*3:'Use Fixed Frame Rate' not working properly - UE4 AnswerHub

*4:実時間経過は無視して、1フレごとに設定された時間が経過したことに強制的にする