2014/03/03

憧れのHUDウィジェット

ちょうど一年くらい前、「MCM」というスカイリムのUIを拡張した素晴らしいシステムを目にした時から……いや、このゲームのユーザーインターフェースが、あのFlashでデザインされていると知った時から、おばちゃんはずっと自作のFlashファイルをゲーム画面に表示させてみたいなあ、と思っていました。
別にこういうものが作りたい、という具体的な野望があったわけではないんですが、あのwebでよく使われている(今はそうでもないけど)Flashのコンテンツがスカイリムの中で再生できちゃうなんて、すごいじゃないですか。何でもいいから表示させてみたいじゃないですか。
でも、そんな思いつきを簡単に試せるほどスカイリムのUIは甘いものではなくて、「Unofficial Skyrim UI SDK(スカイリムのUIのFlashソースキット)」を勢いでダウンロードしてみたものの、おばちゃんには何をどう弄ればよいのかさっぱりわからず、あっさり挫折してしまいました。
だいたいそんなに簡単に弄れるような代物だったら、もっと巷にはUIを大改造するModが溢れている筈ですもんね……おばちゃんごときが手を出すなんて、百万光年早かった。

でも先日、SkyUIのWikiを眺めていたら、ちょっと気になるものを見つけまして。
この「Widget Framework」って何かしら……「Meter Widget(メーター ウィジェット)」ってなんぞ?
Meter Widget (※いつもの如くおばちゃんが勝手に意訳したものなのであまり信用なさらぬように…)
・「MyMod_MyMeter.psc」という新規スクリプトを作成する。
このスクリプトは「scriptname MyMod_MyMeter extends SKI_MeterWidget
(SKI_MeterWidgetスクリプトの拡張スクリプト)にする。
・「MyMod_MyMeter」のスクリプトをクエスト(MyMeterInstanceとする)につけて、プロパティをセットする。
・メーターをコントロールするスクリプトに「MyMod_MyMeter property MyMeter auto」を追加する。
・「MyMeterInstance」クエストの「MyMeter」(のプロパティ)を満たす。
これで「SKI_MeterWidget」のプロパティのすべてにアクセスできるようになり、メーターを実行したり更新(アップデート)したりできる。
インスタンス(MyMeterInstanceクエストのこと?)からメーターのパーセンテージを更新したい時は、「myMeter.Percent = percent as float」を呼ぶ。
これは……この「Meter Widget」って、もしかしてHUDで表示される体力とかマジカとかのバーみたいな奴のことでしょうか?
まさかあのメーターがCK上で自由に追加できたり、Papyrusスクリプトで動かしたりできるの?
ええー!マジで?!すごい!これはぜひ挑戦してみなくては……!
…というわけで、あれこれ試行錯誤して何とかHUDメーターが出せるようになりました。

ちなみにこのHUDメーターのFlashファイルは、GitHubのSkyUIのページのリポジトリのソースファイル一式を使わせていただいています。
それからmeterウィジェットのCKでの作成方法やスクリプトは、「Lovers Lab」というサイトのフォーラムで見つけたh38fh2mfさんという方のカキコミを参考にさせていただきました。
(ちなみにご存知かと思いますが、上記はアダルトなサイトですので閲覧の際はご注意くださいませ)
そんなわけで、そちらを参照していただければわざわざ作成方法をまとめなくても良いような気がするんですが、たぶん一ヶ月もしたら、おばちゃん自身が作り方をすっかり忘れてしまうような気がするので、念のために手順を書き残しておきたいと思います。

■SkyUI meterウィジェットの作成方法

(1)まず、HUDメーターのFlashファイル、「meter.swf」をSkyUIのソースキットを使って作成します。
(個人で試す分には、他人様が作ったmeter.swfを使わせていただいてもよいかもしれませんが)
SkyUIのソースは、上記にも書きましたがGitHubのSkyUIのページからダウンロードすることができます。

