PaperSloth’s diary

主にゲーム開発関連についての記事を書きます。

UE4 ローカルマルチプレイについて

目次

環境

Unreal Engine 4.18.3

概要

ローカルマルチプレイの基本についてはalweiさんのブログが参考になります。
UE4 簡単なローカルマルチプレイヤーゲームを作ってみる - Let's Enjoy Unreal Engine
今回はこれに少し追記したような内容になります。


画面の分割設定について

画面を分割しない設定や縦に分割、横に分割の設定が可能です。
Project Settings - Maps & Modes - Local Multiplayer以下に設定項目があります。
f:id:PaperSloth:20180125213049p:plain


・Use SplitScreen
画面分割を行うか否かです。
デフォルトではオンになっているため、通常はこのままで良いでしょう。

・Two Player Splitscreen Layout
2人プレイ時の画面分割設定です。
Horizontal - 上画面、下画面に分割されます。デフォルトの設定です。
f:id:PaperSloth:20180125213434p:plain
Vertical - 右画面、左画面に分割されます。
f:id:PaperSloth:20180125213351p:plain

ゲーム内容に合わせてお好みでお選びください。

・Three Player Splitscreen Layout
3人プレイ時の画面分割設定です。
Favor Top - 上画面が大きく表示されます。デフォルトの設定です。
f:id:PaperSloth:20180125213537p:plain
Favor Bottom - 下画面が大きく表示されます。
f:id:PaperSloth:20180125213605p:plain

プレイヤーを指定の位置に配置したい場合について

PlayerStartを削除し、Level上に直接Pawn(Character)を配置する方法が楽です。

しかし、配置しただけではどのキャラクターも操作できないため操作設定を行います。
配置したキャラクターのPawn - Auto Possess Playerに指定したいPlayer番号を設定します。
0からカウントされるため、Player 0が1P、Player 1が2Pとなります。
f:id:PaperSloth:20180125213708p:plain

4人配置したイメージ
f:id:PaperSloth:20180125213838p:plain


2人、4人の対戦や協力ゲームだと同じキャラクターの色違いを用意したりするため簡単ですね。
Level上に直接Pawn(Character)を置くため、Pawn(Character)の種類はバラバラでも構いません。
例えば3人プレイのゲームで1対2で1人が巨大なモンスターというのも面白そうですね。
f:id:PaperSloth:20180125214328p:plain

まとめ

さて、比較的簡単にローカルマルチプレイゲームが作れますね。
私自身もUE4ぷちコンゲームジャムで3人構成のチームでこんなゲームを作りました。
www.nicovideo.jp


ですが、幾つか課題もあるためそれらが解決できそうかどうかも考えつつ挑んでみてください。

・4人プレイの場合そもそもコントローラーが用意できるか、繋げるか
 どちらも物理的な問題ですが、繋げるかは結構重要ですね。
 ノートPCのUSBポートの空き数と相談してハブを用意するなどしましょう。

・個々のプレイヤーの入力を上手くさばけそうか
 そこそこ簡単な問題ですが、肥大化したプロジェクトを
 いきなりマルチ対応したりするとぐちゃぐちゃになります。
 どうやったら綺麗に入力を処理できるかのイメージを考えましょう。

・個々のプレイヤー情報をどうやって取得するか
 TickでGet All Actors Of Classで取得するみたいな方法は極力避けたいですね。
 個々のプレイヤーの情報にアクセスしたりチェックしたりは必ず必要になるので、
 最初の段階で考えておくと良さそうです。

・リスポーン処理をどうするか
 例えば対戦ゲーム等でリスポーンがある場合に
 他のプレイヤーと同じ地点にリスポーンしてしまうのは避けたいですね。

以上のようにいざ作ってみると詰まってしまうことがあると思いますが1人プレイより盛り上がります!
Blueprintにちょっと慣れてきた方は作ってみてください。

Lumberyard ScriptCanvasで簡単なゲームロジック作成

この記事はLumberyard Advent Calendar 2017の25日目の記事です。
Lumberyard Advent Calendar 2017 - Qiita

環境

Lumberyard 1.12.0.0


参考資料

プレイヤーが移動するまでは下記2つを追ってもらえれば同じようなのができます!
【Lumberyard】Script Canvas入門 - ユーセンブログ
Script Canvas Tutorials - Lumberyard


空のレベルの作成

ScriptCanvasに入る前に適当にレベルを作成しておきます。
プロジェクトは新規作成したものでも、既存のものでも問題ありません。
f:id:PaperSloth:20171224120529p:plain

新規プロジェクト作成方法はこちら
papersloth.hatenablog.com
もし、Defaultで作成できなかったという時はEmptyも試してみてください。


プレイヤーの作成

続いてPlayerを作成していきましょう。

Entitiy OutlinerかViewport上で右クリックして
Create entity
f:id:PaperSloth:20171224121650p:plain

名前を変更したいので、Entity OutlinerかEntity Inspectorから
名前をPlayerに変更
f:id:PaperSloth:20171224121832p:plain


このままだと空のEntityなので、見た目を追加します。
Entity InspectorからAdd Compoenent
f:id:PaperSloth:20171224121959p:plain

RenderingカテゴリのMeshを追加
f:id:PaperSloth:20171224122104p:plain

まだ見た目がないので、設定していきましょう。
MeshComponentのMesh assetを選択
f:id:PaperSloth:20171224122424p:plain

Engine > EngineAssets > Objects > Default.cgfを選択
f:id:PaperSloth:20171224122548p:plain
この時に面、頂点、LOD、マテリアルの情報とプレビューが表示されるのは見やすいですね。

とりあえずプレイヤーの見た目は一旦これで終了です。
f:id:PaperSloth:20171224123128p:plain


ですが、このままだと何も起こらないので次は物理を追加しましょう。
再びAdd Componentから
Physics > Rigid Body Physicsを追加しました。
f:id:PaperSloth:20171224123410p:plain
Unityユーザーには馴染みがあるかもしれませんね。


