ぷちコン映像編2nd「灯台島」振り返り
お久しぶりです!
ヒストリア様主催のぷちコン映像編2ndに参加させていただきました。
テーマは「ぬくもり」でした。今回その振り返りをさせていただきます。
作品はこちら
今回はモデル、アニメーションなど、天球、音素材以外はすべてオリジナルのものとなっています。
お品書き
・作成前に見ていたものなど
・音楽
・海
・PostProcess
・灯台のマテリアル
・モデル作成
・アニメーション
・編集について
・ちょっとした反省点
・作成前に見ていたものなど
これは全くの偶然ですが、作成前に見ていた記事が今回の作品に大きく関わっています。それがこれらの記事です。
いずれもPontyPantsという方が書いた記事で、ローポリアート(とそのキャラクター)などを魅力的に見せるコツなどが記載されています。
特に最初の記事では
・Specularを高く設定する
・色はSaturationを低く設定する
・Depthを強調する(Fogを使用する、DoFを強くかける)
など今回の作品でも多用しているテクニックが出てきます。ちなみに今回殆ど全てのマテリアルのSpecularは1です。
ところで見てみれば分かると思いますが…
これ、まんますぎるよなぁ。(笑)
それから男性キャラクターは
これのキャラクターからインスピレーションを受けています。
というか当時はこれぐらいのポリゴン数でちゃんとキャラクターが表現できるんだなぁ、と衝撃でした。
・音楽
今回音楽は「PIANO FLAVA」様の「Piano Improvisation #1」という曲を使用させていただいています。
PIANO FLAVA様、素晴らしい楽曲をありがとうございました!
ところでローポリ作品は殆どがBlenderにおけるCyclesのようなレイトレーシング技術を使用したレンダリングエンジンを使用しています。これは独特の温かみを持つ表現がローポリと相性がいいから(というかEeveeやUE4標準のようなレンダリングエンジンだとローポリは安っぽく見える場合が多い)ですが…
じゃあ使おうぜ!レイトレ!
ということでレイトレーシングを使用することに決定しました。
UE4におけるレイトレを使う準備は簡単で、ProjectSetting->Platforms->Windows->DefaultRHIをDirectX12にし、
ProjectSetting->Engine->Rendering->RayTracing->RayTracingにチェックを入れて再起動するだけです。もちろんプロジェクトを作成時にRayTracingをEnableにするとこれらの設定を自動でやってくれます。
とはいえこれだけではまだ準備が整っただけです。特にGlobalIlluminationについてはまだ使用できないため、これを設定するためにはPostProcessVolumeが必要です。
PostProcessVolumeではRayTracingについて色々な設定ができますが、それについてはこちらを参考にしました。
今回の場合一番重要なのはRTGI(RayTracingGlobalIllumination)です。先程も言った通りRayTracingの設定をしただけではRTGIはオンになりません。環境によってはあまりに重すぎるからです。今回、天球がVolumetricCloudを使用しておりただでさえかなり重いため、RTGIをオンにしたが途端とてつもない重さで作業がほとんどできなくなる…ならまだいい方で、最悪UE4が耐えきれず落ちます。
なのでこういう場合は作業中はワイヤーフレーム表示にするといい、というのはよく言われるのですが、とはいえ色味を確認したい場合も結構あるので今回はPostProcessVolumeを小さめに作ってInfiniteExtentのチェックを付けたり外したりすることも併用してやっていました。(そして設定を戻し忘れてレンダリングし直しになったことが何回か…)
ちなみにレンダリング時のRTGIの設定はBruteForceで現在シーン(灯台島がある方)はサンプル数12、過去シーン(街の方)は16です。残念ですが僕のグラボではこれより設定上げるとクラッシュの危険性がかなり上がります。もしかしたらFinalGatherでレンダリングした方が綺麗にできたかもしれませんが…これはこれでチラついてる気がするんだよなあ。
それからRTGIの副作用?なのかもしれませんが、画面全体が明るくなります。それが狙いでやってるので、影のシャープな感じがなくなるのは嬉しいんですが、これをBloomの強さとかで調整しようとすると今度はノイズが目立ってきて…みたいな悩ましい問題が起こります。結果的にちょっと陽光が眩しい感じになりましたが、違和感ないかな?
・海
海の作成に関してはこちらの動画を参考にしています。
ただし動画では半透明を使用していますが、今回は海に半透明は使用していません。理由としてはDoFを使用したい関係上、半透明を多用するわけにいかなかったからです。海を全部半透明にするなどとんでもない、DoFで問題が起きるに決まっています。
…じゃあ今回半透明とDoFで問題は起きてないんだね、というとさにあらず。炎は半透明を使用しているので…
このシーンが一番酷いんですがDoFのかかった物体を半透明マテリアルの手前に置くと、本来ボケるはずのエッジがシャープになってしまいます。ここだけはシーン的にも本当にどうしようもなかった箇所で、他のシーンではなるべく半透明の手前にDoFのかかったものを置かない(そういう角度から撮らない)という解決法でなんとかしています。
海にはもう一つ罠がありまして、RayTracingの環境下でマテリアルのWorldPositionOffsetを使用する場合、Details->Rendering->RayTracing->EvaluateWorldPositionOffsetをチェックしないと表示が変になります。
おそらくRayTracingの処理はWPOとか終わった後にPostProcessとして処理されるから…なんだと思います。
しかしながら上記の解決法でOKなのはUE4.25までの仕様のようで、UE4.26からは距離が一定以上離れるとまた変な表示に戻ってしまいます。これを防ぐために今回は水のマテリアルにはRayTracingを適用しないようにしました。これはMeshエディターにあるLOD設定のマテリアル欄にあるVisibleInRayTracingをオフにすることで設定できます。
どこかの設定でRayTracingを適用したまま正常な表示にできないかなー、と探してみましたが、今回は時間もなかったし、そこまで目立つわけでもないのでこの形で行くことにしました。
・PostProcess
過去のシーンを作るにあたり、PostProcessで画面をセピアにするというのはとてもスタンダードな方法論だとは思います。ただ今回セピア調にしつつも色をちゃんと認識させたかったので、それに加えて少し工夫を入れています。
左下の部分は一般的なセピアカラーを作成している箇所です。ここではそれに加えて自乗したカラーを掛け、更に明るさの調整を入れています。これによってカラーを残したままセピア調にすることができました。
・灯台のマテリアル
・モデル作成
モデルの作成、及びアニメーションの作成にはBlenderを使用しています。ローポリの場合、BlenderにはDecimateという便利なModifierがありまして…これに全面的に頼ればOKなのかというとそういうわけではありませんが、スカルプトしたモデルをとりあえずこれでポリ数を減らして最後に手作業で手直し…という感じで使えます。ポリゴンは四角がいい!という人はShrinkWrapを使用してもいいですし…凹んでいるモデルはちょっと面倒ですが…
・アニメーション
3Dアニメーションのノウハウについてはある程度「アニメーションエイド」様の動画を参考にしています。
まあ今回作成したアニメーションの半分ぐらいは最終的な作品には残っていません。だって入りきらないんだもん!
アニメーションは今回かなり成長したと同時に反省点がある箇所でもありまして、AutoRigProのIKのオンオフ、回転のEulerとQuarternionを場当たり的に設定していった結果Export時に本当に大変なことになりました。
いちいちアニメーション毎に設定を探してExportしてReimportするなーんてハメになるので、良い子は絶対真似しちゃダメだぞ!(^_-)-☆
・編集について
レギュレーション的に大丈夫かなあ?と思ったことが一つだけあって、今回複数の時系列やライティング環境をまたぐ関係上、UE4上でのカット編集が難しくなりました。
一応MediaTrackを使用すればできなくはないんですが、画質が悪くなる上にUE4ってそういう目的で使うものなの?という疑問が湧いてきます。
そこで編集についてはAEを使用することにしました。編集したのはカット及びフェード、それから音なんですが、レギュレーションにはフェードと音に関しては認められているんですが、カットについては含まれてないんですよね。エフェクトやカラーはおろか速度変更すらしてないし大丈夫だとは思うんですが…
で思ったんですが、レギュレーション的に認められるならぶっちゃけバリバリ動画編集ソフトでカット編集したいなあ、と。理由は色々あるんですが、一言でいうとやっぱりUE4はそういうソフトではないから、ですね。向いてないことやる必要ないじゃん、と。
・ちょっとした反省点
音楽が始まってからはいいのだが、最初の効果音のみのパートが薄っぺらい。効果音については要研究、というか最低でもいくつか組み合わせないと安っぽい感じが出てしまうみたい。
というわけで「灯台島」の振り返りでした。12日になっても撮影が一部完了してなかったりしたので、終えられたことが本当に嬉しい!最後アップするときは「ここで誤操作で全部消えたらどうしよう」という緊張のし過ぎて吐きそうになってました。それだけに今はヤッター!って感じです!
それではまた!
ぷちコン供養第一弾「Unstoppable Arkanoid」振り返り
ぷちコン供養の概要はこちら
今回第一弾「Unstoppable Arkanoid」を公開しましたのでその振り返りです。
<動画>
<プレイアブル版>
お品書き
・ゲームの選択
・LabelBlueprint
・演出について
・メニュー画面の操作
・BGMのVolume
○ゲームの選択
上の記事にも書いてありますが、これを思い付いた時にはもう18日しかない状態で「ここからある程度のものを作るには挙動を物理に任せられるゲームしかない」と思ってブロック崩しに決めました。
前回ぷちコンのことが半分トラウマみたいになってまして、「絶対に間に合うようにしたい!」という強い意志があったのもかなりあります。
ま、結果として色々考えてたら結局ギリギリになっちゃいましたけれども
それからブロック崩しというゲームについてですが
ぶっちゃけ物理制御するのは向いてないような気がします
理由としてですが、ヒットした時にイレギュラーな挙動が起こりすぎるからですね。プレイアブル版をやってみると分かるんですが、特にボールとボールがぶつかってしまった時に片方がめちゃくちゃ減速したりします。こっちとしてはぶつかっても速度を保って欲しいんですがね…
それから何らかの原因でバウンドが完全に水平、または垂直になってしまうとゲームが停滞してしまいます。一応ForwardVectorがあまりにも偏った値になった場合、ある程度補正がかかるようにしてあったりするんですが、あまりに不自然な挙動になってしまうとそれはそれでどうなんだということで、一応補正の関数を載せておきますがアドバイス等ありましたらぜひお願いします。
それから横からバーを突っ込ませるようにしてボールを打ち返した場合、かなりの確率で下に落ちてしまいます。そうでなくてもアイスホッケーのように多くの場合打ち返したボールが急加速してしまうので、結果的に目で追えなくなって死にます。
まあバーをゆっくり動かすようにすれば解決なのかも知れませんが、画面上に2つのボールが存在することが前提のゲームで、あまり「バーが遅かったから拾えなかった」みたいなストレスを与えるのもいかがなものかと思い、この速度にしてます。
結果としてこのゲームのゲーム性は「物理をなるべく暴れさせないように細心の注意をはらいながら、爆発でコンボを繋いでいくゲーム」となっています。プレイしているとまるで1分30秒+αの綱渡りをしているような感覚に陥るんですが、これはこれで面白いということで結果オーライかもしれません。(ただ初心者には優しくない)
○LabelBlueprint
このゲームでは一切UMGを使用しておりません。じゃあ文字はText3Dかというとそうでもなく、Blenderで文字のメッシュを作成しそれをBP_NeonLabelというBlueprintで並べて表示しています。ぶっちゃけあんまり意味は無い気がしますが、まあ自己満で作りました。仕組みとしてはExtentの倍の値をプラスしながらStringから取得したCharaに対応する文字メッシュを置いていくだけです。一応用途別に「OffsetなしでConstructionScriptで使用する関数」「Offsetありで左揃えで文字を表示する関数」「Offsetありで右揃えで文字を表示する関数」の三種類を作成しています。
表示するたびにStaticMeshをDestroyする構造なのでタイム表示をこれにしていいのか悩みましたが、結果僕の環境では問題無いようでした。
○演出について
今回は「FlowerPinball」の時と違いオープニングやゲーム開始時などにちょっとした演出を挟んでいます。これを何が管理するのがベストなのか?普通に考えれば「GameModeに任せればいいじゃん?」なのですが、GameModeだとゲームの最初にGetActorOfClassやGetActorsOfClassで使用するActorをGetしなければなりません。
その負荷を嫌って今回はBP_ProduceJunctionというBlueprintを作成してこれに一括で演出を任せることにしました。これでEditableな変数でActorを指定すれば先の関数を使わなくて済む!やったぜ!
結果としてGameModeに任せるのが多分一番いい、ということが分かりました。
第一に面倒臭いです。Actor指定するのはまだいいのですが演出を呼び出す側からもBP_ProduceJunctionをGetしなくてはならないので(そこでGetActor~関数を使うと本末転倒ですし)めちゃくちゃ手間が増えます。その点GameModeはどのActorからでも呼び出せるので、手軽に使えます。
第二に大して負荷は問題になりません。所詮GetActor~関数が呼び出されるのは最初だけです。Actor数が爆発的に増えて負荷が増大したとしてもロード画面を挟めば解決します。相当長い時間待たせない限りユーザーがやる気を無くすほどのストレスを与えるとは考えにくいです。もちろんTickでGetActor~を毎フレーム呼び出すとかは止めたほうが良いと思いますが…
というわけでこれは今回限りですね
○メニュー画面の操作
よくある「一回だけ上下左右を押すと普通に動く&押し続けると連続で動く」という動作。自分の中でやり方がほぼ確立しました。
ClearAllTimersの中身はこちら
配列のClearは必要なのか検証してないんですが、一応入れてあります。
SetTimerByEventはもちろんSetTimerByFunctionNameでも構いません。というかスッキリするので自分はそっちでやってます。
各方向に対してそれぞれ別のFunctionかEventが必要なのが玉に瑕ですね…
○BGMのVolume
これはちょっと自分用のメモみたいなものですが…
ボリューム調整の場合、
こんな感じでSetSoundMixClassOverrideとPushSoundMixModifierを組み合わせて実装すると思うんですが、これでBGMの音量を0にした場合BGMが消えてしまい、再び音量を戻しても流れなくなってしまいます。これは設定で
VoiceManagement->VirtualizationModeがデフォルトでRestartになっているためです。つまり音量が0になった場合、そのSoundを次回また流されるまでは消す、という設定になっているわけですね。SEの場合すぐ次のSoundが流れるので問題にならないのですが、BGMの場合はこの設定をPlay when Silentにしてあげると想定通りの動作になります。
以上、「Unstoppable Arkanoid」振り返りでした。
勝手知ったる物理ゲーと思いきや、意外と詰まるところも多かったのが印象的でした。
ご意見、ご質問、ご指摘などありましたらぜひお願いいたします。
第14回ぷちコン振り返り
というわけで今回9月1日締め切りのぷちコン第14回に「なつのはじまり(未完成)」を投稿いたしました。
はい、というわけでタイトルの通り今回は完成しませんでした。
いやー正直当初はかなり落ち込んでいまして、「ぷちコンロス」みたいな状態になっていましたが、今は少し回復しましたので反省点をば…
基本的には
・最初から大きな目標を立てすぎ(しかもその自覚がない)
・新しいことに挑戦しすぎ
・作業効率化が甘かった
これらが問題となっていたと思っています
まず最初、目標設定について。
恐ろしいことにこのゲームの最初の計画は4ステージ+1エクストラ。キャラは1プレイヤー+15雑魚敵(各ステージ3)+4ステージボス+2エクストラボス=22キャラでした。
開発中盤になってこれがとんでもないことだと気付きます。自キャラなどの人型キャラはアニメーションアセットを使用していますが、少なくとも雑魚は人型ではないため、自分でアニメーションを用意しなければなりません。
最低でも待機、歩く、攻撃*2、食らいモーション、死亡モーション、この6つは必要でしょう。死亡は物理に任せるという選択肢もありますが、モーション一つのためにPhysicsAssetを不自然でないまで弄るとなると、逆に手間は増大します。
はい、単純計算で15*6=90アニメーションです。これにより多くのモーションが必要であろう6体のボスが加わります。どう考えても無理じゃん!
一番まずかったのはこれに気付いたのが中盤だったということでしょう。結果的に路線変更により、折角作ったモデルを捨てなければならなかったりしました。前回(半分以上既製品のおかげで)上手くいったのを過大評価してしまっていて、「前回いけたんだからこれもいけるって!」と考えていたのがかなり裏目に出た感じです。
次に挑戦について。
ぷちコンはそもそも学習を目的としたコンテストでありますので、色々なことに挑戦する、その姿勢自体はとても良かったと思います。完成さえすれば。
とにかく初めてのことが多すぎる。まずキャラクタークラスを動かすようなゲームは「ペーパーニンジャ」以外作ったことがありません。アニメーション作成は初挑戦。インベントリは少し前にチュートリアルで作ったっきり。AIもまともなものは初挑戦。LevelStreamingも初挑戦。Spawnさせた敵キャラってどうやったら動かせるんだっけ?みたいな状態です。
結果的にあらゆる箇所で詰まりまくります。
・Blenderでアニメーション作成中にFakeUserを掛けておらず、いくつかのアニメーションが消失する
・Blenderで変な操作をしていたらNLAエディタに別のアニメーションが入ってしまい、これの消し方が分からない
・Spawnさせた敵キャラの動かし方がいくら調べても分からない
・LevelStreamingした時は事前ライティングってどういう扱いになるの?
・LevelStreamingした時はNavMeshってどうすれば機能するようになるの?
もうこんなんばっかですわ。
前回も詰まる箇所はいくつかありましたが、今回は特にそういう箇所が多かったです。
最後に作業効率化について。まあこれは前二者に比べれば副次的な要素が強いですが。
前項目で書いたとおり、色々なことが初めてなため作業効率化できるところができていなかったりします。
最たるものは全てのアニメーションの遷移をAnimationBPで行っている箇所でしょう。もうスパゲティすぎて、途中からAnimationBPを見るのが嫌になってしまうぐらい。
しかも攻撃が終わった後の状態遷移をNotifyで行っています。結果大量のNotifyが必要になり、それを書くために大量の時間が吸い取られていきます。
これも後で気付いたんですが、BTでもBPでもPlayAnimation(PlayAnimationWithFinishedEvent)を使えば楽だったんですね~。
アニメーションのブレンドができないので、少しカクつくなどの問題はありますが、一瞬ですから基本そこまで気にならないレベルですし…
それからインターフェイスなどの効率化要素もいつもより少なめです。これは比較的問題になりませんでしたが、やっぱり色々効率化できる箇所はあったと思います。
というわけで反省点でした。全体的には無謀な計画を立てたため、効率化もろくろくできず、そのまま押し切られてしまった感が強いです。
まああんまり反省点ばかり振り返っても暗くなるので、今回良かった点などを…
やはり前回よりも色々なことに挑戦したおかげで、かなり知識が深まりました。特に分かっていると思っていたけど、曖昧だったところ。Blender関連などは機能の一部しか使っていないということもありますが、まだまだなんとなくで使っているんだなあ、と。
まあ、AutoRigProは神ってことも分かりましたがね。あれは本当に神のツール。問題が起きなさすぎる。
あとは妥協することの大切さを学べたかな、と。自分を今回一番褒めたいのは、無理だと分かった時点でちゃんと路線を変更したことです。完成していないのでもっと妥協すべきだったかも知れませんが、「妥協したくない」という気持ちと「完成しないだろこれ」という現実を見比べて、曲がりなりにも後者を取れたことは悪いことではないかなと。
それから、分かったことを少し備忘録代わりに書き留めておきます。
Spawnさせた敵キャラですが、SpawnDefaultControllerを繋げばAIControllerも合わせてSpawnさせられる、というところまでは結構色々なサイト書かれているんですが…
しかし実際やってみると何故か動かない。それどころかBlackBoardにアクセスできないらしく、エラーすら吐く。原因はRunBehaviorTreeとUseBlackBoardのノードがEventBeginPlayに繋がっていることでした。
敵キャラをSpawnさせない場合(Levelに直接配置する場合)、ゲーム開始時にもうAIControllerはPawnをPossessした状態になっています。なのでEventBeginPlayでいきなりRunBehaviorTreeしても問題が起きません。
しかしSpawnDefaultControllerでSpawnしたAIControllerはまだPawnをPossessしておらず、そこにはほんの僅かですがタイムラグがあります。この瞬間にRunBehaviorTreeしても紐付けするPawnが見つからないので動きません。
なのでこういう場合、色々解決方法は考えられますが、一番良いのはRunBehaviorTree(とUseBlackBoard)をOnPossessイベントに繋ぐことですね。
AIを扱うためには基本的なことかもしれませんが(というかもしかして極め本に書いてあるんじゃ…)、今回詰まった箇所というわけで紹介しました。
それからLevelStreamingした際のNavMeshですが、そのままだとMainLevelとSubLevel双方にNavMeshを配置しないと動きません。しかし配置するのも面倒ですし、スマートではないやり方なので
ProjectSettingsでEngine->NavigationMesh->Runtime->RuntimeGenerationをStaticからDynamicに変更します。これによってNavMeshが動的に変化するようになり、新たにStreamingしたSubLevelに合わせてNavigationが生成されるようになります。…まあ負荷の問題は発生しますが。
タイトル画面について。
タイトル画面では今回VolumetricClouds(多分ver4.25からの新機能?)を使用しています。利用の仕方ですが、まずはプラグインでVolumetricsのプラグインをチェックします。
再起動したらCtrl+NでDefaultLevelを作成し、ContentsBrowserのShowPluginContentとShowEngineContentのオプションをチェックし、VolumetricsContent->Content->Sky->Blueprints内にあるBP_Volumetric_Cloud_Layerを作成したDefaultLevel上に配置します。ちなみにこの時点ではまだ何も起きません。
次にBP_Volumetric_Cloud_LayerのShowSkyDomeをチェックします。
まだ何も起きませんが、これはパラメーターが初期状態だとめちゃくちゃな数値になっているからです。こんな感じに設定すれば…
こうなります。
はい、何か変ですね。原因はデフォルトのBP_SkySphereが小さすぎて雲の一部がその外側に出てしまっていることです。BP_SkySphereのScaleを10倍ぐらいにしてみましょう。ついでにBP_SkySphere自身の雲は邪魔なのでCloudOpacityを0にします。
これできちんと表示されるようになりました。
これの利点としては、見た目通りかなり「厚み」を持った雲を表現できるのと、太陽光を遮るような表現が可能になる点です。どうしてもテクスチャによる表現だと「厚み」が0になってしまいがちですが、これならそういう不満点を解消できます。
ただし、注意点もいくつかありまして…
はい、この通り。ShaderComplexityが真っ赤です。ぶっちゃけ現状ゲーム内でバリバリ使うのはかなり厳しいんじゃないかと…なので今回はタイトル画面のみの採用となりました。動かなければ問題ないはず、との判断です。
あともう一つ、この雲はDirectionalLightの色などにもちゃんと反応するお利口さんですが、反応するのはデフォルトの「LightSource」のみとなっています。PlaceActorsから新しくDirectionalLightを配置しても、雲が真っ黒になってしまい反応しないので注意しましょう。プロジェクト開始時のデフォルトのレベルやCtrl+Nで作成するDefaultLevelでの使用以外では、どこかから「LightSource」をコピペして持ってくる必要があります。
ということでVolumetricCloudsでした。パラメーターの調整でかなり色々な雲を表現できるのでぜひ色々弄ってみることをおすすめいたします。
それでは。
ご意見、ご質問等ありましたらお気軽にどうぞ。
「Flower Pinball」振り返り
ぷちコン第13回応募作品「Flower Pinball」について色々と…
まずはお品書き
・そもそも最初は全然違うゲームにする予定だったよ!
・「花が咲く」をどう表現する?
・「ピンボールでいける!」と思ったのはなぜか
・最初のピンボールとしての構想
・アートワーク変更
・ギミックの数が…
・スプラインの勉強になった
・また音関連か…
・そもそも最初は全然違うゲームにする予定だったよ!
テーマが発表されてからずっと構想を練っていましたが、最初の7日間は実はアイデアが全然決まりませんでした。それでも出したアイデアとして「(刺し身の)サク」をなるべく薄く切って競うゲームというのを考えたのですが…なんかTwitter見たら同じようなこと考えている人は結構いるし、そもそも変化球としてもそんなに面白くないし、なにより作り方のアイデアが微妙に湧いてこないし(当時ProceduralMeshSliceの存在を知らなかった)で没になり、結局自分が変化球してもあんまり面白くならないなと思い、王道っぽい「(花が)咲く」でいくこととなりました。
・「花が咲く」をどう表現する?
ただ、そもそもなぜ構想段階で「(刺し身の)サク」に行こうとしたのかというと、「(花が)咲く」ことをどう表現したらいいのか分からなかったという…当時は難しく考えすぎてまして、それこそぷちコンの他の作品でいえば、
こういうことやらなきゃ表現できないんじゃないの?、と。最低でもボーン組んでー、とかやらなきゃ駄目だよね?、と。
そんなんできねえよ!多分絶対時間足りねえよ!ということで悩んでいたんですが、そんな時に見付けたのがこのチュートリアルでした。
これはパーティクルのチュートリアルなんですが、これを見て僕は思いました。「あれ?もしかして花弁を別々に作って順々にScale弄るだけで結構「咲く」感じに見えるんじゃね?」、と。
やってみると理想的とまではいかないまでも、スタイライズドな表現としてはまあアリかなと思えました。ということで今回はこの表現を使うことにしました。
ふーよかったわい、一件落着、と思っていたのですが、実はこの時各花の親クラスを作らなかったという痛恨のミスをしています。(後で大変だった)
・「ピンボールでいける!」と思ったのはなぜか
「咲く」の方向性が決まってもゲーム自体の方向性はまだ決まっていませんでした。ピンボールももちろん候補には上がっていましたが、数ある候補の一つという感じです。
さて、僕はぷちコンは今回で2回目、前回は映像編だったのでゲーム制作の経験が他の参加者よりかなり不足しています。特に製作期間についての不安はかなり大きく、常に「これもしかして間に合わないんじゃないか?」と思って作っていました。そんな中ピンボール制作に大きな味方を見つけました。
2015年UnrealFest横浜で発表、公開されたピンボールコンストラクションキット及びその動画です。はっきり言ってこのサンプルが無ければ今回の制作は間に合わなかったと思います。もちろん、そのままこれを使ったわけではなく要所を真似しただけですが、それでも大いに製作期間の削減に役立ちました。
・底板部分は斜めにせず水平にする(ボールに働く重力はBPで付ける)
・ゲート、スピナーの実装はほぼそのまま真似する
・ランプのスプラインを参考にする
・各種サウンド(Super retro audio bundleは買った)流用
などが参考にした部分です。
ただ一番の目玉であるプロシージャルな壁の生成は参考にできてない(特殊すぎる、難しいなどの理由で)んですけどね。まあそれでもスプライン使っているんである意味プロシージャル生成してますけど…(これはこれで見通しがいいなどの利点もあって採用した)
・最初のピンボールとしての構想
弾を撃ち出すのは実はかなり初期から決まっていました。ピンボールの一番の弱点は一回打つとボールが下りてくるまでの間は基本的には暇になってしまうということだと思います。有名なWindowsピンボールはその弱点を補うためにフリッパーを動かすとボールチャネル(一番上の仕掛け)のライトが入れ替わるという仕様になっていました。この仕組み自体は「Flower Pinball」にも実装しましたが、それだけではちょっと退屈になってしまう気がしました。
なので僕がSTGが好きであることも相まって、ボールから弾を出したらどうだろうと思いました。弾が当たると色々な仕掛けが作動してそれが高得点に繋がる。最初の構想では4属性の弾を特定の仕掛けに当てると作動し、全て当てると中央の花が開いて高得点チャンス!、というようなものを想定していました。
ただ弾の射程が長すぎると今度は安定した位置(例えばフリッパーを上げた状態での根元部分など)から仕掛けを狙うことができるようになってしまい、リスクを犯さず延々とスコアを稼げてしまいます。なので射程はかなり短くしました。また反動を付けることである程度ボールをコントロールする能力を付けました。これはゲームを面白くする要素であると同時に、仮にボールがどこかで止まってしまった場合に低リスクでその状況から復帰できるという保険でもあります。
・アートワーク変更
話がちょっと変わりますが、僕は最初にタイトル画面から制作を始めました。実はこのゲームのタイトル画面ですが、個人的にかなり気に入っています。
「すりガラスの細工物っぽい花の中心からぼんやり光が放たれている」といった感じの絵を頭の中に描いていたのですが、StationaryLightとSubsurfaceを使うことでかなり近い感じになってくれました。
さて、当初の予定ではタイトル画面はともかくとしてゲーム画面は現在よりかなり「植物的」な見た目になる予定でした。Megascansから引っ張ってきた草木や蔦をそのままの形でもりもりと並べるつもりでした。恐らくですが「Megascansを使えば手間を節約できるし(タダだし)、盛れば色々至らなくても隠せるだろ」とか考えたんだと思いますが、結局の所タイトル画面との融和性も考えて、タイトル画面側の冷たい感じのアートワークに寄せることにしました。
ただし、今もその時の名残は残っています。枠に貼り付けられた蔦のデカールや、寂しいなと感じた所には草花を植えています。当然Megascansから引っ張ってきたものを使用しています。
・ギミックの数が…
今回一番大変だった部分です。一本のゲームを、しかも独自アートワークで統一、ということになると、とにかく沢山のギミックを作らなくてはならず、かなりの回数Blenderを起動しました。作ったのはベース、スリングショット、プランジャー、ボールチャネル、ランプレーン、バンパー類など。花のモデリングが見た目ほど難しくなかったのは救いでした。
こことか
プランジャーの柄の部分なんですが、結構リアリティ持たせたのに1ミリも映らない。
悲しい。
まあ作ってる途中で「ここ全然映らなくない?」とは思ったけれども…
・スプラインの勉強になった
ここからは少し技術的な話もします。
今回最も勉強になったのはスプラインの扱い方です。いやーこれぶっちゃけ便利ですね。
今回使っているのは壁、ランプレーン、左側のサブレーンの三箇所です。
まず基本的な使い方を見ていきましょう。
スプラインをループさせないアクタの場合、ConstructionScript内でNumberOfSplinePointsから2を引いてForLoopでAddSplineComponentを回します。
生成されたComponentをSetStartAndEndでこんな風にSplineの位置とタンジェントに指定してやればOK。
基本の使い方はこれです。
ループさせる場合は2ではなく1を引きます。
もちろんですがClosedLoopにチェックを入れるのを忘れないように
LoopIndexで指定することで途中から生成されるMeshを変更したりすることが容易にできます。また一つ一つのSplineMeshComponentに対してHitEventやOverlapEventなどを持たせたい時は、
ConstructionScriptで最初に配列化したMesh等に対してDelegateで指定することで持たせることができます。ただこういうことをやる時にちょっと注意なんですが…
ConstructionScriptで配列にAddする場合、最初にちゃんと配列を初期化しないと処理が走るたびに配列に新しい要素が追加されていってしまいます。気づいた時に要素数300とかなってて焦りました。画像ではClear関数を使って初期化してますが、もしかしてこういうのってAddUniqueとかで解決できるんですかね?
左側のサブレーンのようにお互いのSplineMeshComponentを少し離して配置したい場合は、透明なMeshを用意するとかも一つの手立てではあると思いますが、僕は少し違う方法を取っています。
MeshのBoundsを弄って実現しています。これがいい方法なのかはよく分かりませんが、とりあえず今の所問題は無さそうです。
・また音関連か…
前回の反省を踏まえ、今回は音楽をかなり最初の方に選び、その雰囲気にある程度合わせてゲームを制作していくという形を取っています。事実拙いながらもBGMに関しては前回よりミキシングが格段に良くなったと思います。そう、BGMに関しては。
問題だったのはSEです。前回よりも効果音系アセットは増えているにも関わらず、それでも「これだ!」と感じるSEって無かったりするんですよねぇ…もうSE自分で作れるようになりたいよなあ…結局あるものをPitchを弄ったりして間に合わせましたが、「もうちょっとだけこう…」みたいに感じるSEも無くはないです。どうにかしたいですねこれ。
以上、「Flower Pinball」振り返りでした。正直最初辺りの構想段階よりもいいものは出来たとは思います。なのである程度満足するべきなのか知れませんが、まぁ、作ってるとね…欲がね…不満がある所をピックアップすると、音関連は当然として、Particle関連、そもそものアイデアの貧弱さとか…後はゲームバランスですかね。
ピンボールは延々続けられてしまうというのが慢性的な問題となっているゲームなので、この辺りに切り込めれば良かったのですが、今回はその辺はこれまでのピンボールと変わりません。タイムアタックモードを構想した時期もあったんですが、期間が足りませんでしたね。
ぷちコン第13回に応募しました
とりあえずご報告までに…
第13回ぷちコンに自分のゲームを応募いたしました。
タイトルは「Flower Pinball」です。
お花をテーマにしたピンボールとなっています。
ゲーム本体はよろしければこちらでダウンロードをお願いします。
また僕自身のプレイ動画も上げました。
40分超という超長丁場な動画になっていますがよろしければどうぞ。
油断すると結構あっさりと死んでしまうゲームなので、プレイしてる本人としてはかなりの緊張感があったりします。
正直この作品にかまけ過ぎていて殆ど更新していない状態になっていたこのブログですが、この作品の振り返り記事から更新を再開していこうかなと思っています。
まぁとはいえまた何かコンテスト的なものがあると止まっちゃう可能性が高いと思いますけれども…
とりあえずぷちコン参加者の皆さん、それから運営のヒストリア様(は忙しいのはこれからだとは思いますが)、お疲れさまでした。
…次の作品の方向性(やりたいこと)は既にちょっと決まってたりします。
ぷちコン映像編振り返り
この度ヒストリア様主催のぷちコン映像編に参加させていただきました。
(僕の環境だとサムネイルが表示されないんですが、ちゃんとアップロードできてるのかな?)
この記事はその過程で工夫したこと、引っ掛かったこと、反省すべきことなどをまとめようというものです。
以下お品書きです。
・月入りの夜空の作り方
・Matineeを使うことになった話
・CineCam付きActor
・詰め込みすぎ
・自作で効果音を作ろうとしたけれど…
・半透明とFOD問題がおきてるみたいですけど…
・Megascansについて
・完成させることができてよかった
・最後に
それでは早速いきましょう。まずは月入りの夜空の作り方について。
この映像で天球として使用しているのはBP_SkySphereを改造したもののみです。今は対応していますが、当時「ANIMAL VALIETY PACK」がUE4.24に対応していなかったため、4.24以降の機能であるSkyAtmosphereは使用しておりません。
BP_SkySphereもそれなりに高機能な天球ではありますが、弄ってみれば分かる通りそのままでは月を浮かべることができないという弱点があります。
なのでこれを改造します。BP_SkySphereのまま弄ると色々面倒なのでEngineContents内にあるBP_SkySphereとそれから参照元であるM_Sky_Panning_Clouds2をDuplicateしてContentの方に持っていきます。
M_Sky_Panning_Clouds2からMaterialInstanceを作成し、ConstructionScript内にあるMaterial参照部分に突っ込みます。
MaterialInstanceのパラメーターを調整します。今回はこんな感じの数値にしてみました。
SunRadiusは少し月が小さめと感じたため調整しています。それからDirectionalLightのLightColorをOverallColorと同じ色に変更するのも忘れないようにしましょう。色に合わせてSkyLightなど他のパラメーターも微調整しましょう。
さて、とりあえず夜空ができましたが、このままでは星がありません。これは月に見えているのは実際には天球内では「太陽」であり、今の時間は色味を調整しただけの「昼」にすぎないからです。なのでこれを操作するためにM_SkyPanning_Clouds2内を見ていきますと、SkyColorsというコメントブロック内にStarBrightnessにSunHeightを掛けている部分があります。
この接続を切って常に1を掛けるようにすればいつでも強制的に星を表示させることができるようになります。これで月が入った夜空を表示させることができるようになります。(ただしこの方法だと満月のみですが)
・Matineeを使うことになった話
実はこのムービー、Sequencerからキャプチャしているのではなく、出力にMatineeを経由しています。なぜならUE4.23ではSequencerから直接キャプチャするとPlayRateTrackが効かなくなるという欠陥があったからです。要はスローモーションなどを実装することができません。結局それを使うためにはMatineeを経由させる必要があり、しかしながら4.23だとMatineeは既に非推奨なので、しばしば言うことを聞いてくれなくて困りました。妙なノイズが入ってしまったり、なぜかUMGの制御が上手くいかなかったり…
今試してみたところUE4.24.1では問題ないみたいです(ただし名前がTimeDilationTrackに変わっています)。みんな、最新版を使おうぜ!
・CineCam付きActor
カラスとテントウムシのチェイスシーンではテントウムシのActorにSpringArmとCineCameraをくっつけて撮影しています。これがかなり良くて、とてもダイナミックなシーンが撮れました。
SpringArmのTransformを操作することで、カメラをずらしたり回転させることもできます。ただ今回Meshの子コンポーネントとして実装してしまったため、Meshを回転させるとカメラが動いてしまうという問題が起きてしまいました。とはいえ一緒に動かしたい場面もあるのでRootComponentを作成してそこに紐付けしたものを別に用意するといいかもしれません。Sequencerがまたゴミゴミするかも知れませんが…
・詰め込みすぎ
さてここからはちょっと反省っぽくなります。動画を見ていただければすぐに分かるのですが、1分という時間にまとめるには無理がある内容になっています。起きていることも速すぎて何しているのが分かりにくい、いや分からない。制作のある時点ではもっと長い内容になる予定でした。まあ正直これは経験不足ですね。1分という時間がどれぐらいの内容を詰め込める長さか、というのが自分の中でイメージできていなかったのでしょう。もう一つの原因としてSequencerで操作し続けていると時間感覚がおかしくなっていくというのがあります。制作している本人は既に内容が分かっている上に、1コマ1コマ見ることができるため、人にもよると思いますがどうしても色々速くなりがちです。次はもっと余裕を持った映像にしたいですね。
・自作で効果音を作ろうとしたけれど…
音に関しての問題です。当初この動画には風切り音のような効果音を付けるつもりでした。しかし風切り音の素材が見つからない…フリー素材やMarketplaceを覗いてみましたが結局あまり合う音が見つけられませんでした。ならもうしょうがないなと風立ちぬばりに自分の「吹く」音を録音し、それを加工してなんとかしてやろうとしました。
結果全然駄目でした。
まずかなり静かな環境でも何かしらノイズが乗ってしまいます。加工で何とかしようにも、「ノイズはどんなに加工してもノイズ」という事実しか分かりませんでした。あと普通に"息"っぽいです。風切り音に全然聞こえません。これもいくら加工しても消すことはできませんでした。音質も問題で、動画のトーンと合わないです。
結果としてカラスの鳴き声だけが乗るという寂しい結果となってしまいました(鳴き声がトリガーになっている演出があるので削るわけにいかなかった)。
BGMに関しても上手くできたとは全く思っていません。というかコンポジションを考えるならBGMは最初の方に決めるべきなんですよね。完全に後付けなので、シーンと合ってないこと甚だしいです。
・半透明とDoF問題がおきてるみたいですけど…
うるさいな!俺も気にしないようにしてるんだよ!
テントウムシのアセットの羽根の部分に半透明マテリアルが使用されており、DoFによる演出も多用しているため、これによって半透明マテリアルとDoFとの問題が起きています。具体的に言うとピントがテントウムシに合っている間は羽根はぼけ、ピントが背景に合うと羽根にフォーカスするという訳のわからない状態になっています。そしてこれは(詳しいことは省きますが)UE4側の仕様です。
ぶっちゃけこれ解決方法とかあるんでしょうか?色々調べて解決法を探ってみましたが、「今回は使えないよなあ」と感じるものばかりで駄目でした。大体そんなインスタントに解決するような問題ならEpicが既に実装しているでしょう。
まあ、ほら…羽根が動くスピードが速いから、みんな気付かないでしょ、きっと…
・Megascansについて
Megascansはとても凄いアセットなのですが、結構使い方を選ぶ必要はあるなと感じました。まずいくら高解像度だからといってメッシュに対して限界まで接写するような使い方はしない方がいいです。テクスチャは問題ないのですが、それよりも先にポリゴンの粗さが目に付きます。それからMaterialInstanceのパラメーターを調整しないとリアリティがちゃんと出ません。特にNormalIntencityとRoughnessです。今回、草系のアセットならNormalIntencityは2~3、Roughnessは0.4程度にしています。
・完成させることができてよかった
しかしまあとにかく完成させることできて本当に良かったなあ、と。なんでも完成させるということにはエネルギーが必要で、想定外のことでつまづいたり作り直したりしていると「これってもしかしてもう完成させられないんじゃなかろうか?」という漠然とした不安に襲われます。
そんな時はとにかく誤魔化しでもなんでもいいんで先に進むことがいいんじゃないかなあ、と。多分どんな達人でも「ここをこうすればもっと良かったのになあ…」と思いながら進んでいくんじゃないですかね?勝手な推測ですけど。
・最後に
誰かLandscapeMaterialの作り方教えて下さい!いや真面目に今回アセット(Megascansですらない)から取ってくっつけただけだなんで…