SkyUIのソースファイルをダウンロードしたら、次にこちらのページからTweenLiteというトゥイーンアニメーションを簡単に実行するためのFlashのアクションスクリプト用のライブラリをダウンロードします。
スカイリムのUIのデータはみんなそうですが、SkyUIのFlashファイルも使っているのは「ActionScript2.0」ですので、AS2用のライブラリを間違えずに入手しましょう。
ちなみにおばちゃんは同ページの下の方にあるFAQのところの「Download AS2」と書いてあるところからダウンロードしました。

TweenLiteのライブラリをダウンロードしたら、解凍してできた中の「gs」フォルダをSkyUIのソースファイルの「src/HUDWidgets」フォルダに入れます。
そして「src/HUDWidgets/skyui/widgets」フォルダ内にある「WidgetBase.as」をテキストエディタで開いて、冒頭の二行を以下のように修正します。
import com.greensock.TweenLite; → import gs.TweenLite;
import com.greensock.easing.Linear; → import gs.easing.Linear;
これは何をしてるのかというと、インポートするTweenLiteのライブラリのパスを書き換えてるわけです。
TweenLiteの方を「com.greensock」の指定に合わせて書き換えてもいいんですが、ひとつのファイルでは済まなくてややこしいので、上記のように「WidgetBase.as」の方を変えちゃった方が早いです。

TweenLiteライブラリの準備が済んだら、「src/HUDWidgets」フォルダの中にある「meter.fla」をFlashで開きます。ちなみにFlashの最新版であるCCでは、なんと、ActionScript2.0が完全廃棄されましたので、FlashはCS4~CS6のどれかを使う必要があります。(ちなみにおばちゃんはCS6を使いました)
これがSkyUI謹製?のmeterウィジェットのflashの元ファイル…「meter.fla」です。

それから「meter.fla」だけでなく他のFlashファイルも全部そうなんですが、Flashファイルは、SkyUIのソースの階層そのままの状態で、ファイルを勝手にどこかに移動させたりせずに作業する必要があります。
Flashファイルは単体で構成されているわけではなく、他の場所にあるスクリプトと複雑に連携していますので、ファイルの階層が変わってしまうとうまくパブリッシュできません。
くれぐれもダウンロードして解凍した時の状態を変えてしまわないようにご注意ください。

またFlashファイルを開く時にフォントがないよ、というお知らせが出ますが、それは無視して大丈夫。
「meter.fla」を開いたら、そのまま特に設定を弄る必要はなく、「書き出し」>「ムービーの書き出し」を行ってパブリッシュ(※swfファイルを書き出すこと)します。
ムービー(swf)を書き出す時には、「コンパイルエラー」のパネルを開いて、念のため何かエラーが出ていないかどうか確認してください。
書き出した「meter.swf」はスカイリムのDataフォルダの「Interface/Exported/Widgets」以下に適当なフォルダ名を作って、その中に入れます。

(2)「meter.swf」の用意ができたら、次はPapyrusスクリプトを用意します。
 meterウィジェットで使用するスクリプトは主に以下の3つです。
(A) meterウィジェットそのものを定義するための、基幹のスクリプト
(B) 使いたいmeterを設置するために、meterごとに個別につけるスクリプト
(C) 実際にmeterを運用・更新するためのスクリプト
(A)は上でリンクを張ったh38fh2mfさんという方のカキコミの、「First create new script, call it "MyWidgetMeter", here is the contents:」という行以下にある、「MyWidgetMeter」というスクリプトがそうです。
このスクリプトはSkyUIの「SKI_WidgetBase」スクリプトをmeterウィジェット用に拡張したもので、meterの表示幅や高さ、バーのグラデーションの色、値と連動するパーセンテージの定義などを行っています。