次に入力を取れるようにします。
このあたりはtachi1427さんの解説と重複している箇所があるため
読み飛ばしていただいても問題ありません。
【Lumberyard】Script Canvas入門 - ユーセンブログ


Lumberyardでは空のEntityでは入力の取得ができない仕組みになっています。
Add Componentから Gameplay > Input
f:id:PaperSloth:20171224123728p:plain

入力の設定ファイルを作成します。
Input to event binding内にアーケードコントローラーみたいなボタンを押します。
f:id:PaperSloth:20171224124207p:plain

するとAsset Editor Windowが開くので、
File > New > Input bindingを選択します。
f:id:PaperSloth:20171224124342p:plain

(ProjectName) > inputbinding内に
player.inputbindingsを追加しました。
f:id:PaperSloth:20171224124531p:plain


作成するとこんな感じになります。
f:id:PaperSloth:20171224124753p:plain
もし、反映されていなければ
File > Openから探して開きましょう。

続いて入力イベントを登録していきます。
Input Event Groupの+ボタンを押下します。
f:id:PaperSloth:20171224125548p:plain

MoveX(左右)とMoveY(前後)を登録しました。
LumberyardはZ-upなので、Yが前後になりますね。
f:id:PaperSloth:20171224130043p:plain

続いてMoveX, MoveYにそれぞれどのキーを割り当てるかを登録していきます。
Event Generators > +
f:id:PaperSloth:20171224130729p:plain

Inputクラス作成のダイアログが出るのでOKを押します。
f:id:PaperSloth:20171224130900p:plain

するとEvent Generatorに登録され
Input Device TypeやInput Nameが設定可能になりました。
f:id:PaperSloth:20171224131035p:plain

とりあえずは前後移動のWSキーを登録しました。
左右移動のADキーも同様に追加します。
f:id:PaperSloth:20171225133423p:plain

全て追加したらFile > Saveを選択して保存します。

作成はされましたが、登録はされていないので登録します。
Playerを選択してInput > Input to event bindingsに作成したplayer.inputbindingsを追加します。
f:id:PaperSloth:20171225133839p:plain
f:id:PaperSloth:20171225133932p:plain

一旦はこれでプレイヤー周りのコンポーネントの追加、作成は完了です。
いよいよScript Canvasに入っていきます。


Playerの処理の追加

さぁ、ようやくScript Canvasの記述です。
先ずはScript Canvasを開きましょう。
Tools > Script Canvas(PREVIEW)です。
f:id:PaperSloth:20171225134223p:plain

Script Canvasを開いたらFile > New ScriptかCtrl + Nで新規作成します
f:id:PaperSloth:20171225134311p:plain

一旦ここで保存します。
File > Save(Ctrl + S)かFile > Save as...(Ctrl + Shift + S)です。
Lumberyard/1.12.0.0/dev/(ProjectName)/ScriptCanvasにPlayerController.scriptcanvasで保存しました。
f:id:PaperSloth:20171225134630p:plain


早速処理を書いていきましょう。
操作はUE4のBlueprintを触ったことがある人は入りやすいかもしれません。
先ずは入力の取得から。
Script Canvas上で右クリックし、inputと入力すると下記のようになります。
Input Handlerを選択しましょう。
f:id:PaperSloth:20171225140330p:plain

MoveXとMoveYとLogを下図のように繋いで先ずは入力が取れているか確認します。
f:id:PaperSloth:20171225140638p:plain

playerを選択してAdd ComponentでScript Canvasを追加します。
f:id:PaperSloth:20171225140805p:plain

Script Canvas Assetの参照も追加します。
f:id:PaperSloth:20171225140908p:plain

先程作成したplayercontroller.scriptcanvasを割り当てたら
実行してWASDキーを押下して、WDで1.0、ASで-1.0が出力されることを確認します。
f:id:PaperSloth:20171225141130p:plain


入力が取れていることが確認できたら移動処理を書きます。
移動にはMove Entityノードを使います。
Sourceは移動ターゲット
Directionは移動方向ですね。
f:id:PaperSloth:20171225141835p:plain

ですが、InputのValueとMoveEntityのDirectionは接続できません。
Number型とVector3型でScript Canvasでは自動でキャストしてくれないからですね。
f:id:PaperSloth:20171225142031p:plain

Constructノードを使ってNumberからVector3を生成します。
f:id:PaperSloth:20171225142233p:plain
同様にMoveYも組んでみると上下左右に移動できると思います。
ノードの複製はCtrl + Dで行えます。

これで動かしてみるとすごく高速移動します
f:id:PaperSloth:20171225142602g:plain


続いて速度の補正を加えることにします。
Multiplyノードで0.1をかけ、1/10の速度にします。
f:id:PaperSloth:20171225142946p:plain

どちらも同じ値なのでとりあえず変数にまとめちゃいます。
Create Variable > Numberで作成します。
f:id:PaperSloth:20171225150732p:plain

Speedという名前で保存しました。
f:id:PaperSloth:20171225150810p:plain

これで1/10の速度で動いてだいぶいい感じになりました。
f:id:PaperSloth:20171225161510g:plain

カメラの追加

動画を見ての通り、カメラ処理がないのでカメラを追加していきます。

先ずはカメラの作成から。
Create Entityして、Camera Componentをつけるだけ!
f:id:PaperSloth:20171225153248p:plain

ですが、これだと動かしてもカメラの位置が変わらないので
Playerの子にすることで追従します。
この辺はUnityと同じですね。
f:id:PaperSloth:20171225153637p:plain

また、位置も調整して少し斜め後ろから見下ろすようにしました。
f:id:PaperSloth:20171225153844p:plain

動かすと分かりますが、少し酔いますね。
f:id:PaperSloth:20171225162442g:plain


