スカイリムでは、モーションの壁についてはFNISという偉大なModが早々に制約をぶち破り、発売から7年が経過した今では、プロの犯行かと思われるような秀逸なアニメーションが溢れかえっていますが(でも釣りモーションは探せども無いです。誰か作って!)、残念ながらUIの方面については、二、三年前から進化が止まっているように思います。
いい加減、SkyUIのMCMと覇権を競うくらいの新たなUIのフレームワークが出てきても良さそうなものだと思うのですが……いまだにMCM一択というのが残念でなりません。
また、メニュー追加系のUIでは、RaceMenuやEFFの作者さまがお作りになったUIExtensionsがよく使われているなあという印象です。こちらは特にリストメニューがよくできているので、Modderに好まれるのもさもあらん、という感じ。
これはスカイリムのデフォルトのリストメニュー(Message Box)
デフォルトのUIのリストメニューの致命的な欠点は、選択肢が横並びなところです。横並びだとスペースの都合上、選択肢の個数が限られてしまうし、選択肢の内容も長いテキストは使えません(日本語のような全角文字だとますますつらい)。
デフォルトのメニューの利点ですが、唯一上げるとするなら、本体がMessageというオブジェクトなので選択肢のテキストがローカライズしやすい、という点でしょうか。
しかしそのせいでスクリプトだけでは完結しない(選択肢をCK上で追加しなくてはならない)ので、そこが不便といえば不便です。
ちなみにリストメニューを追加するUI Modとしては、SkyUIの作者さま作の「SkyUILib」についても、私としてはお勧めしたいところです。
字幕ModもDirectorもかな入力メニューも、私はこのModのソースを見ることができたから、Flashファイルの中身まで全部公開してくださっていたから作ることができました。
そんなわけで私にとっては師匠ともいうべき存在なので、ついでに紹介させていただきます。
SkyUILibのリストメニューはデフォルトのメニューの横並び状態を
縦並びにしただけのシンプルな作りです。
SkyUILibのリストメニューの利点としては、バニラのデフォルトのメニューと同様に、メニューの上にタイトル的な一文を追加できるという点がポイントです。縦並びにしただけのシンプルな作りです。
UIExtentionsのリストメニューだと選択肢オンリーなんで、「○○○について選んでください」というような補足説明が表示できないんですよね。
スクリプトもSkyUILibはバニラのMessageの「show()」と同じ感覚で使えます。
ところでスカイリムでは、こういったUI追加Modの実装方法を解説したドキュメントやチュートリアルのようなものって、全くといって良いほど見かけないですね。
汎用のMCMの実装方法については、作者様が自らGitHubのWikiにまとめてくださってますけど、SkyUILibもUIExtentionsも「ソースを見て察しろ」という状態です。
Modderの皆さんはどうやって使い方を会得してるんでしょうか。
皆さん、このくらいは説明なんかなくったって楽勝だろ、というレベルなのでしょうか。
少なくとも私は全然わかりませんでした……特にUIExtentionsは。さっぱり。
そんなわけで手探りで解読したUIExtentionsのリストメニューの実装方法を書いておきます。
■UIExtentionsのリストメニューの実装方法
UIExtentionsのリストメニューの最大の特長は、選択肢にさらに子の選択肢を簡単に追加していけるところです。この子選択肢の機能があるからこそ、多くのModで採用されているのではないかと思います。UIExtentionsはフレームワークのスクリプトだけでなくUI自体のクライアントとなるespが独立して存在していますので、前提必須ModとしてUIExtentionsを導入した上で、下記のようにして自作Modの任意のスクリプト内でUIExtentionsのリストメニューを読み込みます。
UIListMenu listMenu = UIExtensions.GetMenu("UIListMenu") as UIListMenu
というてもリストを用意するには「AddEntryItem」という関数さえ知っていればOKです。
たとえばこんな感じの構造のリストメニューを作りたいとします。
最初に「メインメニュー1」「メインメニュー2」「メインメニュー3」の3つの選択肢があって、そのうちの「メインメニュー2」「メインメニュー3」からはさらに分岐する。
「メインメニュー3」のサブメニューの2からは、さらに子の選択肢が3つ派生する。
このような構造のリストの作りたい時はこういう記述になります。
エディタのスクショ画像を貼ったので少し見ずらいですが。
でも「AddEntryItem」という関数だけで指定できてしまっているのが分かりますでしょうか。
「AddEntryItem」という関数で選択肢を追加すると、追加した選択肢は追加した順に0から通し番号が振られます。
(「AddEntryItem」関数の戻り値として取得することができます)
その番号を「entryParent」や「entryCallback」といった引数の値に使うことで、選択肢の分岐を指定するのです。
「entryParent」は自分の親の選択肢の番号、「entryCallback」はおそらく最初の分岐の根元の選択肢の番号です。また子の選択肢を持つ選択肢については「entryHasChildren」の値を「true」にしてあげます。
上記のサンプルではわかりやすいように選択肢のテキストを直接ぶちこんで個別に記述していますが、もちろんstringの配列でwhileなどを使ってまとめて記述してOKです。
選択肢の準備ができたら、あとは「OpenMenu()」関数でメニューを開き、選ばれた選択肢の情報を取得します。
「GetResultInt」で選んだ選択肢の通し番号が分かります。
また「GetResultString」で選んだ選択肢のテキスト自体も取得できます。
こうして書くと、結構単純な作りなのですが……何も説明がないので、どういうしくみなのか解読するのにえらい時間がかかりました。
せめてソースにコメントくらい書いておいて欲しいです。