(B)は同カキコミの「Then create next script, for example if you want to make hunger bar you could do this:」という行の以下にある「 MyHungerBar」というスクリプトです。
これは(A)のスクリプトを拡張したもので、これをCKでクエスト上に設置することで実際にmeterをゲーム中に表示・制御することになります。
また、このスクリプト内で自分で用意した「meter.swf」の場所を記述しておきます。
ちなみにFlashファイルの場所の指定は「Data/Interface/Exported/Widgets」は省略です。
つまり「Data/Interface/Exported/Widgets/obachan/meter.swf」なら、「obachan/meter.swf」になります。

最後に(C)は、meterウィジェットを設置するクエストにつけて初期設定を行ったり、またOnUpdate()でmeterのバーのパーセンテージを随時更新するためにつけるスクリプトです。
これは同カキコミの「So create another script:」という行以下にある「HungerBarUpdate」というスクリプトになります。
ちなみにこのカキコミのスクリプト例では「myGlobalValue」というGlovalValueの値を2秒ごとに確認して、meterのバーの長さに反映するスクリプトになっています。

…以上の3つのスクリプトを用意したら、pexファイルにコンパイルしておきます。
(B)のFlashファイルの場所さえ正しく書き換えれば、あとは上記のカキコミ元からソースを単純にコピペしただけでも動くはずです。
ちなみに当然ですが、このスクリプトはSkyUIとSKSEのスクリプトソースを導入した環境じゃないとコンパイルはできませんよ。

(3)CKでmeterウィジェットを実行するModを作成する。

さて「meter.swf」と3種のスクリプトの用意ができたら、あとは完成まで60秒です。
あ、CKを起動するだけで30秒くらいたっちゃうか……ま、作業時間約1分といったところですね。
さてCKを起動したら、まず適当な「GlovalValue」を新規作成します。
これは前述した「myGlobalValue」というGlovalValueのプロパティに使われるもので、meterの数値(バーの長さ)になるものです。
これはゲーム中で動作を確認するため、コンソールで指定しやすいように、入力しやすい簡単な名前にしとくのがよいでしょう。
また数値はバーのパーセンテージになりますので、0~100の間で適当にいれときます。

次にmeterをとりつけるクエストを作成します。設定はこんな感じで。

クエストを作成したら、一旦クエストのウィンドウを閉じて、Modをちゃんと保存しましょう。
(保存しないと、クエストはうまく設定できない箇所が多いんですヨ)
しっかり保存したら、もう一度このクエストを開いて、「Scripts」のタブを開きます。
そしてあらかじめ用意してある「MyHungetBar」と「HungerBarUpdate」の2つのスクリプト(BとC)をこのクエストにAddしてください。

スクリプトをAddしたら、またこのクエストのウィンドウを閉じて、一旦セーブしてください。
(一旦ウィンドウを閉じたり保存したりしないと、クエストはうまく設定できない箇所が多いんですヨ)
んで、またクエストを開きなおしたら、「HungerBarUpdate」のスクリプトのプロパティを設定してやります。
こんな感じね。

「HungerBar」というのは、このスクリプトをAddしたクエスト自身を指定します。
それから「myGlobalValue」の方は、30秒前に作ったGlovalVariableをセット。
これでCKの作業は完了です。ゲームを起動してHUDメーターが表示されてるか確認してみましょう。
(当然ですがSKSEとSkyUIを一緒にロードしないと動きませんよ)
コピペしたままの状態でしたら、こんな風に画面左上に黄緑のバーが表示されるかと思います。

コンソールでmeterバーの数値として作成したGlovalValueの値を変えてやると、バーが連動して動くのが確認できるかと思います。
ちなみにこのmeterの表示位置、大きさ、バーの色なんかは、スクリプトの設定で変えられます。
ざっとでしたが、meterウィジェットの基本的な設置方法は以上です。