カメラの追従を緩やかにする。

このままでもカメラは追従しますが、動きが早いと酔ってしまうので緩やかにする追従するようにします。
UnityだとVector3.MoveTowards, UE4だとVInterpToでサクッと実装できますが
Lumberyardでそれに該当する関数が見つけられなかったので、自作していきます。
CameraRig Componentを活用すればもっと楽に実装できる気もします。

CameraにScript Canvasを追加し、新規にCamera Managerという名前で追加しました。
f:id:PaperSloth:20171225170022p:plain

Camera側でPlayerを参照するには
Playerを選択した状態でReference Selected Entityを選択します。
UE4のLevel BlueprintでLevel上のActorを取得する時と同じようなイメージですね。
f:id:PaperSloth:20171225170238p:plain

先ずはCameraとPlayerの距離ベクトルをDistanceという変数を作成し、保存します。
On Graph StartノードはUnityでいうStart, UE4でいうBeginPlayです。
f:id:PaperSloth:20171225183410p:plain

次は長いので、小分けに説明していきます。
OnTickノードはUnityでいうUpdate, UE4でいうTickです。
InterpSpeed, Offsetという変数を作成しました。
InterpSpeedで追従の速度を調整することになります。
f:id:PaperSloth:20171225183752p:plain

次にPlayerの位置に先程のプレイヤーからカメラの距離を加算します。
この値をカメラの座標にセットすると親子階層にした時と同様に固定距離でべったり貼り付くカメラになりますね。
f:id:PaperSloth:20171225184153p:plain

計算がもう少しだけ続きます。
Playerにカメラとの距離を加算した値からカメラの位置を引きます。
その値に先程求めたOffsetを掛けます。
f:id:PaperSloth:20171225185644p:plain

最後にその値をカメラ座標に足してやれば完成です。
f:id:PaperSloth:20171225185842p:plain

全体像が見えるように折り返して写しました。
中でやってる計算はUE4のVInterpToの簡素なバージョンです。
f:id:PaperSloth:20171225190035p:plain

また、このままでは正常に動作しないため
CameraをPlayerの子階層から外しておきましょう。
f:id:PaperSloth:20171225190430p:plain

計算が多いのはノードエディタと相性が悪いため、LuaC++で書いた方がいいですね。
関数の作成方法や独自のノードの作成方法が分かればリファクタリングしたいところです。

とりあえず動きとしてはよくなってきました。
f:id:PaperSloth:20171225190743g:plain


弾を撃つ

続いて攻撃処理を作っていきます。
移動できて、弾が撃てて敵が倒せればゲームが作れそうですよね。

先ずは弾となるEntityを作成します。
Create Entityをし、Projectileと命名しました。
f:id:PaperSloth:20171225200422p:plain

Meshを追加し、SampleProject > Objects > default > primitive_sphereを使用します。
サイズも大きいので、Scaleを0.2にしておきます。
f:id:PaperSloth:20171225200628p:plain

次にコリジョンを追加します。
Add Component > Rigid Body Physics, Primitive Colliderを追加します。
f:id:PaperSloth:20171225220259p:plain

Primitive ColliderのAdd Required Componentから
Sphere Shapeを選択します。
Primitive ColliderのSource Typeにはmat_defaultを設定します。
f:id:PaperSloth:20171225220501p:plain


続いて弾の移動処理を追加します。
ProjectileにScript Canvasを追加して、Projectileという名前のScript Canvasを追加しました。
f:id:PaperSloth:20171225201801p:plain

処理は開始時にY軸方向に100倍した値を速度として渡しています。
因みにこのY軸の値は必ずしも1.0ではありません。
f:id:PaperSloth:20171225220943p:plain

最後に弾の消滅処理を書きます。
DelayとDestroyを使うわけですが、Script Canvasは少しおもしろい書き方ができます。
削除は下図の形でつなぎます。
Delayで3秒後に自身をDestroyします。
f:id:PaperSloth:20171225221314p:plain

Script Canvasでは1つのOutピンから複数の入力ピンにつなげることができます。
UE4ではSequenceノードがその役割を果たしていますね。

弾の処理に関しては一旦これで終了です。


弾をSliceとして登録する。

スライスと動的スライス - Lumberyard
SliceとはUnityでいうところのPrefabです。
UE4ではClass Blueprintが近いですね。
f:id:PaperSloth:20171225221704p:plain

Lumberyard/1.12.0.0/dev/(ProjectName)/slicesに保存しました。
f:id:PaperSloth:20171225221821p:plain

Sliceとして保存するとEntity Outliner上で青く表示されます。
f:id:PaperSloth:20171225221920p:plain


ドキュメントを読めば書いてあるのですがこのスライスは実行時に生成ができません。
そのため、動的スライスとして登録します。
先程作成したSliceをAsset Browser上から探し、右クリックでSet Dynamic Sliceを選択します。
f:id:PaperSloth:20171225222341p:plain

これで動的スライス(DynamicSlice)として登録され、実行時の生成が可能になりました。
実行して、弾が無事に飛んでいくことを確認すれば、Entitiy OutlinerからProjectileを削除します。


プレイヤーから弾を撃つ

弾が登録できたので、次はプレイヤーから撃ちます。

Player EntityにSpawnerを追加します。
f:id:PaperSloth:20171225222741p:plain

Dynamic Sliceに先程登録したProjectileを設定します。
f:id:PaperSloth:20171225222915p:plain


次に発射ボタンの登録を行います。
player.inputbindingsを開き、Fire Eventとしてマウスの左ボタンを追加しました。
f:id:PaperSloth:20171225223108p:plain
この時、保存を忘れないようにしましょう。

PlayerControllerのScript Canvasを開き、先ずはログで入力の確認。
左クリックで-1が出力されれば入力が取得できています。
f:id:PaperSloth:20171225223328p:plain

