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()がおきると、実行中の行動に支障が生じて中断されることがあります。
それがストーリーイベントの重要なシーンだったりすると、 最悪の場合、そのままクエストが進行不能になる危険性があります。

3 件のコメント:

  1. Floating Menu、大変有難く使わせていただいております。
    わくわくしながら便利な機能を色々試させていただいているのですが、一つどうにもよくわからないことがありまして、詳細をお聞きできたら…と思い、書き込ませていただきました。
    文章下手なうえに、UI作成の知識も浅いため、要領を得ない質問になってしまうかと思いますが、どうかご容赦下さい。


    FloatingWidgetの「Delegate()」というネイティブの関数についてなのですが、これはBalloonWidgetに付属している「SetDialogueDate」という関数専用の処理なのでしょうか?
    私はてっきり他の自作の関数でも中身に「Delegate()」と書けば、自動的にFlash側で同じ引数を引き継いで処理が実行されるのかな?と思ったのですが、「SetDialogueDate」以外の関数だとどうにも動かなくて、これはどのように使えばいいんだろうと首をひねっております。


    ちなみに私は今のところ、ウィジェットのFlash側に搭載した関数をPapyrusから呼ぶ際には、

    UI.Invoke("Floating Menu", "_root." + widgetID + ".test_mc.testFunction")

    …のような感じでSKSEのUIスクリプトを使って呼び出しております。


    BalloonWidgetのSetDialogueDate関数のコメントを拝見すると、

    > _root.[ ウィジェットID ].SetDialogueData(...) を呼ぶ

    とあるのですが、私が作成したウィジェット内の関数をSKSEのUIスクリプトで呼ぶためには、ウィジェットIDの後ろに、ルートのムービークリップのインスタンス名を挟まないと動かなくて……それも少し引っかかっております。
    もしかしたらその違いが「Delegate()」が効かない原因なのでしょうか。


    こんなよくわからない説明で伝わりますかどうか甚だ心もとないのですが「Delegate()」について、少しでもヒントをお聞かせ願えれば幸いです。

    返信削除
    返信
    1. あああ、これ、とても分かりにくかったと思います。

      https://1.bp.blogspot.com/-OYtqEHLUwpI/Vu_dbmTDKMI/AAAAAAAABKk/73x0LbPBAqg_ZqeKbHpCgvWtVgVHsTDfw/s1600/widget.png

      こんな感じに、インスタンスで定義した関数ではなく、タイムラインのほうに埋め込んだ関数を呼び出しています。
      インスタンスで定義した関数を呼び出す場合は、この例のように、インスタンスの関数を呼び直してください。

      SkyUIのHUD Widgetのようにインスタンス名を「widget」で固定して、_root.widget.ほげほげ() を直接呼び出すほうが分かりやすくて良かったかもです。
      フレームごとに処理を変えたり、自由度が上がるかな~とか思ったのですが、そういうシチュエーションはほとんど無さそうですし・・・

      削除
    2. ああ、フレームの方に直接書いてあったのですね……!!!
      つまり私が試作したウィジェットは、BalloonWidgetのFlashのソースファイルを元に作り変えさせていただいていたからこそ、スクリプトがそのまま残って奇跡的に動いていたわけで……
      迂闊にも全然気が付きませんで、大変失礼しました。

      しかしこれでPapyrus側から簡単にUI側の関数を呼べるとわかって嬉しいです。
      SKSEのUIスクリプトだと、引数が沢山ある関数を呼ぶのって、面倒なんですよね。
      痒いところに手が届く便利機能ばかり実装して下さって、本当にどうも有難うございます。
      しかし色々できるものなのですねえ……そもそもHUDメニューのようなMenuそのものを新規に追加したりできるなんて、考えもしなかったので、とてもびっくりしました。

      削除