ところでmeterウィジェットと同じSkyUIの「HUDWidgets」に「arrowcount」というサンプル用?のウィジェットが入っているんですが、これが非常にシンプルな作りをしていまして、これを見ていたらどうすればCKのスクリプトからFlashファイルが制御できるのか、ほんのちょっとだけですが、SkyUIのHUDウィジェットのカスタマイズ方法がわかったような気がしました。
たとえばこんな感じのタイムラインの内容を持つ「atmos」というムービークリップが
rootのArrowCountWidgetムービークリップの直下に配置されてるとします。

Papyrusスクリプトから、このFlashファイルの「atmos」ムービークリップを制御するには、このFlashファイル(arrocount.fla)をコンパイルする時に読み込むことになる、「src/HUDWidgets/skyui/widgets/arrowcount」内に入ってる「ArrowCountWidget.as」というアクションスクリプトファイルを編集します。
まずは「STAGE ELEMENTS」が定義されている場所(デフォルトのArrowCountWidget.asでは「countText」というテキストフィールドが設定されてます)に、「public var atmos: MovieClip;」と追加して、「atmos」のムービークリップを登録します。
んで、名前はなんでもいいんですが、「atmos」ムービークリップを操作するための関数(仮にcallAtmosとしました)を「ArrowCountWidget.as」内に設定してやります。
// @Papyrus
public function callAtmos(a_num: Number): Void
{
if (a_num == 1){
atmos.gotoAndPlay("atmos1");
}else if(a_num == 2){
atmos.gotoAndPlay("atmos2");
}else if(a_num == 3){
atmos.gotoAndPlay("atmos3");
}else if(a_num == 4){
atmos.gotoAndPlay("atmos4");
}else{
atmos.gotoAndStop("non");
}
}
ちなみにこれはアクションスクリプトですが、別にそんなの深く知らんでも大丈夫です。
現におばちゃんが知ってるのは「gotoAndPlay」と「stop」くらいですが、GIFアニメに毛が生えた程度のアニメーションだったら、その二つを知ってりゃ大抵なんとかなります(ホントか?)
ちなみに「atmos.gotoAndPlay("atmos1")」というのは、「atmos」ムービークリップの「atmos1」というラベル位置からフレームを再生しろという命令です。
つまり「callAtmos」で「1」が入力されたら「atmos1」を、「3」が入力されたら「atmos3」のラベル位置からFlashアニメが再生されるように定義しておいて、これをPapyrusスクリプトから呼んでやるわけですね。
一方、このFlashファイルを動かすための、Papyrusスクリプトの記述はこんな感じになります。
int Property atmosChange
int function get()
return _atmos
endFunction

function set(int a_val)
_atmos = a_val
if (Ready)
UI.InvokeInt(HUD_MENU, WidgetRoot + ".callAtmos", _atmos)
endIf
endFunction
endProperty
これはSkyUIの「misc」フォルダの中に「PN_ArrowCountWidget.psc」というArrowCountウィジェットを定義するためのスクリプトが入ってますので、それをベースにして作成します。
meterウィジェットのスクリプトでいったら、(A)にあたるのがこの「PN_ArrowCountWidget」スクリプトです。
つまり、このウィジェットを定義するスクリプト内に、Flashファイルの「callAtmos」にアクセスするための新たなプロパティを設定してやれば、SkyUIのウィジェットのしくみを通して、Flash ファイルが操作できるようになるわけなんですね。
ちなみにこのスクリプトを見ると一目瞭然なんですが、SkyUIのHUDウィジェットって、ゲームデフォルトのHUD画面に追加というか便乗するような感じで表示させてるようです。
(つまりNPCと対話するダイアログ画面になると、HUDがきかなくなるので表示されない)
SKSEのUIスクリプトって、どういう使い方するんだろう…と今まで謎に思ってましたが、この手の拡張したUIを制御するために使われてたんですね……目からウロコです。

ArrowCountウィジェットを改造して作った雰囲気HUD

よく考えたら、空気の読めないノルドの国、スカイリムでは意味のないHUDでした……