弾の発射処理はこれだけです。
SpawnがSelfになっていることに違和感を感じた方もいるかもしれませんが、
これはSpawnerに登録されているDynamic SliceをSpawnするという意味なのでこれで問題ありません。
f:id:PaperSloth:20171225224347p:plain


ちょっと見た目を変えたりしておしまい。
f:id:PaperSloth:20171225230436g:plain


まとめ

なんとなくゲームを作る雰囲気が見えてきたでしょうか。

幾つかやり残したことがあって次回の課題としたいと思います。
・カメラの回転
 カメラ追従にこったりせずにこっちを実装すればよかったと今更思います。
・エフェクトの発生
 弾を撃つ時、弾を当てた時のマズルフラッシュと爆発エフェクト
・サウンドの再生
 発射音、ヒット音の再生
・敵の追加
 AIを作るには時間が足りないのですが、当たり判定の解説用に作ればよかったところです。
・UIの作成
 Hit Pointや制限時間などなど
・キャラクターのアニメーション
 これに関しては資料というかサンプルが豊富なので、なんとかなりそうです。
 1.12からシステムが変わったようなので、時間を作って解析したいところ。
・Levelの遷移
 Level遷移は少し資料を探すのが大変そうで難しいかなと思っている点です。
 Forumを探したら意外と直ぐに見つかる気もします。

一先ずゲームを完成させた!というにはまだまだ解説する要素が足りなかったですね。
ですが、Lumberyardは2018年で大きく変わるようですので、本当に楽しみです。

来年のAdvent Calendarをもっともっと盛り上げていければと思います。
最後までお付き合いいただき、ありがとうございました!

LumberyardをUnity, UE4と比較(グラフィック編)

この記事はLumberyard Advent Calendar 2017の19日目の記事です。
Lumberyard Advent Calendar 2017 - Qiita

環境

Unity 5.6.x
Unreal Engine 4.18.1
Lumberyard 1.12.0.0


Unity

Cg、HLSLが利用可能。
シェーダーの種類は頂点シェーダー、フラグメントシェーダー
サーフェイスシェーダー、固定関数シェーダー、コンピュートシェーダー
ジオメトリシェーダー、ハルシェーダー、ドメインシェーダーがある。
種類が多くて大変そう。と思ったかもしれませんが
個人的には頂点、フラグメントさえおさえればいいと思っている。
ComputeShaderの利用例としてはGPU Particleで100万個のパーティクル出したい!とかですね。

また、モバイルはGLSLですがUnity側でCg/HLSLをGLSLにコンパイルしてくれているようです。
さらに条件付きでGLSLの記述も可能なようです。
tips.hecomi.com

公式からUnitychanトゥーンシェーダーが配布されており、アニメ調の表現が誰でも手軽に実現できます。
最近公開されたPhantom KnowledgeはUnityユーザー必見ですね!
unity-chan.com

最近ではShader関連の書籍も出てきました。
Unityシェーダープログラミングの教科書 ShaderLab言語解説編 - 染井吉野ゲームズ - BOOTH
自前のCascade Shadow Maps, Variance Shadow Mapsなんかは
Unityユーザーなら誰しも書いていることでしょう。

また、Shader Forgeを利用することでノードベースのシェーダーが扱えます。
アーティストでも気軽にシェーダーに触れることが出来てオススメです。
https://www.assetstore.unity3d.com/jp/#!/content/14147

何でも出来て最高!と思ったかもしれませんが、1つ困ったことがあります。
幾つかのShaderを自分で用意する必要があります(勿論Assetを買ってもいいです)
Anti-AliasingもBloomもLensFlareもMotionBlurも自分で書かなければいけません。
アセットを購入したりGitHubで落としてきたり、サンプルに含まれていることもありますが
ライセンスの扱いが厳しい場合やそれらに含まれていない場合は自前実装が必要になるため、
Shaderを覚えておくと幸せになれるかもしれません。


Unreal Engine4

HLSL, Material Editorが利用可能。
シェーダーの種類は頂点シェーダー、ピクセルシェーダー、ジオメトリシェーダー
ハルシェーダー、ドメインシェーダー、コンピュートシェーダーがあります。
UE4も標準でPBRに対応しており、デフォルトで様々な画面効果がかかっているため
モデルをインポートして配置しただけでとても綺麗に表示されます。

Material Editorがとても高機能でDecal, PostProcess, UIの種類を簡単に切り替えができ、
Maskや半透明やDither抜きも簡単に表現できます。
質感も豊富で皮膚、髪、目、布、葉など様々です。
モバイルではUnity同様にエンジン側でHLSLをGLSLに変換してくれます。

標準機能だけで大凡の表現が簡単に行え、Material Instanceの利用で
再利用も簡単に行うことが出来ます。
もっと詳しく知りたい人は書籍で一通り学ぶと良さそうです。
Unreal Engine 4 マテリアルデザイン入門[第2版] (GAME DEVELOPER BOOKS) 茄子さん、もんしょさん
Amazon CAPTCHA


続いてHLSLですが、Material Editor内でCustomノードを使うことで利用できるほか
Engine内のusf(Unreal Shader File)内でも定義することができます。
さらに4.17からはGlobal ShaderをPluginとして追加する機能がサポートされました。
Create a New Global Shader as a Plugin | Unreal Engine

UE4といえばリアルな絵が特徴的でしたが今ではアニメ調の絵作りが増えてきましたね。
Airtone, DQ11, Project LayereD, DRAGON BALL FighterZ ...etc

誰でも手軽に機能が追加できるのが魅力的ですね。
さらに何でも出来て最高!と思ったかもしれませんが、1つ困ったことがあります。
ほんとに誰でもルールなく追加するとメモリ、処理負荷共に大変なことになります。
ですので、適切にリーダーを立ててワークフローを構築した方が良さそうです。


Lumberyard

