2016年3月13日日曜日

Floating Menu & Ballon Widget

詳しい説明は時間が出来たときに書きます。
とりあえず簡単な説明だけ。

(2016/03/21)
SWF側に機能を追加しました。
 ・SWFからPapyrus関数やイベントを呼び出せるようになりました。(ただし戻り値取得は不可)
 ・SWFに音を鳴らす機能を付けました。
 ・SWFでキー入力を拾えるようにしました。

使用方法は・・・使用例(BalloonWidget)を見て悟ってください。



FloatingMenu

「フローティングメニュー」を追加するMOD。
アクターやオブジェクトの位置にウィジェットを表示できるようになります。

※ ただし、これ単体では何の役にも立ちません。


要skse 1.7.3以上、SkyUIは不要

(MODのダウンロード)
https://drive.google.com/open?id=0B6YyKRZ4dV_2MnJ3VTVEblJYMkU


BalloonWidget

FloatingMenuの使用例です。
アクターが喋ったときに、頭上に吹き出しウィジェット表示します。

(MODのダウンロード)
https://drive.google.com/open?id=0B6YyKRZ4dV_2R003V0JRRmJFSWc


見た目はこんな感じ。
https://twitter.com/himika/status/693827749689995265





Floating MenuとBallon Widgetで使っているdllとswfのソースファイル
https://drive.google.com/open?id=0B6YyKRZ4dV_2MVI3b05mQkxTSFk





 まだ作ってる最中なので、バグ多いです。API等の仕様も変わると思うのでご了承ください。
あと、飽きたら唐突に終わると思います。終わったらゴメンナサイ。

SkyUIではHUD Menu上にウィジェットを追加していますが、こちらはFloating Menuという名前の新規メニューを作り、そこにウィジェットを追加します。HUD Menuとは「完全に別」なので、HUD Menu上のコンパスや各種バー、SkyUIのウィジェットとは干渉しません。






ウィジェットを自作する方へ。
ものすごくハマりそうなポイント。

(1)
セーブ&ロードやエリア移動などでローディング画面を挟むと、自動でウィジェットのswfをロード・アンロードする仕様になっています。
(その際、パピルススクリプトに OnWidgetRemoved と OnWidgetLoaded を飛ばします)

swf側ではデータを保存しておくことができないため、再ロードによって真っ白の状態に戻ります。
swfの表示に必要なデータはパピルス側で変数に保存しておき、OnWidgetLoadedイベントを受け取ったら、データを再送信してください。


(2)
FloatingWidget.Destroy() でウィジェットを削除しても、削除したFloatingWidget上でスクリプトが動いている間は、メモリ上に残り続けます。ただし、スクリプトのselfはウィジェットをくっつけたObjectReferenceから完全に切り離されてNoneになります。
削除後のselfを主体にした呼び出し(例えばself.GetPositionX()など)は全てエラーが出るので注意してください。


(3)
FloatingWidgetManager.GetWidget() 関数は、最初に見つけたFloatingWidgetを返します。
同じオブジェクト/アクターに複数のFloatingWidgetがくっついていた場合は、最初に見つけたものしか取得できませんので、注意してください。


(4)
FloatingWidgetでは、RegisterForUpdate系の関数やイベントを使わないほうが良いです。

ひとつのアクターに複数のスクリプトがついている場合、発生するイベントはスクリプト同士で干渉します。(これはパピルス自体の仕様なので、どうしようもないです)
たとえば、FloatingWidgetでOnUpdateイベントが発生すると、他のスクリプトにもOnUpdateイベントが発生しますし、さらには、UnregisterFor~()でイベントを止めると、他のスクリプトのイベントも止まります。
ユニークなアクターには、たいていの場合、死後に死体を消すためのスクリプトWIDeadBodyCleanupScript がついています。またAIパッケージで後からスクリプトがくっつくこともあります。例えばNPCが何か物をはこぶ際はCarryActorScriptが付きます。場合によっては、
同じアクターに複数のFloatingWidgetを貼り付けることもあるでしょう。

これらすべてで干渉するため、安易にRegisterFor~()を使うと思わぬ誤動作の原因になります。

回避策ですが、ReferenceAliasやActiveMagicEffectなら、くっついた先のObjectReferenceやActorと干渉しません。同じActorにくっついたReferenceAlias同士で干渉することもありませんので、RegisterFor~系の関数はReferenceAliasやActiveMagicEffectをつけて、そちらで使うのが良いと思います。

余談ですが、ReferenceAliasには別の問題が存在します。
ReferenceAliasをアクターにくっつけると必ずEvaluatePackage()が発生し、AIパッケージが再計算されるのですが、アクターの行動中にEvaluatePackage()がおきると、実行中の行動に支障が生じて中断されることがあります。
それがストーリーイベントの重要なシーンだったりすると、 最悪の場合、そのままクエストが進行不能になる危険性があります。