Interaction Animations

Mod紹介

ドアを開けたり物を拾うときにアニメーションするようになります。MilanesaZさんの作品です。

Nexusで見る

使い方

ドアをアクティベートするとプレイヤーが開く動作をします。動作の最中にもう一度アクティベートするとドアが開きます。立て続けに2回アクティベートする必要があります。

なぜ2回アクティベートする必要があるのか

Skyrimではアクティベートの際にスクリプトを動かすことが楽にできました。Fallout 4では仕様がかわっており、難しくなっています。

アクティベート処理をぶん取ると様々な弊害が発生しやすくなります。クエストアイテムを拾ったのにクエストか進まないとか、ドアをアクティベートしたのに開かないとか、色々と問題が起こります。

このInteraction AnimationsというModは、この2つの問題を回避するために、1回目のアクティベートはアクティベート処理をぶん取ってアニメーションをさせ、その最中はぶん取るのを停止するので2回目のアクティベートはバニラの処理になる、ということなのだと思います。

アクティベートの際にスクリプトを動かす方法

PerkでEntry PointをActivateにします。Entryを作って条件を並べます。すると条件を満たすアクティベートの際にスクリプトが動くようになります。

Skyrimの場合はReplace DefaultをオフにしてRun Immeditalyをオンにします。すると、アクティベートの動作はバニラのままで、それと並行してスクリプトが動きます。これが本来のアクティベート動作を阻害せず、任意のスクリプトを実行する方法です。

Fallout 4の場合は、同じように作ると、対象をクロスヘアで捉えている最中はずっとスクリプトが動き続ける動作になります。おそらく毎フレームごとに実行されているので、あっというまにスタック領域を使い切ります。これをいったい何のために使うのか、なぜ仕様をかえたのかはわかりません。

アクティベートを取得する他の方法

アクティベートされる側にスクリプトを仕込んでおく方法があります。アクティベートされる側はOnActivateイベントが発生するので、これを使うのです。この方法はドアのアニメーションをさせるのであればすべてのドアにスクリプトを仕込まなければならず、現実的ではありません。

アクティベート操作を捕捉する方法があります。RegisterForControlで監視をしてOnControlDownOnControlUpで捕捉します。問題は何をアクティベートしたのかわからないことです。SkyrimであればSKSEにて現在クロスヘアで捉えている対象を調べる関数が用意されているのですが、F4SEにはないので、この方法は使えません。

何とかしてみた

クロスヘアで捉えている物を取得する

PerkでReplace DefaultをオフにしてRun ImmeditalyをオンにしたEntryを用意します。例として「対象はドアである」という条件を設定します。

これだと対象を捉えている間はスクリプトが動き続けてしまいます。

クエスト、ReferenceAlias、キーワードをそれぞれ用意します。キーワードの名前はTargetRefとしました。このTargetRefキーワードをReferenceAliasに設定します。Perkのスクリプトでは、アクティベート対象のリファレンスをReferenceAliasにForceRefTo関数を使って入れます。これで対象にTargetRefキーワードが付与されます。Perkの条件に「TargetRefキーワードを持たない」を追加します。

これでクロスヘアで対象を捉え続けてもスクリプトが繰り返し実行されません。ほとんどの場合は2回動きます。スクリプトが処理するのに1フレーム以上かかるので、次のフレームに進んでしまい、新たなスクリプトが稼働してしまうのだと思います。

違う物をクロスヘアで捉えると、それはTargetRefキーワードを持っていないため、Perkの条件を満たすようになるので、Perkのスクリプトが動いてReferenceAliasの中身が入れ替わります。

このようにして、ReferenceAliasには最後にクロスヘアで捉えた対象が入るようになります。

クロスヘアで捉えるのをやめたことを検知する

グローバル変数IsTargetingを用意します。

さきほどのPerkのスクリプトに、グローバル変数IsTargetingを1にする処理を追加します。

PerkにEntryをもうひとつ用意します。このEntryは「対象はドアではない」「IsTargetingが1である」という条件を設定します。スクリプトを紐づけ、スクリプトでは以下のことをします。

  • グローバル変数IsTargetingを0にします。
  • ReferenceAliasを空にします。

こうすることで、ドア以外の物をクロスヘアで捉えると、ReferenceAliasの中身が空になります。またIsTargetingが0になるということはPerkの条件を満たさなくなるので、無駄にスクリプトが繰り返し実行されることもありません。

アクティベートを捕捉する

RegisterForControlでActivateを予約します。OnControlDownイベントでActivateを受け取ったらアクティベートが発生したということなので、ReferenceAliasの中身を確認して、中身が有効であれば適したアニメーションを再生します。

このOnControlDownイベントは何かをアクティベートした時ではなく、アクティベートの操作をしたときに発火します。つまり、何もない空間をアクティベートボタンを押すと、カチッというSEが鳴るだけで何も起こりませんが、OnControlDownイベントは発火してしまいます。また、ドアに関係なくすべてのアクティベート操作でイベントが発火するということは、無駄なスクリプト動作が多くなってしまうということになります。

そこで、Perk側のスクリプトを工夫して、ReferenceAliasに何かを入れたあとはRegisterForControlを実行し、ReferenceAliasを空にしたらUnregisterForControlで予約を解除するようにします。これでドアを捉えている時だけOnControlDownイベントを受け取るようにできます。

タイトルとURLをコピーしました