前置きが長くなりました。いよいよLumberyardです。
HLSL, DirectX FX(コンパイル済みエフェクトファイル)と似た形式が利用可能。
標準で豊富なシェーダーが用意されており
リアルな絵作りにおいてはカスタムシェーダーがほとんど必要ないかもしれません。
http://docs.aws.amazon.com/ja_jp/lumberyard/latest/userguide/shader-ref-intro.html

Lumberyardは2017年2月末の時点でCrytekのコードは約50%になっていました。
さらに10ヶ月が過ぎた現在では多くが新機能になったと思われます。

Lumberyardはリアルタイムレンダリングなので、Lightmapのベイクの必要がありません。
反射から、天候の変化まで簡単に行えます。
その分エディターが重いかというとそんなことも全くありません。
さらにLumberyardはSpeedTreeが無料で利用可能なのも魅力的です。


続いてLumberyardの特徴的で高度なグラフィックス技術を幾つか紹介します。

・Specular Anti-Aliasing
スペキュラのハイライト部分がカメラ移動に合わせてちらつくのを抑えている。
www.youtube.com

・Order Independent Transparency(順不同の半透明描画)
www.youtube.com


UE4が実装を諦めたVoxel-based Global Illuminationが搭載されている。
docs.aws.amazon.com

・Fur Shaderを標準サポート(1.12から利用出来ます)
f:id:PaperSloth:20171218212144p:plain
UnityやUE4でもAssetとして購入したり自前で作ることは勿論可能ですが
Lumberyardは標準でサポートされており、高品質です。

2017/12/20追記
・ToonShaderも公式でサポート!
docs.aws.amazon.com

まとめ

Lumberyardのグラフィックスはリアルな絵作りにおいてかなり技術的にも進んでいます。
グラフィックの目玉はVoxel-based Global Illuminationでしょう。
標準で高機能なシェーダーが揃っているのでちょっとマテリアルで遊ぶだけでも楽しいです。
f:id:PaperSloth:20171218212551p:plain

あと、Unitychanみたいなかわいいキャラが出てくると日本でももっと触ってもらえそうだと感じました。
個人的にはFurShaderのくまちゃんとロゴマークの木こりが好きです。

少しでも興味が湧いた人は早速ダウンロードして触れてみてください!

LumberyardをUnity, UE4と比較(プログラミング編)

この記事はLumberyard Advent Calendar 2017の17日目の記事です。
Lumberyard Advent Calendar 2017 - Qiita

環境

Unity 5.6.x
Unreal Engine 4.18.1
Lumberyard 1.11.1.0


Unity

プログラミング初心者でもコンポーネントの付け替えで動きが作れるため初めてゲームを作るのに適していると感じる。
特に設計を考えるにあたっては勉強になる部分が多く初めてのオブジェクト思考にもってこいだと思う。

C#に特化することの最大のメリットはクライアント、サーバー、エディタ拡張まで同一言語で作成する環境が作れること。
サーバーまでC#でやるかどうかはプロジェクトや会社によって異なるだろうが、
同じ言語に統一することでクライアントサイドのプログラマーでも比較的サーバーサイドが理解できる。

アセットを買うことでノードベースのビジュアルスクリプトも使用できる。
ただし、これだけでゲームを完成させようとするよりは
Prefab単位でのキャラクターのチェックなど
アーティストがちょっと動きを試してみたりする程度に抑えるのが良さそう。


Unreal Engine4

みんな大好きC++が書ける。
と、言いたいところだがUnreal C++なのでガチガチのC++erには敬遠されている。
実際に使ってみると分かるように制約やルールが多い。それを守れば問題なく扱える。
寧ろ自前実装していた部分の大半を用意してくれているため、ゲームロジックの実装に注力できる。

もう一つ忘れてはいけないのがBlueprint
エンジンの大凡の機能にアクセスできるため、基本的にはBlueprintだけでゲームが作れる。
チームの構成やゲーム規模によるものの両方を上手く扱うのがベストな場合が多い。
もちろん好みや得手不得手もあるため、Unreal C++を強制する必要はない。

BlueprintでもUnreal C++でもエディターの拡張が可能なのが嬉しいところ。
また、一番の魅力はエンジンコードの閲覧、編集が可能なこと。
エンジン改造もプラグイン開発もC++, Unreal C++で比較的簡単に行える。


Lumberyard

Lua, ScriptCanvas, C++が選べる。

Lua:簡単なScriptの記述が中心。
コンポーネントとしてくっつけるのでUnityのC#に近いイメージ。
まだ試せていないがC++との連携も行えるようなので自由度は高そうだ。
入門記事はさらっと書いてみたので、興味があれば見ていただきたい。
papersloth.hatenablog.com


ScriptCanvas:CryEngineのFlowGraphが廃止され、1.11から導入された。
UE4のBlueprintほど多機能ではないが、これからの開発がどうなるか次第。
Lumberyard Advent Calendarで入門記事を書いてくださった方がいるので
興味がある方はこちらを見てもらいたい。
tachis.hatenablog.com


C++Unreal C++に比べると制約が少なく比較的自由に書ける。
f:id:PaperSloth:20171217184317p:plain
AZFrameworkでエンジン部分との連携を行いEBusでイベントの登録を行う。
Lumberyard側で用意したCastや文字列等覚えることは幾つかあるが
これはどのエンジンを使ってもエンジンのFrameworkを活用することになるので覚えてしまえば問題なさそうだ。
C++についても簡単な入門記事を書いたので、興味があれば見ていただきたい。
papersloth.hatenablog.com

LumberyardはUE4同様にエンジンコードへのアクセスが可能でライセンス的にも改変自体は問題がない。
LumberyardはLua, ScriptCanvas, C++の他にエンジンの一部にPython2.7も使用されている。
ScriptCanvasはLuaやC ++と同じフレームワークを利用しているため今後アクセス可能な機能がどんどん増えると思われる。


