The Loomの話はこれで最後です。
今回はMatineeを使っている部分の解説と小ネタ集です。
まずオープニングは小ネタを2つ。
1つ目はDestructibleMeshです。
The Loomでは一部のオブジェクトをDestructibleMeshに変換して破壊を行っています。最終レベルの机や本棚、皿などがそれですね。
しかし、DestructibleMeshに問題があるため、女神像だけは使用していません。
この問題というのはデカールが有効にならないという点です。
DestructibleMeshは物理挙動が前提となっているためか、動かすことを前提としていないデカールが貼りつけられなくても仕方ないかとは思います。
今回のゲームでは破壊された女神像を動かす必要がないのでStaticMeshとして配置することにしました。
もちろんDestructibleMeshはStaticMeshではないので、そのままではStaticMeshとして配置できません。
そこで、一旦Exportして各部位を切り出して別のモデルに保存、これを再度ImportしてStaticMeshとして配置しました。
まずは分割数5のDestructibleMeshを女神像のメッシュから作成、ランダムシードはいい感じに分割してくれるものを選択しました。
これをFBXでExportすると女神像の全体メッシュ+各分割メッシュが1つになったメッシュと分割メッシュを動かすためのボーンになります。
ただ、Mayaならどういう感じで出力されてるかわかりやすかったのですが、Blenderではさっぱりわかりませんでした。
Blenderはやっぱりあれだなぁ…
小ネタその2はLevel06から配置されている赤い光源です。
当然動的ライトですが、動作自体は鎖の物理シミュレーションを用いています。
本来、この手のループ再生させるべきものはループモーションを作成させるべきなのですが、モーション作成する能力が自分にないので物理シミュレーションで動かすことにしました。
しかしまあ、当然と言えば当然なのですが、物理シミュレーションで動かすと摩擦の問題もありますのですぐに止まってしまいます。
最初は普通に振り子の方程式で動かしてみたのですが、鎖がしならずにそれっぽい動きになりませんでした。
ですので、物理シミュレーションを用い、定期的にImpulseを与えてやる形で実装しました。
何度も言いますが、本来こんな実装はしません。普通に作成する場合は素直にモーションを作成しましょう。
実装のBPグラフは以下の通りです。
ユーザが設定するのは揺れる力と方向で、力を与えるべきソケットがアクターの基準位置より揺れる方向に存在した場合に1秒間だけその方向にImpulseを与えます。
Impulseを与え終えたら揺れる方向を逆方向に向けておけば物理的に動いた後で揺れる方向に少しでも傾いたところで再び1秒間のImpulseが与えられます。
イマイチわかりにくBPグラフかもしれませんが、やっていることは非常に単純です。
与える力はいろいろ調整して行っていますが、調整パラメータは変数としてBPに設定しています。
物理的な動作はレベルエディット中に確認できませんが、ゲームを実行して確認するのは面倒です。
そこで適当なレベルに配置し、Simulateを行って確認します。
Simulateはこのような、ちょっとした動作だけを確認したい場合にPlayより簡単に行えるのが便利ですね。
というわけで小ネタはこれで終了。
続きからはMatineeの話です。
最後はMatineeの話。
主にカットシーンを作成するためのMatineeですが、今回はやっぱりカットシーン的なものに使用しています。
Matineeは2種類あって、1つはドアを開けるアクション、もう1つはエンディングのカットシーンです。
ドアを開けるMatineeはちょっと特殊です。
Matineeで動かしているのは4つのTargetPointアクターのみです。
こんな感じです。
4つのTargetPointは2つが入口ドア、2つが出口ドア、それぞれのうち1つがPlayerPawnの移動用、もう1つがPlayerPawnの注視点です。
以下の図は入り口側のTargetPointの初期配置図です。出口側も同様の形で配置されています。
プレイヤーが扉に近づくと扉を開いて中に入るイベントが開始されます。
以前に解説したとおり、この段階でIsInteractフラグが立ってPlayerPawnの動作が奪われます。
PlayerPawnはMatineeイベントの開始を通知され、移動を示すMoveTargetと注視点を示すLookTargetを渡されます。
IsInteractフラグが有効な間はMoveTargetのXY座標を取得してそこに移動、LookTargetにカメラを向けるように処理しています。
なぜMatineeによるカメラ操作を行わなかったのかというと、理由は2つあります。
1つはフラッシュライトが追随しないことです。
当初の仕様ではフラッシュライトは常にONでしたので、Matineeによるカットシーン中もフラッシュライトがカメラに追随しなければいけませんでした。
このMatineeを作成した段階ではフラッシュライトの位置は確定しておらず、その状態でMatinee用のフラッシュライトを配置するのは大きなリスクでした。
もう1つはFPSカメラからMatineeカメラに移行するときは補間がいい感じに進むのですが(エンディング参照)、MatineeカメラからFPSカメラに戻る時がきちんと補間されずにパッと移動してしまう状態になったからです。
Matineeカメラの最終フレームとそこに移動したFPSカメラの位置が完全に一致すればこの問題も解決できるのですが、数値を完全に合わせるのが少々難しかったのです。
結局、このような問題に対処するよりはPlayerPawnを動かした方がいいんじゃないかと思ったのでこの仕様となりました。
さて、実はドアオープンのイベント矩形を踏んだ段階ですぐさまMatineeが再生されるわけではありません。
この段階ではMatineeは0フレーム目にフレーム移動をしているだけで再生されていません。
なぜ再生しないのかというと、PlayerPawnが所定の位置(0フレーム目のMoveTargetの位置)に移動し、所定の注視点(0フレーム目のLookTargetの位置)を見るようになるまで待っているのです。
このような処理をしている理由はイベント矩形を踏んだ際の位置や注視点が予想できないからです。最悪、矩形の端の方をドアに背を向けて踏むかもしれません。
視線が露骨におかしい場合はカットシーンを開始しないようにすることもできますが、何を露骨かというと判断は難しく、結局このような処理は入れませんでした。
当初は定数フレームで回転と平行移動をさせていたのですが、そのフレーム数が短いと回転や移動が急激になりすぎるし、長いと前を向いて矩形に触れた場合に”もう補間し終わってるはずなのに始まらない”という状況になってしまいます。
大半の場合はドアに向かっていくので前を向いているはずですし、数少ない明後日の方向を向いた状況のために長いフレーム数を予約するのはよろしくありません。
最終的な判断としては、一定速度で移動、およびカメラの回転を行い、所定の位置に移動&所定の方向を見終わったらMatineeを再生するという処理を行いました。
その昔、PS2時代のJRPGを作成している時にはデザイナさんを通さない簡易カットシーンを作る必要があり、プレイヤーキャラクタたちが所定の位置に着くまで待ち、着いたら会話とかを開始するというものを作成する際に重宝した手法です。
この処理はRInterpToとVInterpToを使用していますが、これらのBP関数は移動や回転の終了を通知してくれません。
終了チェックは自前で行いましたが、結果とターゲットが完全に一致することはありませんでしたので、回転の場合はYaw-Pitch-Rollのすべて、座標の場合はXY座標が極めて近い場合は終了したとみなすようにしました。
以下の図がその処理を行うMyCharacterの関数です。
この処理の後、Matineeが再生を開始したらPlayerPawnの位置とFPSカメラはMatineeが操作するTargetPointを追うようになります。
この処理は以下のMyCharacterの関数で行います。
IsInteractがTrueの場合はE_InteractStateの状態によってこれらの関数を呼んでいます。
ここまでで説明していないのは扉を開ける処理とそもそもMatineeを再生している部分ですが、これはドアを実装しているBP_Doorです。
このBPにはイベント開始矩形が含まれているので、この矩形に接触するとMyCharacterのインタラクトを開始、その後はTickイベントでMyCharacterのInteractStateを監視し、補間状態から先に進んだらMatineeを再生します。
これらの処理がBP_Doorに入っている理由は全くありません。
MyCharacterとかに持たせてた方がいいと思うのですが、作成した際にこのBP上で作成してしまい、その上リファクタリングする余裕がなかったのでそのままになっています。
慣れの問題かもしれませんが、ビジュアルスクリプティングのリファクタリングってやりにくいですね。
扉の開閉はBP_Doorに存在する関数を呼び出すと開いてから閉じるまでの一連の流れが実行されます。
これをMatineeで実装しなかったのはMatinee以外からも呼び出す可能性があったからです。
最終的にはMatinee以外からは呼び出していませんが、悪い判断だとは思っていません。
以下がその処理です。
扉を開くMatineeは以上ですが、エンディングのMatineeはそれほど解説する部分はありません。
こちらは普通にカメラを動かし、必要に応じてSEを再生、フラッシュライトとしてスポットライトを配置して動かしています。
若干特殊かと思うのはカメラの指定方法くらいでしょう。
ヒストリアさんのこちらのブログ記事「Matineeでカットシーン制作(2)」ではDirectorグループでカメラを指定する方法が書かれています。
The Loomのエンディングではこの方法は用いず、Matinee再生と同時にSetViewTargetWithBlendを用いてFPSカメラからMatineeカメラへ補間を行っています。
Directorグループを用いた場合は瞬時に移行してしまうようなので、FPSゲームのようなゲームからそのままカットシーンに移行する場合にはあまり向かないように思います。
なお、その処理を行っているBPグラフは以下です。
というわけで、The Loomの制作の話はこれにて終了です。
すべてのBPグラフを提示したわけでもありませんが、面倒な部分はたいてい説明したつもりです。
もしもゲームを遊んでいて気になった部分がありましたらメールやTwitter、コメント等でお伝えしていただければおまけ解説を行います。