【UEFN】フォートナイトのダメージイベントをVerseで制御する (Verseの学習⑦)
UEFNのVerseにおけるイベント処理
UEFNのVerseでは、ゲーム内で発生したイベント(事象)に対する処理を登録しておくことで、イベント発生時に独自の処理を行うことができます。
登録可能なイベントは現状あまり多くはありませんが、その中に、プレイヤーがダメージを受けるということがイベントとして準備されており、プレイヤーがダメージを受けた際に独自の処理を付け加えることが可能になっています。
イベント処理は現在のVerseによるUEFN開発のもっともコアとなる部分です。
今回は、このプレイヤーがダメージを受けるというイベントに対して独自の処理を追加する方法を説明していきます。
1. ダメージイベントに対するOnDamagedEvent関数の登録
では、さっそく制作を開始していきます。
Verseによるクリエイティブデバイスを一つ作成し、OnBegin部分を以下のように記述します。
OnBegin<override>()<suspends>:void= for(Player : GetPlayspace().GetPlayers()): if(FortniteCharacter : fort_character = Player.GetFortCharacter[]): FortniteCharacter.DamagedEvent().Subscribe(OnDamagedEvent) return
流れとしては、
プレイスペースにいるすべてのプレイヤーを取得
プレイヤーがFortniteCharacterを保有しているかどうか判断
保有している場合は、そのFortniteCharacterに対してOnDamagedEventを登録(Subscribe)
となっています。
このSubscribeがイベントを登録するための関数となっており、カッコの中に登録したい関数名を記載します。
今回の例では、OnDamagedEventがこの後に整備するイベントが発生したときに自動的に実行される関数です。
(※この段階では、まだOnDamagedEvent関数を準備していないため、赤色の波線でエラー表示されています。無視してください)
これで、ゲーム開始時に、存在するすべてのプレイヤーに対して、ダメージを受けた際に、"OnDamagedEvent" が実行されるように登録されたことになります。
2. OnDamagedEvent関数の基本形
次に、OnDamage関数を以下のように準備します。
OnDamagedEvent(DamageResult : damage_result) : void = Print("Damaged") return
関数名自体は、Subscribe部分と一致していれば、この名前でなくても問題ありません。
注意点として、DamagedEventに登録する関数は、damage_result型の引数を与えておく必要があります。
この段階で一度実行してみます。
プレイヤーがダメージを受けたタイミングで”Damaged”と画面に記載されれば成功です。
3. damage_resultを使った処理の変更
damage_result型に関して
DamagedEventは、どういった形でダメージが与えられたかという情報をdamage_result変数の中にいれて、登録した関数が実行した際に使用できるようにしてくれています。 damage_resultのデータ構成は以下のようになっています。
ダメージを受けた人(Target)、ダメージ量(Amount)、ダメージを起こした人(Instigator)、ダメージを起こしたソース(Source)の情報が取得できるようです。
InstigatorとSourceは、前に「?」マークが付いており、オプション型であることを意味しています。
ダメージを受けた理由が、環境による場合やVerseから直接与えられた場合などがあり、直接的に該当するものが存在しない可能性があるため、オプション型となっています。
ダメージを受けた、与えたプレイヤーの取得
こうした処理では、ダメージを受けた、与えたのがどのプレイヤーか判断し、その情報の取得をしたい場合があります。 例として、ダメージを受けた対象がプレイヤーかどうかを判断し、プレイヤーであればその位置情報を取得してみます。
その場合のコードは以下のようになります。
### get damaged character position var DamagedCharacterPosition : ?vector3 = false if(DamagedPlayer : fort_character = fort_character[DamageResult.Target]): set DamagedCharacterPosition = option{DamagedPlayer.GetTransform().Translation}
damage_resultのTargetがキャラクターなのかを判断し、キャラクターであれば、その位置情報を保存しています。
少しわかりづらいのは以下の部分ではないでしょうか?
fort_character[DamageResult.Target]
damage_resultのTargetはdamagable型となっています。
なぜ、こんなことができるのでしょうか?
これは、プログラム的にいうと、damagable型からfort_character型へのキャストを試みているコードになります。
プログラムに不慣れな方は、
「きゃ、キャスト・・・???」
となってしまうかと思いますので、簡単にキャストを説明します。
例として、fort_characterで、説明していきます。
fort_characterをFortnite.digest.verseで参照すると以下のようになっています。
細かな部分は説明を省きますが、ざっくりと説明すると、
fort_characterは(positional、 healable、 healthful、 damageable、 shieldable、 game_action_instigator、 game_action_causer)という7個の機能からできている
ということを意味しています。
positionalは位置取得をすることができる機能、damageableはダメージを受けることができる機能といった感じで考えてください。
damageableという機能をもったものは他にも存在し、たとえば乗り物(fort_vehicle)は以下のようになっています。
DamagedEventはdamageable機能で実装されているイベントであり、damageable機能を内部的にもっているものであれば、なんでもDamagedEventを発生することができます。
そのため、damage_resultでは、あくまでも「damageableの機能を持った何かだよ」という情報しか提供していません。
そこで、プログラマー側が、「あなたは、damageable機能をもった何かだそうですが、fort_characterとして扱っていいでしょうか?」という確認をとらなくてはなりません。
この行為がキャストになります。
これは、当然失敗することが考えられるため、ifのブロックの中で実行されています。
Verseのキャストに関しては、土屋つかささんがブログで詳細に説明してくれいますので、ぜひそちらも参考にされるといいかと思います。
同様に、ダメージを与えた側の位置情報は以下のように取得できます。
### get attacker var AttackerPosition : ?vector3 = false if(attacker : fort_character = fort_character[DamageResult.Instigator?]): set AttackerPosition = option{attacker.GetTransform().Translation}
Instigatorはオプション型のため、「?」で値を取得している部分だけ差異がありますが、あとは同様のコードとなります。
完成コード
今回のコードをまとめると以下のようになります。
using { /Fortnite.com/Devices } using { /Fortnite.com/Characters } using { /Fortnite.com/Game } using { /Verse.org/Simulation } using { /UnrealEngine.com/Temporary/Diagnostics } using { /UnrealEngine.com/Temporary/SpatialMath } damage_process_manager_device_for_blog := class(creative_device): OnBegin<override>()<suspends>:void= for(Player : GetPlayspace().GetPlayers()): if(FortniteCharacter : fort_character = Player.GetFortCharacter[]): FortniteCharacter.DamagedEvent().Subscribe(OnDamagedEvent) return OnDamagedEvent(DamageResult : damage_result) : void = Print("{DamageResult.Amount}") ### get damaged character position var DamagedCharacterPosition : ?vector3 = false if(DamagedPlayer : fort_character = fort_character[DamageResult.Target]): set DamagedCharacterPosition = option{DamagedPlayer.GetTransform().Translation} ### get attacker var AttackerPosition : ?vector3 = false if(attacker : fort_character = fort_character[DamageResult.Instigator?]): set AttackerPosition = option{attacker.GetTransform().Translation} var StartPosition : vector3 = vector3{} var EndPosition : vector3 = vector3{} if((set StartPosition = AttackerPosition?) and (set EndPosition = DamagedCharacterPosition?)): spawn: DoSomething(StartPosition, EndPosition) return DoSomething(StartPosition : vector3 , EndPosition : vector3)<suspends> : void = #Do something here return
あとは、DoSomething部分などに、独自の処理を書いていくことになります。 自分の作例では、DoSomething部分で、VFXの爆発を発生させる処理を書いています。
まとめ
今回は、DamagedEventを例に、Eventを登録し、その情報を使用して制御を変更する流れを説明しました。
現状、Verseで制御できるEventはそれほど多くはありませんが、今後増えていくことが予想されます。
他のイベントも今回と同様の手法で制御できると思います。
今回、VisualStudioCodeの中に自動生成されている、Digestファイルの中身を参考にしながらコーディングを行いまいした。
実は、機能構成などは、このDigestファイルが一番参考になるかと思います。(オンラインドキュメントは現状そのあたりが不足している)
Verseにある程度なれたら、Digestファイルを一度見てみることをお勧めします。
RingoGamesではUEFNに関するさまざまな情報を発信していきます。Twitterでお知らせしていきますので、よろしければ、Twitterのフォローをしていただけると幸いです。
Twitterはこちら
基本的な検証が大枠できたので、自分もついにリリースするためのゲームを作り始めました:)
頑張って6月までにはリリースしたいです。
【UEFN】フォートナイトクリエイティブで自作モデルをVerseから動的生成する (Verseの学習⑥)
フォートナイト24.30で自作モデルのスポーンが可能になった!
遂に、遂に、5月2日に更新された24.30で、Verseから自作モデルのスポーンが可能になりました!
といっても、
「はて、スポーンってなんじゃら?」
という方もいらっしゃるかと思いますので、簡単に説明しておきます。
今までのUEFNでは、コンテンツの作成時、レベルに必要なプロップ(小物、アイテム)を、コンテンツビューアーからレベル上にドラッグアンドドロップして、レベルを作成していきます。
100個、必要なプロップがあれば、100個ドラッグアンドドロップしてレベル上に配置することになります。
アーティスティックに作る場合はこの方法で問題ないのですが、レベルの進行状況に応じて表示するための小物などに関しても、同様な手段を取らねばならず、レベルが進行したら表示したいコインなどといったプロップに関しても、「最初は非表示にしておき、必要な時に表示する」、みたいな仕組みを入れていくことになります。
「め、めんどくさい・・・・」
ってなります・・・。
これをプログラム上から可能にする機能がスポーン(生成)です。
先ほどのドラッグアンドドロップのような操作が、Verseから可能になり、
ゲームの進行状況に合わせて、必要な時にスポーン、不要になったら削除、みたいなことが可能になります。
このスポーン機能、Fortnite専用プロップに対しての機能自体は当初からあったのですが、自作モデル(カスタムプロップ)はスポーンすることができていませんでした。
それが、なんと、24.30で可能になりました! ありがとうEpicさん!
今回は、その機能を用いて、プロップをスポーンするためのVerseプログラムを紹介していきます。
一つリンゴをゲーム開始時にSpawnするVerseプログラム
カスタムプロップをスポーンする簡単なプログラムは以下のような形になります。
using { /Fortnite.com/Devices } using { /Verse.org/Simulation } using { /UnrealEngine.com/Temporary/Diagnostics } using { /UnrealEngine.com/Temporary/SpatialMath } ### "Ringo" means "apple" in Japanese!!! #### ringo_spawner_device := class(creative_device): @editable RingoAsset : creative_prop_asset = DefaultCreativePropAsset # Runs when the device is started in a running game OnBegin<override>()<suspends>:void= SpawnPoint : vector3 = vector3 : X := 100.0 Y := 100.0 Z := 100.0 SpawnRotation : rotation = MakeRotationFromYawPitchRollDegrees(0.0, 0.0, 0.0) SpawnProp(RingoAsset, SpawnPoint, SpawnRotation) return
注目するのはまず以下の行です。
@editable RingoAsset : creative_prop_asset = DefaultCreativePropAsset
Verseの中では、コンテンツブラウザー上のプロップ素材が、creative_prop_asset、レベルに配置したプロップが、creative_propということになります。
このコードをコンパイルしてcreative_deviceを画面上に配置すると、creative_deviceの詳細パネルで、スポーンするためのアセットが選択できるようになります。そこにあらかじめ用意した自作のcreative_propを設定してやります。
自作のcreative_propの作り方は以下のページなどを参考にしてください。
OnBegin時、以下の部分でカスタムプロップが生成されます。
SpawnProp(RingoAsset, SpawnPoint, SpawnRotation)
これで、SpawnPointの位置に、SpawnRotationの角度でRingoが生成されます。
無限にリンゴを発生し続けるVerseプログラム
では、次に以下のようなプログラムを書いてみましょう
- 1秒ごとにリンゴを作成し続ける
- 作成したリンゴは、2秒間かけて上昇していく
- 上昇したリンゴは削除される
このコードは以下のようになります。
### "Ringo" means apple in Japanese!!! #### OnBegin<override>()<suspends>:void= loop: ## spawn RingoFunction spawn{SpawnAndMoveAndDisposeRingo()} Sleep(1.0) return SpawnAndMoveAndDisposeRingo()<suspends> : void = SpawnPoint : vector3 = vector3 : X := 100.0 Y := 100.0 Z := 100.0 EndPoint : vector3 = vector3 : X := 100.0 Y := 100.0 Z := 600.0 SpawnRotation : rotation = MakeRotationFromYawPitchRollDegrees(0.0, 0.0, 0.0) var SpawnedProp : ?creative_prop = SpawnProp(RingoAsset, SpawnPoint, SpawnRotation)(0) if: Ringo : creative_prop = SpawnedProp? then: Ringo.MoveTo(EndPoint, SpawnRotation, 2.0) Ringo.Dispose()
簡単に説明していきます。
1秒間隔のループを回し、その中で、並列処理の関数を駆動しています。
spawn{SpawnAndMoveAndDisposeRingo()}
紛らわしいですが、最初のspawnは並列処理を開始するためのspawn式(※関数ではない)、{}で囲まれた部分が、並列処理を行う関数部分になります。
次にこちらですが、
var SpawnedProp : ?creative_prop = SpawnProp(RingoAsset, SpawnPoint, SpawnRotation)(0)
SpawnProp関数の返り値は、タプル型で、(?creative_prop, spawn_prop_result)の2要素 となっています。 必要なものは、第1要素のcreative_propであるため、最初の要素にアクセスするために(0)を付けています。
ただ、SpawnProp関数は失敗する可能性もあるため、creative_prop型には「?」が付いており、オプション型という型で値を受け取らなくてはなりません。
この辺りは他の言語ではあまり見ないVerseの仕様のため、他のプログラム言語に慣れている方でも躓きやすいポイントではないでしょうか。
※SpawnProp関数は、生成しているprop数が多すぎる場合などに失敗する
if: Ringo : creative_prop = SpawnedProp? then: Ringo.MoveTo(EndPoint, SpawnRotation, 2.0) Ringo.Dispose()
オプション型で値を受け取っているため、失敗コンテキストのifを使用して値をセットしなおし、SpawnPropが成功している場合のみ、MoveTo(移動)とDispose(削除)を行うといった流れになります。
このコードがうまくいっていれば、以下のような結果となります。
まとめ
今回は24.30から可能になったカスタムプロップのVerseによる生成機能について説明しました。 現状では、Spawnしたプロップに対する処理がVerse上からあまり行えないため、できることがそれほど多くはありませんが、今後は、CreativeDeviceのVerse内での生成や、PropとDeviceとの関連付けなどがVerseから可能になると思われます。
こうした機能が実装されれば、UEFNでできることの幅は飛躍的に広がるはずです。
ますます今後のUEFNが楽しみになってきましたね。
RingoGamesではUEFNに関するさまざまな情報を発信していきます。Twitterでお知らせしていきますので、よろしければ、Twitterのフォローをしていただけると幸いです。
Twitterはこちら
基本的な検証が大枠できたので、自分もついにリリースするためのゲームを作り始めました:)
頑張って6月までにはリリースしたいです。
【UEFN】フォートナイトでクリエイティブをする時に、たくさんのプレイ環境に対応するために気を付けること
UEFNで作成したコンテンツを様々なプレイ環境に対応させるために
フォートナイトは様々なプレイ環境に対応しているため、UEFNで作成する際も、たくさんの人にプレイしてもらうためには、様々な環境での動作確認をする必要があります。
しかし、プレイステーション、XBox、Switchなどのプラットフォームのほか、PCには様々な描画モードが存在します。 個人レベルの開発で、様々なプレイ環境で検証するというのはなかなかに大変な作業です。これがUEFNで制作する上での一番難しい点ではないでしょうか。
環境による差異が発生しやすいのはライティング関連やテクスチャ関連などかと思います。
少し雑多なまとめですが、最近開発を行った中で、自分としてはまったポイントを4つ紹介します。
1. ライトはMovableにしておく?(現状では)
一番最初はライトのMobilityに関してです。 DirectionalLightやSkyLightなど、ライトにはその可動性(Mobility)を指定することができ、Mobilityの初期値はStationalyとなっています。
そもそもMobilityがどういった役割なのかは、Epicの篠山さんがかかれている以下のドキュメントが非常に参考になります。
(いつもなんらかの篠山さんのページをリンクさせていただいております)
UEFNのライトは、StationalyとMovableの2つのMobilityがあります。
Stationalyの設定は事前のライト計算をして使用するモードとなりますが、自分の把握している範囲では、Stationalyに設定しても、UEFN上でライトの事前計算を実施する機能が現状はないように思われます。 (もしあったら教えてください)
ここで最初の重要ポイントです。
現状のUEFNでは、Lumenに対応していない環境でStationalyのライトを使用すると正しくライティングが行えないようです。
なんのこっちゃ?かもしれませんが、SkyLightを例にとって説明していきます。
まずは、Lumenオンの環境でスカイライトをStationalyに設定している場合です。
このシーンではSkyLightのみでライティングを行っています。 SkyLightでプレイヤーはただしくライティングされています。
次にLumenをオフにしてやります。 プレイヤーがまったくライティングされなくなりました。(いつものリムライトはいる状態)
UEFNエディターでのライトの事前計算はしてないので(できないので)、空間に環境光のためのボリュームデータが存在せず、プレイヤーなどの動くオブジェクトに対してまったくライティングできていないのだと思われます。
この状態でライトの設定をStationalyからMovableに変えてみます。
すると無事、SkyLightでプレイヤーがライティングされるようになりました。
ライトをMovableにするというのは最も処理としては重い処理となるため、本来的には避けたいところですが、現状で、様々な環境に対応するためにはライトはMovableにしておくのが必要なのではないかと思います。
※Lumenがオンの環境ではStationalyのモードとなっていても問題ありません。ライトがMovableとStationalyと違うことで、Lumenの処理がどう変わるのかは追えていません。すべてMovableにするというのはかなり横着な手段です。Lumenだけを考えればStationalyの方がいい可能性があります。
※これはあくまでも現状(2023年5月時点)の挙動だと思われ、今後仕様が変わる可能性が大きくあります。
2. テクスチャサイズは一枚2048まで
次に使用できるテクスチャのサイズに関してです。
以下の8Kのテクスチャを例にします。
このテクスチャを球体に張り、背景とすることを考えます。(そもそもゲーム開発の人には、8Kを使おうとしている時点で怒られそう・・・)
UEFN上では、それっぽく表示されているため、Fortnite上での動作確認のためセッションを起動します。
すると、
UEFNに怒られます。
「おまえの使ってるテクスチャ、デカすぎじゃボケぇ!2048以下にしとけや!」とおっしゃられているようです。
でも、優しいUEFNさん、文句をいいつつも、実行してくれます。
「なんだ、優しいじゃん」と思って画面を見ると、
「あれ、なんか汚い・・・」
はい、そうです。
2Kを超えるテクスチャは、小さいサイズにダウンされてフォートナイト上では、使用されるようです。
この話は次に続きます。
3. 仮想テクスチャはすべての環境では使えない
「2Kより大きいテクスチャ使えないのか~。なら仮想テクスチャ使っちゃおう!」 そう考えました。
UEFNは非常に大きなテクスチャを分割して使用する仮想テクスチャ(VirtualTexture)に対応しています。
仮想テクスチャに関する説明は、やっぱりEpic篠山さんの以下の記事が参考になります。
テクスチャを8分割し、2Kの8枚のテクスチャ変更しました。
これを仮想テクスチャとしてUEFNに読み込みます。
天球を仮想テクスチャに対応できるようにした簡単なマテリアルを組み、フォートナイト上で動作確認します
今度のものは8Kのテクスチャとして相応の解像感です。
「はい、できた~」
と思ったのもつかの間、
「Switchでも正しく表示されるのかなぁ?」と思い、Switchで実行してみました。
すると、
「ぐっふぇ!(涙)」
Switch版のフォートナイト、仮想テクスチャに対応していないようです。(分割した一番最初の画像が張られている)
同様に、PCのパフォーマンスモードも仮想テクスチャに対応していません。(まぁ、パフォーマンスモードだからね・・・)
ちなみにPS4では正しく表示されました。(PS4頑張ってる!テクスチャの読み込みが遅いので、徐々に高解像度になっていきますが)
未検証ですが、PS4がOKであれば、PS5、XBoxでも問題ないのではないかと思われます。
仮想テクスチャを使うのであれば、現状はSwitchなどで正常表示されないことを覚悟しないといけないようです。
4. ハードウェアレイトレーシングのLumenでは、メッシュ側にレイトレースを避ける手段がない
空をテクスチャで表現する場合、別のことにも気づきました。
「あれ?ハードウェアレイトレースのLumenの場合、画面が暗いぞ・・・」
ハードウェアレイトレースオフのLumenの場合は問題ないのですが、ハードウェアレイトレースオンの場合、全天球を配置してしまうと、全体が影に覆われてしまうようです。
これは、おそらく、レイトレースの計算の中で、「空間が球でおおわれているから影!」と計算しているように思われます。
UEFNではなく、本家のUnrealEngineでは、レイトレースの計算にそのメッシュを含めるか含めないかのチェックがオブジェクト単位に存在します。
しかし、この機能がUEFNでは未実装のようです。
これをどう回避するか、もろもろ試した結果、回避方法が分かりました。
それは、
「球を超でっかくする!」
これです。
球の大きさが大きくなりすぎると、レイトレースのレイが到達しなくなり、影が落ちることがなくなるようです。
一応の回避策はありますが、あまりスマートではないですね・・・。
まとめ
フォートナイトは様々な環境でプレイできることが大きなメリットですが、UEFNを使って開発をする際には、様々な環境でテストしなくてはならなくなり、これはかなり大変な作業に思われます。 この環境であればこの設定、この環境であればこの設定、といったことができればいいのですが、現状のUEFNではそうした機能がなく、今後の機能追加に期待です。
今回は自分がはまったポイントを4点紹介しました。この記事がなんらか皆さんのお役に立てば幸いです。
今後もRingoGamesではUEFNに関するさまざまな情報を発信していきます。Twitterでお知らせしていきますので、よろしければ、Twitterのフォローをしていただけると幸いです。
Twitterはこちら
UEFN 超楽しい :)
基本的な検証が大枠できたので、自分もついにリリースするためのゲームを作り始めました:)
頑張って6月までにはリリースしたいです。
【UEFN】フォートナイトのLumenでライティングをリアルにしたい!(5/2更新)
フォートナイトのUEFNでもLumenを活用したい!
現在の映画やテレビなどのCG作成では、ハイダイナミックレンジの360度HDRI画像を使用して、非常に高い品質の環境ライティングを行うことが可能です。
このライティングは、高品質な一方、多くの計算時間がかかるため、ゲームなどのリアルタイムCGで使用することは、到底不可能でした。
しかし、EpicGamesは、この常識を打ち破り、リアルタイムで高品質な環境ライティングを行う新機能「Lumen」を開発し、UnrealEngine5に搭載しました。
その後、Lumenは、フォートナイトにもChapter 4から正式導入され、対応したPCでは非常に高品位なライティングが行えるようになっています。(でも、きっとみんなLumenのないパフォーマンスモード使ってる・・・。自分も実はパフォーマンスモ(以下略))
Lumen自体の説明は、Epicの篠山さんが書かれた以下のドキュメントが非常に参考になります。自分もこのドキュメントでLumenの概要を理解することができました!
また、以下のページでもLumen使用時のTipsをまとめて下さています。こちらも非常に参考になります。
上の2つのドキュメントはどちらもUnrealEngine5のものであり、UEFNにはあまりLumen周りの設定がありません。
そこで今回、360度のHDRI画像をSkyLightに適用した環境ライティングをUEFNで行い、どのようになるのか検証いたしました。
今回のテスト内容
まず、最初に画像内にカラーチャートが含まれている360度HDRI画像を探しました。
なぜカラーチャートがあるものが必要か?といえば、
ライティングに使用するHDRI画像の中にカラーチャートの映像が含まれていれば、CGのカラーチャートと、HDRI画像のカラーチャートの比較が容易にでき、どの程度の再現性でライティングできているのか検証することができるからです。(なんらかの基準がないと検証できない)
そのあたりの詳しい内容は、CGWorldで特集されたCGSLABさんの記事などを参考にしてください。
そこで、カラーチャートが含まれたHDRI画像がどこかで提供されていないか探していたところ・・・
ありました!
同じくCGSLABさんが、CGWorldのWeb上で公開してくれています!
それも、なんと、太陽光源を後処理で追加してくれています!(太陽光源はHDRIのレンジでは正確に再現できないため、別途、DirectionalLightなどで再現、SkyLightでは環境光の再現のみとするのが一般的なやり方)
さすがです、CGSLABさん!
そこで今回はありがたくこれを使用させていただき、HDRIを適用したSkyLightだけで、どこまでLumenがライティングできるのか、検証することとしました。
UEFNでの事前検証
HDRIで検証する前に、Lumenが本当にUEFNで動作しているか、簡単なシーンで確認することとします。
Lumenを使用するためにはフォートナイトの場合、DirectX12に設定する必要があります。そのため、エディターの描画設定もDirextX12ベースのSM6にしました。 (ソフトウェアレイトレースのLumenはDirectX11でも使用可能なはずで、DirectX12でしかLumen設定が使用できないのはフォートナイト独自の制限事項と思われます)
空間上に3つの球を置き、左の球は青色、右側の球は黄色で発光するようにマテリアルを設定します。 この2つの球が、真ん中の球とプレイヤーにどういった影響を与えるかを確認します。
エディター上では、Lumenが機能し、左右の球が真ん中の球に影響を与えている様子が確認できます。
では、フォートナイトゲーム内での動作を確認します。
フォートナイトのグラフィック設定は以下の設定で行っています。
ハードウェアレイトレーシングはオフにしています。 この項目がオフの場合は、おそらくDistanceFieldを使用したソフトウェアレイトレースが実行されることになるのだと思われます。
※UnrealEngineはMeshDistanceFieldといわれる情報を保存して、様々なことに使用しています 参考リンク
フォートナイトの実行結果はこのようになりました。
フォートナイト上でも、正しく左右の光が真ん中の球に影響を与えているようです。
スクリーンスペースグローバルイルミネーションなど、画面内に映っているものだけで行うフェイクの環境光表現がUnrealEngineにはあるため、こうしたスクリーンスペース系の処理でないことを確認するために、画角を変え、左右の球が画面にうつらない状態でも確認します。
左右の球が映っていない状態でも、真ん中の球は左右の光からの影響を受けています(①) 画面に映っているものに対してフェイクで環境光が足されているわけではなさそうです。
DistanceFieldを使用したLumenでは、DistanceFieldを持たないスケルタルメッシュ(キャラクターなど)へのLumenの効果は限定的になるとのことですが、キャラクターにも左右の光が影響を与えている様子を確認することができました。(②)
UEFNで作成したクリエイティブコンテンツでも、Lumenはきちんと動作可能なようです。
HDRIライティングテスト
シーンのセットアップ
では、いよいよHDRIを使用した環境ライティングのテストを行っていきます。 まず、シーンのセットアップです。
新規シーンを作成し、前回と同様にLumenExposureManagerを使用して、完全にマニュアル露出にします。(やり方は前回記事を参照)
SkyLightをシーンに追加し、そこにCGSLABさんのお台場のHDRI画像を適用します。
※CGSLABさんが提供してくださっているHDRI画像は、色域がACESのものでした。UEFNでは、現状ACESの色空間を扱う手法がないため、事前に別のソフトを使用して、ACESからLinearガンマ/sRGB色域の画像に変換したものを使用しています AfterEffectsの場合は、カラーコンバーターエフェクトを以下のように設定することになるはずです。
360度画像を絵として表示するため、Emissiveに360度画像を適用したマテリアルを作成してSphereに適用して背景画像を作ります。 この球が影を落とさないよう、CastShadowや以下の設定をすべてオフにします。
これで大きくシーン側の準備は完了です。
実行結果
では、フォートナイト上の実行結果です。
カラーチャートとしての再現性としては、完全一致しているとは言えないまでも、それなりに近い形で再現されています。
反対側から撮影したイメージがこちらになります。カラーチャートがこちらはないので、なんとも言えませんが、奥に映っているビルの陰の感じから判断するに、説得力のある形で陰影がついているように思われます。
地面に映る陰に関して、やはりSkyLightだけで表現するのはかなり難しく、この天候であればはっきりとした影が欲しいところですが、かなり柔らかい影表現になってしまっています。
ためしにSkyLightのCubeMap Resolutionを128から大きく上げてみました。多少は結果が変わりましたが、大きな変化はなく、あまり上げる意味はなさそうです。
キャラクターに注目すると、輪郭の光が強く、その馴染みがあまりよくありません。
なぜだろうと思い、SkyLightをオフにして真っ暗にしてやりました。
「あれ?なんかリムライト表現がついている・・・・ライトは一つもないはずなのに・・・」
キャラクターをいくつか変えてみましたが、すべてのキャラクターで同様のため、キャラクターの問題でもなさそうです。
このリムライトを無理やり消すために、カメラを8STOP分(2の8乗=256倍)暗くし、ライトを256倍強くして消えないか試してみましたが、まったく変わらず、どうやらカメラの露出に関係なく、一定の強さのリムライト表現が適用されるようになっているようです・・・・。
おそらくLumen適用モードでは、さまざまな露出のシチュエーションでキャラクターを目立ちやすくするために、あえてこういった表現をいれているのではないかと予想します。
5/2更新
Fortnite 24.30のアップデートでキャラクターのリムライトが正しく設定されるようになり上記の現象は解決しました。
まとめ
今回は、LumenとSkyLight+HDRIのライティングに関して検証していきました。
SkyLightはキャプチャ機能のボタンがなかったり、まだ、未整備が部分も多くありますが、Lumenの環境光の能力はリアルタイムであると思えないくらい素晴らしく、大きく絵のクオリティを上げることができそうです。
直接光の部分もかなり健闘していますが、ここはやはりDirectionalLightで表現した方が良いのではないでしょうか。
今回うまくいっていないリムライト問題に関しては、もう少し追っていきたいと思います。
Lumenオンの状態で、リムライト表現をオフにする仕方がわかる人がいれば、ぜひ教えていただきたいです。
まだフォートナイトでのLumenを使用したライティングの検証については書き切れていない部分があり、引き続きLumenを使用したライティングについて追っていきます。
今後もRingoGamesではUEFNに関するさまざまな情報を発信していきます。Twitterでお知らせしていきますので、よろしければ、Twitterのフォローをしていただけると幸いです。
Twitterはこちら
UEFN 超楽しい :)
【UEFN】FortniteCreativeで思い通りにライティングするために知っておきたい3つのこと
- FortniteCreativeで思い通りにライティングするために知っておきたい3つのこと
- はじめに ~LumenExposureManagerについて~
- 1. 自動露出のワナ
- 2. マニュアル露出時のワナ
- 3. ToneMapperのワナ
- まとめ
FortniteCreativeで思い通りにライティングするために知っておきたい3つのこと
ライティングによる見た目の調整(ルックデベロップメント)はゲーム性を決める大きな要素です。
UEFNの標準では、Day Night Cycleという仕組みで基本のライティングが行われていますが、 LumenExposureManagerを使用することで、ライティングをゼロから完全に構築することができます。
今回は、実際にLumenExposureManagerを使用してライティングを行った際に、自分がハマった3つのワナを、本職である映像系エンジニアの視点で紹介します。
はじめに ~LumenExposureManagerについて~
LumenExposureManagerの基本的な使用方法は以下のUEFNの公式ドキュメントに記載されています。
LumenExposureManagerは、/Fortnite/Lighting/Toolsにあり、これをレベルにドラッグアンドドロップすることで使用可能となります。
LumenExposureMangerをレベルに追加してから、ワールドセッティングで「Disable All Tme Of Day Managers」 にチェックを入れると完全にカスタムなライティングをゼロから行うことができます。
LumenExposureManagerは、Lumen用のポストプロセスコンポーネントと、Lumen未使用時のポストプロセスコンポーネントから構成されています。このそれぞれが、ライティングに対する制御を行うためのコンポーネントであり、画面全体の最終的なルックを決める重要な要素となります。
ポストプロセスを使用したことがない場合は、ポストプロセスコンポーネントがどんな働きをするのか、UnrealEngineの公式ドキュメントを一読されることをお勧めします。
LumenはUnrealEngine5から導入された比較的新しい技術のため、対応できるプラットフォームが限られており、Lumenの使用、未使用、それぞれの設定でコンポーネントが分けられているようです。プレイヤーの実行環境に応じて、自動的に使用されるコンポーネントが切り替わります。
UEFNでゲームとして公開するためには、さまざまなプレイ環境を想定し、どちらのポストプロセスも同様に調整しておく必要があります。
では、ここからいよいよ、ライティングを完全にカスタムにした際に自分が陥ったワナを解説していきます。
1. 自動露出のワナ
LumenExposureManagerを追加して行うことは、「画面がどれくらいの明るさで表現されるか」の調整、専門的に言うと露出の調整となります。
説明のために一つのスポットライトが配置されているだけの簡単なレベルを準備しました。
このレベルで最終的にプレイヤーに表示される画面の明るさは以下の3要素で決まることになります。
- シーンを照らす光の強さ
- 物体の反射率
- 撮影するカメラの設定(絞り、シャッター速度、感度)
これらが掛け算で組み合わさることでゲーム画面に映る映像の明るさが決まります。
今、このシーンのスポットライトの明るさは、300となっています。 テストとして明るさを300から1200に増やして、画面がどのように変化するのか検証します。
一瞬明るくなりますが、すぐにもとと同程度の明るさにもどっていき、光の強さを4倍にしたのに画面としては4倍明るくなっていないことに気づくかと思います。
なぜ、このような現象が起こるのでしょうか?
これは、カメラの自動露出機能が働いているからです。
UEFNでは画面の明るさを見ながら、画面が明るくなりすぎたり、暗くなりすぎたりを防ぐ自動的に露出を制御する機能(Auto Exposure)が動作しています。そのため、照明が明るくなっても、自動的にカメラ側の制御が働き、画面が明るくなりすぎないように動作しているのです。
そのため、自動露出がオンのままライティングをしていくと、非常に弱いライトでライティングしても、自動露出により、適正な明るさに調整される、同様に、非常に強いライトでライティングしても、適正な明るさに調整される、ということになります。こうしたライティングをおこなっていくと、ライトの基準が分からなくなり、強さの適切でないライトでライティングしてしまうという問題が発生しやすくなります。
この自動露出機能は、ポストプロセスボリュームのExposure->MeteringMode(測光モード)をAuto Exposure Histogramから manual にすることによりオフにすることができます。
この設定をおこなって自動露出機能を無効化し、ライティングしたままの強さが画面に反映されるようにすることで、適切な強さのライティングを行うことができます。
なぜ、過度に弱すぎたり、強すぎたりするライトを避けた方がいいのかは、次の項目で説明していきます。
(自動露出をオフにするのは、あくまでもライティング開発段階の話であり、最終的なゲームとして、自動露出のON/OFFは設計次第ですので、開発の最終段階も必ずオフである必要はありません)
2. マニュアル露出時のワナ
測光モードをマニュアルにした場合、標準状態では、Physical Camera Exposure(物理カメラの露出を適用)が有効になります。
UEFNの場合、各項目で左のチェックがオフの場合、その初期値が適用される動作となるため、この状態では、物理カメラの露出機能はONで、使用する状態になっています。
(他のものも同様に、左側のチェックボックスがオフの場合、その項目は初期値が使用されます。左側のチェックがオフならその機能もオフと勘違いしないように注意が必要)
この場合の物理カメラの設定は以下の項目、 シャッタースピード、感度、絞り(F値)です。
こうした設定項目は、現実のカメラの露出の設定と同一であるため、それぞれのパラメータがどういった意味になるのか不明な方は、以下のようなカメラに関するページを参考にしていただくのがいいかと思います。
この3つのパラメータの組み合わせで最終的な画面の明るさは決まりますが、この初期設定(シャッタースピード1/60、ISO100 絞り4.0)は、EV値10というカメラの露出設定になります。
明るめの部屋などで撮影する際に、ちょうどいいバランスの映像を撮影する時の露出設定です。
では、この「物理カメラの露出を適用」しない場合はどうなるのでしょうか?
その場合は、EV値0というカメラの露出と同じとして考えることができます。
これは、真っ暗闇の中で超高感度カメラを使用して、画面を明るく昼間に見えるように撮影しているというような設定になります。ゲームの中でしかありえない設定です。
そこで、UEFNが出た当初、「EV10のカメラを基準として、物理的に正確なライティングしてみよう!」(物理的に正確という言葉に惹かれる性質なので・・・)という方針でライティングを進めてみました。
しかし、ここに大きなワナがありました・・・。
これの問題は何なのか。
それは、ライトの影響を受けず、自ら発光する物体に対する絵作りです。
それを説明していきます。
自発光しているものは、以下の組み合わせで画面上の明るさが決まります。
- 自発光している物体の光の強さ
- 撮影するカメラの設定(絞り、シャッター速度、感度)
この2つになります。
シーン上にUEFNの自発光オブジェクトの一例として、DanceMannequinを追加します。
最初にEV0(UEFNの初期値としての露出)で再現した映像はこちらになります。
自発光しているDanceMannequinも適切な明るさで表示されています。
次に物理カメラの露出を適用しつつ、画面の明るさが変わらないように光の強さを調整します。
EV0からEV10に撮影設定を変えると、カメラは2の10乗分(1024倍)、暗く撮影することになります。カメラが暗く撮影する分、光の強さも2の10乗分、強くすれば、さきほどと同じ映像になるはずです。
その結果がこちらになります。
結果として光に照らされた物体の見え方は、先ほどのものと一致しました。
しかし、右側のDancingMannequinが何か変です。
ライトの影響をうけずに自己発光しているDancingMannequinは、カメラが暗く撮影する分だけ、同じ発光量では暗くなってしまい、まったく表現されていないのです。
これを解消するためには、DancingManequinの自発光の強さも2の10乗分強くすればいいのですが、Fortnite標準アセットのマテリアルの調整はユーザー側からはできません。
Fortniteでは、基本的にEV0を基準として(現実的なカメラ露出は適用しない状態で)開発が行われていると思われ、EV10の現実的なカメラ設定を使用してしまうと、Fortnteが整備している自発光しているアセットがほぼ使えないことになります。(エフェクトなどVFXのものも多くが自発光。そのため使えなくなるものが多数発生することが予想される)
MarketPlaceで出ているアセットも、基本的に物理的なカメラ露出を適用せずに明るさ調整をしているものが、大半であると思われます。
結論として、基本的にmanual露出にした場合も、以下の図ように設定して物理的なカメラ露出を使用しない状態で開発をするほうがいいでしょう。
これをしないと、自発光のものが真っ黒になってしまうという地獄を味わうことになります。(味わった)
(※Fortniteのアセットを使用しない、自発光のものを使う予定がないのであれば、物理的なカメラ露出を使用しても問題ないですが、そんな開発はほぼないんじゃないでしょうか・・)
3. ToneMapperのワナ
次に、UEFNの内部の物体の発光がポストプロセスを通って最終的にどうなるのか、検証してみることにします。 ポストプロセスの設定は以下の設定で行っていきます。
このテストをするために、簡単なマテリアルを準備しました。 自発光のマテリアルで、左から右で直線的に0から1に発光量が変化するようにしてあります。
このマテリアルを適用したプレートを画面に配置し映像出力を検証します。 プレート左端の輝度が0(モニターで一番暗い黒)、右端のモニター輝度が1(モニターの一番明るい白)に表示されれば、そのままの色が画面に表示されていることになります。
結果を画面キャプチャし、波形モニタで確認したところ、このような結果でした。
0から1の変化に対して点線で示したような直線になっていれば、プレートの光った通りに映像が出力されていることになります。しかし、実際には1の光を発している物体がモニター上で映るときには、モニターの最大輝度の70%程度の値にしかなっていませんでした。
マテリアルの右側の設定値を1から10(10倍の輝度の白)に変更して同様の結果を行った結果がこちらになります。 分かりやすいように1ごとに縦線を入れています。
大きな光を出すに従い、徐々に値は上がっていきますが、10の発光をしてもモニターの最大輝度に到達していないようです。
これはどういった現象なのでしょうか?
これはUEFNのポストプロセスでToneMapperという制御が行われていることの影響になります。
ToneMapperは、1以上の強い輝度の諧調を保持できるよう、CGカメラで撮影された映像の最終画像処理を行っています。
これは、悪いことではなく、広い諧調をもつ映像を、狭い諧調しか表現できないモニターで表現するために生み出された手法になります。
この辺りはUnrealEngineと同一のため、実はある程度予想できたことでした。
ただ、物体の光の量をそのまま映像として出力してやりたいケースもあります。 (2Dゲームなどで、画像の色味をそのまま表示したい場合など)
そういった場合はToneMapperをオフにしなければなりません。
そこで、テストとして、ToneMapperをオフにして・・・と思ったところで、あることに気づきました。
「あれ?ToneMapperをオフにする機能がない・・・」
昔のUnrealEngineもオフにする機能がなかったのですが、UnrealEngine4の後半からはToneMapperをなくす機能が追加されました。当然、現在の5.1.1では問題なくToneMapperを外すことができます。
しかし、なぜか現状のUEFNにはこの機能がないようです。
参考までに、現在のUnrealEngine5.1.1でToneMapperを外す方法を紹介しておきます。 UnrealEngine5.1.1でToneMapperを外すためにはPostProcessでToneCurveAmountをゼロにします。
この設定でさきほどと同様のテストをすると、こちらでは、発光量に応じて直線的にただしく映像出力されることが確認できました。
ToneMapperをオフする機能は今後UEFNにも追加かと思いますが、現状では、テクスチャなどで作成した画像の色味を完全にそのままUEFN上で表示させることは難しいようです。
まとめ
今回はルックデベロップメントを行う際に重要な、露出に関する基本的な項目について紹介しました。
画面に表示される最終映像出力は、光、反射率、発光量、カメラの組み合わせによって生じるため、まず、最初にどれかを固定してから実施する必要があります。
1,2で述べた方法で、まずカメラの露出を固定し、ポストプロセスでの自動処理を切ってからライティングを行うことで、統一性をもったエラーの出にくいライティングが可能になるかと思います。
今回は、ルックデベロップメントを行うさいの露出に関して注意点を説明しました。 次回は、画面の色を決めるライトの色温度、カメラのホワイトバランスといった内容を解説する予定です。
今後もRingoGamesではUEFNに関するさまざまな情報を発信していきます。Twitterでお知らせしていきますので、よろしければ、Twitterのフォローをしていただけると幸いです。
Twitterはこちら
Fortnite 超楽しい :)
【UEFN】マーケットプレイスの無料アセットをフォートナイトで使用する
UnrealEngine用のマーケットプレイス無料コンテンツをフォートナイトのUEFNで使用する
前回の記事でUnrealEngineのマーケットプレースのアセットをUEFNで使用したのですが、4月の上旬にniagara系の無料アセットが出ており、非常に使えそうだったため、マーケットプレイスのコンテンツをUEFNで利用する方法をもう少し丁寧に書いてきます。
同様の方法で他のマーケットプレイスのアセットもUEFNで活用できるものがあるかと思います。
今回、紹介する無料アセットをディスプレイ用にフォートナイト上で並べてみました。 (めちゃくちゃ))
※ディスプレイ用に一部をフォートナイト上に陳列しただけなので、ゲームにはなっていません。
様々なシチュエーションで使えるかと思いますし、パラメータを変更することにより、独自の演出をするきっかけになるかと思います。
マーケットプレイスでは、有料無料ふくめて様々なアセットがダウンロードすることができます。
作業の流れ
1.UnrealEngineのインストール
マーケットプレイスのアセットを利用するには、まず最初にUnrealEngineをインストールする必要があります。
EpicGamesLauncherで+を押し、最新バージョン(現在は5.1.1)をダウンロードします。
そのままインストールしてもいいのですが、今回の用途としては、不要なファイルがあるため、オプションボタンを押して、インストールをカスタマイズします。
自分の場合は、「Android」「iOS」「Linux」などのオプションを外しています。 外さなくても問題はありませんが、今回の用途としては不要なものになります。 (かなりのファイルサイズが節約できます)
2.プロジェクトの作成
UnrealEngineのインストールが完了したら、マーケットプレイスのアセットを一時保存する新規プロジェクトを作成します。 スターターコンテンツは特になくても問題はありませんが、何かと役立つアセットが入っているため、今回は、チェックをオンにしてプロジェクトを作成しました。
プロジェクト名は仮にPortingToUEFNとしています。
3.無料アセットを購入
次にアセットをマーケットプレイスで購入していきます。(0円なので購入というのも変ですが)
無料のタブをクリックすると、無料のアセットが多数表示されます。
今回はそのうち、「Korean Traditional Pattern Effect]をカートに入れて購入(0円です)しました。
余談ですが、EpicGamesは毎月、月ごとに無料コンテンツを配布しており、毎回非常にいいコンテンツが無料となるため、今月の無料コンテンツは毎月欠かさず入手しておくことをお勧めします。(将来的にはほとんどのものがUEFNで使える可能性が高いです)
4.プロジェクトへの追加
購入したアセットをプロジェクトに追加します。 マイダウンロードに購入したアセットが表示されるはずなので、そこからプロジェクトに追加するボタンを押し、先ほど作成したPortingToUEFNプロジェクトを選択し、プロジェクトに追加を押します。
これで、PortingToUEFNプロジェクトに対してダウンロードがされるため、準備が完了したら、PortingToUEFNプロジェクトを再度開きます。
コンテンツブラウザに「KTP_Effect」フォルダが追加されています。これが「Korean Traditional Pattern Effect」のディレクトリになります。
Particleのフォルダを見るとサムネイルで様々なエフェクトを見ることができます。 ダブルクリックするとniagaraの画面としてどういったエフェクトなのか確認することができます。
5.アセットの移行
気に入ったものが見つかったらこれを移行(コピー操作)していきます。
アセットを右クリックして、アセットアクションー>移行を選択します。
パーティクルの中で使用されているマテリアル、テクスチャなどが自動解析され、それらすべてがコピー対象となります。
次に、コピー先を選びます。
これは、UEFNのプロジェクトのContentフォルダを必ず選択して実行します。
以下の階層になります。
プロジェクトディレクトリ/Plugins/プロジェクト名/Content
これでUEFNへのコピーが終わりました。
6.UEFNでの使用
コピーしたniagaraエフェクトは以下に入っています。 (もとのディレクトリ構成と同じです)
niagara system をシーンにドラッグアンドドロップします。
これを再生する方法は前回と同様なので詳細は割愛しますが、基本的には以下の流れです。
- Level Sequenceを作成
- Level Sequenceにniagaraを追加し、niagara component と niagara system life cycle trackを追加して制御
- Cinematic Sequencer Devideを追加して、Level Sequenceを再生できるようにする
7.エラー対応
ここまでセットアップしたら、あとは実行するのみなのですが、実行しようとすると以下のエラーが発生しました。
[AssetLog] /EffectPortingFromUE5/KTP_Effect/Particles/Fly/Others/Trail_04_07 : Trail_04_07 missing an Effect Type asset. (FortValidator_Niagara) [AssetLog] /EffectPortingFromUE5/KTP_Effect/Materials/PublicMaterials/M_FX_Glow_fly07 : Soft references /Game/Phase2/Fly02-unreal/Texture/T_Fx_Glow_AS_05 which does not exist. (AssetValidator_AssetReferenceRestrictions) [AssetLog] /EffectPortingFromUE5/KTP_Effect/Materials/PublicMaterials/M_FX_Glow_fly07 : References /Game/Phase2/Fly02-unreal/Texture/T_Fx_Glow_AS_05 which does not exist. (FortValidator_IllegalReferences)
エラーとしては、「niagara のタイプがない」ということと「テクスチャ素材T_Fx_Glow_AS_05がない」という2点のようです
niagaraのタイプがないことに対する対応
これは、UnrealEngineからUEFNにコピーした際に必ず発生します。どうやらUnrealEngineとUEFNではEffectTypeが異なるようです。
これを修正するためには、再度アセットを開き、システムプロパティのFix Issueを押すことで解決できます。 これを押すとEffectTypeが自動的に設定されます。
テクスチャ素材がないことに対する対応
これは、このKorean Traditional Pattern Effectのアセットの作りの問題のようです。 おそらく過去に使用していたもののなんらかの情報が、完全に消去されない状態になっているものと思われます。 該当のマテリアル(この場合は、M_FX_Glow_fly07)を開き、再度、Textureを割り当てなおしてApplyすると、この問題は解消しました。
このコンテンツの他のものでも同様の現象があるようで、基本、該当マテリアルをApplyしなおすと治るようです。
まとめ
マーケットプレイスには、UEFNで使えるたくさんの無料素材があるため、ぜひ同様の手法で、マーケットプレイスの素材を使ってみるといいかと思います。
niagara のエフェクトの多くは同様の手法で、UEFNでも使えるかと思います。 (ただし、UnrealEngineのようなBlueprintによる制御は行えないため、シーケンサーでの制御に現状はなるかと思います)
今後もRingoGamesではUEFNに関するさまざまな情報を発信していきます。よろしければ、Twitterのフォローをしていただけると励みになります。
Fortnite超楽しい:)
リヴァイとミカサ買いました:)