まとめ

Lumberyardでちょっと動きを試してみたいという人はScriptCanvasが良さそう。
Luaに触れた経験があって得意だという人はもちろんLuaがオススメだ。

C++上記2つに比べるとエンジンとの連携部分の記述等の準備が少し面倒に感じた。
しかし、エンジンを上手く活用するためには決して避けて通れないだろう。

ScriptCanvasはBlueprintと比較するとまだまだ機能が足りず
LuaはUnity C#ほどの自由度はない。
しかし、UnityやUE4と比較して純粋なC++が書きやすいという環境を好む人も多いだろう。

Lumberyardは1.12が公開され、2018年の開発に本腰を入れるようなので
公開されて新しいScriptCanvasの成長には特に期待している。

UE4 Editorから外部アプリケーションの実行

この記事はUnreal Engine 4 (UE4) その2 Advent Calendar 2017の17日目の記事です。
Unreal Engine 4 (UE4) Advent Calendar 2017 - Qiita

環境

Visual Studio Community 2015
・Unreal Engine4.17.2
・Windows10

コードの記述

Blueprint Function Libraryから呼び出せる形にしています。
以下コード

header

// Copyright(c) 2016-2017 PaperSloth

#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "ExampleFunctionLibrary.generated.h"

UCLASS()
class PROJECT_API UExampleFunctionLibrary : public UBlueprintFunctionLibrary
{
	GENERATED_BODY()
public:
	UFUNCTION(BlueprintCallable, Category = "Editor")
	static void CreateProcess(const FString& path, const FString& args = TEXT(""));
};

cpp

// Copyright(c) 2016-2017 PaperSloth

#include "ExampleFunctionLibrary.h"

void UExampleFunctionLibrary::CreateProcess(const FString& path, const FString& args)
{
	FPlatformProcess::CreateProc(*path, *args, true, false, false, nullptr, 0, nullptr, nullptr);
}

実装としてはFPlatformProcess::CreateProcを呼び出すだけのシンプルな実装です。
WindowsPCの場合はFWindowsPlatformProcess::CreateProcを呼んでいるのと同じです。

他にもExecProcessがあり、引数がシンプルなのですがこちらは使用しません。
関数の中を見るとどちらもWinAPIのCreateProcess関数を呼び出していることがわかります。
MSDN - Create Process関数
https://msdn.microsoft.com/ja-jp/library/cc429066.aspx?f=255&MSPPError=-2147217396

詳細が知りたい方はぜひとも内部実装を覗いてみてください。
Engine\Source\Runtime\Core\Private\Windows\WindowsPlatformProcess.cpp

ExecProcessを使用しない理由は簡単です。
関数内でWaitForSingleObjectが呼び出されており
起動したプロセスが終了するまでUE4 Editorが編集できなくなるからです。

::WaitForSingleObject(ProcInfo.hProcess, INFINITE);

Editorからの実行

簡単にEditor拡張を行うために、Blutilityを使用します。

先ずはBlutilityを使用できるように設定を有効にします。
エディターを再起動する必要はありません。
f:id:PaperSloth:20171125142636p:plain

Blutilityが有効になっていれば、下図のようにBlueprintタブから作成ができます。
f:id:PaperSloth:20171125142857p:plain

Blutilityを使用する場合は基底クラスに「GlobalEditorUtilityBase」を選択します。
f:id:PaperSloth:20171125143000p:plain

続いてBlueprintの編集です。
適当なCustomEventを作成し、Call In Editorにチェックを入れましょう。
f:id:PaperSloth:20171125143250p:plain

先程作成した関数を呼び出します。
Path:実行したいアプリケーション(exe)のパス
Args:アプリケーション起動時の引数
f:id:PaperSloth:20171125144407p:plain

PathとArgsに変数を渡します。
CustomEventの引数にした場合はBlutilityから呼び出せないため
変数を作成し、Instance Editableにチェックを入れます。
これで実行時に変数を編集できるようになります。
f:id:PaperSloth:20171125202711p:plain

実行画面は下記のようになります。
今回はDirectX診断ツールを起動してみました。
こちらは環境変数に既にパスが通っているため、これだけで呼び出しが可能です。
f:id:PaperSloth:20171125203021p:plain

問題点

さて、このBlutilty拡張なのですが幾つか問題点があります。
・batが起動できない
 これはCreateProcessの仕様です。
 ドキュメントを見てみると
 "To run a batch file, you must start the command interpreter;
  set lpApplicationName to cmd.exe and set lpCommandLine to the following arguments: /c plus the name of the batch file."
 とあります。コマンドプロンプトを立ち上げて引数にパスを指定することで起動できるようです。
CreateProcess function (Windows)

・幾つか起動しないアプリケーションがある
 例:cmd.exe等
 さて、先程のbatが起動できない問題はcmdに引数を渡すだけで実行できそうです。
 しかし、ここで問題発生。cmd.exeを起動できませんでした。
 これについては原因不明で特定に至っていません。

 この問題で何か分かる方がいればコメントかTwitterからアドバイスをいただけると幸いです。

まとめ

・Blutilityでのエディタ拡張は簡単
 SlateやPlugin、エンジン拡張に比べると影響範囲も少なく簡単に機能の追加、削除ができます。

・外部のアプリケーションの呼び出しも簡単
 関数1つを呼び出すだけなので、直ぐに実装できる。
 但し、上記で挙げたような問題に直面しています。

・FPathsとの組み合わせでより使いやすく出来る
 FPathsを使用すれば、エンジンのディレクトリやプロジェクトのディレクトリを簡単に取得できます。
 そのため、Plugin特有の機能やProject特有の機能をVCSにアップしておいてプロジェクトメンバー全員が同じパスで利用することができます。
 エンジン内での利用方法もFPathsとの組み合わせでパスを結合して利用しています。
 FPaths - API Reference
 FPaths | Unreal Engine

LumberyardをUnity, UE4と比較(エディタ編)

この記事はLumberyard Advent Calendar 2017の15日目の記事です。
Lumberyard Advent Calendar 2017 - Qiita

環境

Unity 5.6.x
Unreal Engine 4.18.1
Lumberyard 1.11.1.0

概要

これまでに仕事、趣味でUnity, UE4にそれぞれ2, 3年程触れてきました。
Lumberyardをちゃんと触り始めたのは今月からです。
2週間ほど触ってみたけど、Lumberyardはどうか。という話を書いていきます。
※Unityは最後に触れたのが5.6系のため、2017系の新機能を終えていない部分があります。
今回はそれぞれのEditor画面について。


Unity

Scene, Gameが分かれていてエディタ環境と実行環境を同時に見れるのが便利。
何よりエディタ拡張がかなり簡単にでき、C#の機能と組み合わせることで
外部アプリケーションとの連携もかなり簡単に実現できる。
簡単に拡張ができるが故にプロジェクトごとにUnityの皮を被った何かになることもある。

よく作る拡張機能としては下記の2つ。
・外部VCSとの連携
qiita.com
・エディタメニュのカスタマイズ(追加したり削除したり)
あとはプロジェクトごとのcsv読み込み、書き出しやAnimator拡張とかとか。
エディタ拡張を行った際にショートカットキーも直ぐに登録できます。
とにかく拡張しやすいのが魅力!


Unreal Engine4

特筆すべきは日本語環境があること。
f:id:PaperSloth:20171214231546p:plain
エディタ環境といえばDCCツールもゲームエンジンも英語が中心。
日本語でエディタが見れるのは安心感がある人も多いはず。
カメラ操作も豊富でカメラ移動速度も調整しやすく、エディタ操作がかなりしやすい。
機能が豊富なため、最初は戸惑うがひとつひとつの機能が強力で便利なものが多い。

エディタ拡張はC++の知識とSlateの知識を身につければなんとかなる。
もしくはBlutilityで誰でもサクッとお手軽拡張が出来る。
また、ショートカットのカスタマイズが出来るので拡張とまではいかなくても簡単に操作変更ができる。
Editor Preferences > Keyboard Shortcuts
f:id:PaperSloth:20171214231923p:plain


Lumberyard

エディタの実行、カメラ操作等がUnity, UE4とも異なるショートカットが多い。
操作系を含めてCRYENGINEから引き継いでいる機能もいくつか見受けられる。
f:id:PaperSloth:20171214232550p:plain
初期のUE4ほどはUIが大きく変わることは少ないと思うが、
CRYENGINEの機能を消したりLumberyardの機能を増やしたりで今後メニューは変わっていきそう。
最初こそ慣れが必要だが、他のエンジンを触った人は大きく苦労することもなさそう。
使いにくいという意見を見かけることもあるが、慣れの問題というレベル。

エディタで魅力を感じたのはVolumeの選択のしやすさ。
コリジョン等を選択する時にその範囲内をクリックすると選択できるので選択しやすい。

また、最初は画面に様々な情報が出ているが
右上の"i"ボタンを押下すると表示情報の切替ができる。
Editorに出ている選択状態を示す情報などは右上の"?"ボタンで表示、非表示の切替が可能になっている。

エディタ拡張についてはまだ行ったことがないため、実装コストは分からないが
Gem(Plugin)が幾つかあり、全てSourceCodeにアクセス可能なのでなんとでもなりそうな気はする。
Componentの追加に関しては手順は多いが比較的簡単で下記を参考にしてみてもらいたい。
Lumberyard C++入門 - PaperSloth’s diary


まとめ

Lumberyardはどうなのか?エディタ編に関して。
Unity程エディタ拡張が容易といったことはないが、操作は慣れの問題で大きくは困らない。
これからCRYENGINEの機能がLumberyardの機能に置き換えられていくことが予想される。
しかし、消える予定の機能はLegacy, 実験段階の機能はPreviewといった表記があるため
いきなり機能が消えてしまって困った!という事態になることは少なそう。
これからどんな機能が追加されていくのか成長が楽しみである。

Lumberyard C++入門

この記事はLumberyard Advent Calendar 2017の11日目の記事です。
Lumberyard Advent Calendar 2017 - Qiita

概要

今回作るもの
C++で新規のコンポーネントを作成
コンポーネントのビルド、登録まで行います。
コンポーネントの中身は空なので、ログを出したり
更新処理や当たり判定を取ったりはまだやっていません。


環境

Lumberyard 1.10.0.0
Visual Studio Community2015


Solutionファイルの生成

新規プロジェクトを作成すると下記にソリューションファイルも生成されます。
Lumberyard\(EngineVersion)\dev\Solutions\(ProjectName)

プロジェクトの作成方法についてはこちらを参照ください。
papersloth.hatenablog.com


さて、ソリューションエクスプローラーを見てみると
ソリューション(ProjectName)
の下に幾つかフォルダが並んでいて、その中に自分のProject名のフォルダもありますね。
今回はCPPProjectという名前で作成しました。
f:id:PaperSloth:20171208003814p:plain

ソースコードの追加

続いて、コードを追加していきましょう。

先ず、ソースコードは下記に保存されています。
Lumberyard\1.10.0.0\dev\(ProjectName)\Gem\Code\Source

今回はここにComponentsというフォルダを追加しました。
f:id:PaperSloth:20171209134725p:plain


ソースコードを追加する場所はProject名のフォルダの下のProjectファイルの中です。
f:id:PaperSloth:20171208004431p:plain

UnityやUE4だとエディターから追加すると思いますが、Lunberyardは通常のC++ソースコードの追加方法になります。
先ずはHeaderの追加
保存場所は先程追加したComponents以下を指定します。
f:id:PaperSloth:20171209135341p:plain


ドキュメントを見ながらそれっぽいコードを書いていきます。
コンポーネントの作成 - Lumberyard

ExampleComponent.h

#pragma once
#include <AzCore/Component/Component.h>

namespace LYGame
{
	class ExampleComponent : public AZ::Component
	{
	public:
		AZ_COMPONENT(ExampleComponent, "{9D793D70-7616-4936-A45F-F921C4B4985A}");

		static void Reflect(AZ::ReflectContext* context);

		void Init() override;
		void Activate() override;
		void Deactivate() override;

	private:
		AZStd::string exampleProperty;
	};
}

ちょっと特殊なのが、AZ_COMPONENTの第2引数ですね。
一意の UUIDを入れる必要があるようです。
Visual Studioのツールから作成して設定しておきます。


続いてHeader同様にcppファイルも書いていきます。
ExampleComponent.cpp

#include "StdAfx.h"
#include "ExampleComponent.h"
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>

namespace LYGame
{
	void ExampleComponent::Reflect(AZ::ReflectContext* context)
	{
		AZ::SerializeContext* const serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
		if (serializeContext == nullptr)
		{
			return;
		}
		serializeContext->Class<ExampleComponent>()->Version(1)->Field("ExampleProperty", &ExampleComponent::exampleProperty);

		AZ::EditContext* const editContext = serializeContext->GetEditContext();
		if (editContext == nullptr)
		{
			return;
		}


		editContext->Class<ExampleComponent>("ExampleComponent", "Example Component Description")
			->ClassElement(AZ::Edit::ClassElements::EditorData, "")
			->Attribute(AZ::Edit::Attributes::Category, "ExampleCategoryName")
			->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game"))
			->DataElement(0, &ExampleComponent::exampleProperty, "ExampleProperty", "Example Property Description");
	}

	void ExampleComponent::Init()
	{
	}

	void ExampleComponent::Activate()
	{
	}

	void ExampleComponent::Deactivate()
	{
	}
}


Init, Activate, Deactivateは先程のドキュメントを見てもらえればわかりますが
Initだけ軽く説明をこちらにも書いておきます。

Entity(Unity:GameObject, UE4:Actor or UObject)に対して一度だけ呼び出されます。
ComponentはすぐにActiveにならないことがあるため、ここで最小限のセットアップを行います。
コンストラクタ(ctor)に近いイメージですね(Unity:Awake, UE4:ctor)

処理を長々とReflectに書きました。Reflectについてはこちら。
シリアル化と編集のためのコンポーネントのリフレクション - Lumberyard

この辺りは自分もお作法として書いてるところですので、詳しい解説はまだできそうにないです。
詳しく知りたい方はドキュメントを読んでいただければと思います。

ビルドシステムへの通知

続いて、Wafビルドシステムへの通知を行います。
Waf ビルドシステム - Lumberyard

Lumberyardのビルドシステムにコンポーネントの追加を知らせる処理を書きます。
(ProjectName).waf_filesを開き
f:id:PaperSloth:20171209222009p:plain

下記のように記述します。

{
    "none": {
        "Source": [
            "Source/StdAfx.cpp",
            "Source/StdAfx.h"
        ]
    },
    "auto": {
        "Include": [
            "Include/CPPProject/CPPProjectBus.h"
        ],
        "Source": [
            "Source/CPPProjectModule.cpp",
            "Source/CPPProjectSystemComponent.cpp",
            "Source/CPPProjectSystemComponent.h"
        ],
        "Source/Core": [
            "Source/Core/EditorGame.cpp",
            "Source/Core/EditorGame.h",
            "Source/Core/CPPProjectGame.cpp",
            "Source/Core/CPPProjectGame.h",
            "Source/Core/CPPProjectGameRules.cpp",
            "Source/Core/CPPProjectGameRules.h"
        ],
        // 追加部分
        "Source/Components": [
            "Source/Components/ExampleComponent.cpp",
            "Source/Components/ExampleComponent.h"
        ],
        // 追加終了
        "Source/Game": [
            "Source/Game/Actor.cpp",
            "Source/Game/Actor.h"
        ],
        "Source/System": [
            "Source/System/GameStartup.cpp",
            "Source/System/GameStartup.h"
        ]
    }
}

コンポーネントの登録

続いてコンポーネントの登録を行います。
登録するには先ず
Source\Core\(ProjectName)Game.cppを開きます。
f:id:PaperSloth:20171209223712p:plain

全部をまるっと載せるのかなりの行数になってしまうため、変更点だけ載せます。
includeの追加

#include <AzCore/Component/ComponentApplicationBus.h>
#include "Components/ExampleComponent.h"


CompleteInitへの追加

bool CPPProjectGame::CompleteInit()
{
    // EBUS_EVENTを追加
    EBUS_EVENT(AZ::ComponentApplicationBus, RegisterComponentDescriptor, LYGame::ExampleComponent::CreateDescriptor());
    return true;
}

動作確認

エンジンをビルドしてEditorを立ち上げます。

ソースコードからEditorを立ち上げる場合はSandbox/Editorを使用します。
f:id:PaperSloth:20171210022017p:plain

Sandbox/Editorをスタートアッププロジェクトに設定し
[All] Debugで実行してみましょう。
f:id:PaperSloth:20171210022203p:plain

エラーがなければ、エディターが立ち上がります。


Level, Entityの追加方法等はこちらを参考にしてみてください。
papersloth.hatenablog.com


無事にコードが反映されていればAdd Componentから"Example Component"が選択できます。
f:id:PaperSloth:20171210022328p:plain

f:id:PaperSloth:20171210022445p:plain

今回はここまで。以上となります。

バージョンによってはソースコードの場所やソリューションの構成まで少し変わっているところがあるようです。
ドキュメントやチュートリアルを見る時はバージョンが同じか、違っていても同じような機能かを確認しながら作成するとなんとかなりそうです。