差分

ナビゲーションに移動 検索に移動
インターフェースの日本語化(kagi)
{{Version|timeless}}
※2025/10/6、英wikiよりkagi翻訳を通しました。機械翻訳なので一部不自然な日本語があるかもしれません。ぜひ編集してくれるとありがたいです。

CK3のユーザーインターフェース(UI)は高度に改造可能ですが、そのためUI Modはチェックサムを変更します。これはマルチプレイでのチートや非同期化を防ぐためです。

パッチ1.9以降、UI Modを導入しても実績は無効化されません。

ゲームにはGUIエディタが含まれていますが、機能が不足しています。

Modderができること:
* インターフェースの見た目のスタイルを変更する*
* ウィンドウを移動・リサイズ可能にする
* 要素を変更・削除する
* 新しいボタンを追加する
* コードからより多くの情報を表示する
* 新しいウィンドウを追加する

Modderができないこと:

* *新しいHUDスキンの作成。ゲームはMod内の新規.skinファイルを無視します

* 新しいホットキーの追加。既存のものの再利用のみ可能で、.shortcutsファイルがMod内にある場合、ゲームはそれを無視します
* 開発者が対応を入れていない限り、あるウィンドウの情報を別のウィンドウに表示すること
== 基本 ==

CK3のインターフェースはgame/guiフォルダ内の.guiファイルで作られており、htmlファイルにやや似ています。

そのため、[[Modding#Tips_.26_guidelines |VS Code、Sublime、Pulsar]]など任意のテキストエディタで編集できます。シンタックスハイライトはPythonかPerl 6を選ぶと相性が良いです。

CK3はテクスチャに.ddsファイルを使用し、game/gfx/フォルダに保存されています。多くの箇所では.pngファイルも受け付けます。

.ddsファイルの編集・保存には、Photoshop+[https://software.intel.com/en-us/articles/intel-texture-works-plugin Intelプラグイン]、またはGIMP+[https://code.google.com/archive/p/gimp-dds/downloads このプラグイン]を使ってください。

[[File:Debug launch options.png|alt=Steamの起動オプションのスクリーンショット|thumb|ファイルの自動リロード用のSteam起動オプション]]

'''起動オプション'''

ゲーム内でguiファイルを自動リロードし、コンソールコマンドを使うには、起動オプションに<code>-debug_mode -develop</code>を追加します:
* Steamでゲームを右クリックし、プロパティを選択。下部の起動オプションに<code>-debug_mode -develop</code>を入力
.guiファイルを保存するたびに、ゲーム側でリロードされます。

'''重要なコンソールコマンド:'''

* <code>dump_data_types</code> - 利用可能なすべてのGUI関数を、ログフォルダ(Documents/Paradox Interactive/Crusader Kings III/logs/data_types)のdata_types*.logに一覧出力します
検索しやすくするため、すべてのログを結合できます。

手早く行うには、そのdata_typesフォルダに以下のコードの.batファイルを作成して実行します: <code>type *.txt > ALL_DATA_TYPES.txt</code>
* <code>tweak gui.debug</code> - UI要素のハイライトを有効にします。全オプションのチェックを外し、gui.debugにチェックを入れます。ツールチップに当該要素の正確なファイルと行番号が表示されます。
* <code>release_mode</code> - 画面上にエラーカウンタを表示します。UIでエラーを素早く見つけるのに非常に便利です。コンソール下のボタンでトグルできます。
* <code>gui_editor</code> - GUIエディタを開きます。ホットキーはControl + F8

[[File:Errorhoof.jpg|alt=エラーカウンタのスクリーンショット|thumb|エラーカウンタ]]
任意機能(ホットリロードがあれば通常不要):
* <code>reload gui</code> - すべてのguiファイル、または指定したファイル名があればそのファイルのみをリロードします: <code>reload gui/frontend_main.gui</code>
* <code>reload texture</code> - すべてのテクスチャ、または指定ファイルのみをリロード: <code>reload texture flatmap.dds</code>
その他のヒント:
* エラーログを開いたままにして、コードにミスがないか確認する(同じログフォルダにあります)
* データタイプのログを開いたままにして、テキストエディタのオートコンプリートに使う
* テストにはgui/debugのtest_gui.guiが使えます。このウィンドウを表示するにはコンソールを開いてTest Windowをクリック。閉じるには<code>GUI.ClearWidgets</code>コマンドを実行。
* コードを折りたたんで構造を見やすくする。一般的なホットキーはCtrl+K, Ctrl+1(1は折りたたみレベル)

== GUI Modの作成 ==

1. ゲームランチャーを起動し、Modライブラリ→Upload Mod→Create a Modへ。タグを含め、すべての項目を入力します。

Createを押すと、<code>Documents/Paradox Interactive/Crusader Kings III/mod</code>に新しいフォルダと.modファイルが作成されます。

2. 次に、Mod内に「gui」フォルダを作成し、game/guiから改造したいファイルをそこへコピーします。

* どのファイルが必要かわからない場合は、コンソールコマンドgui.debugやGUIエディタでゲーム内から該当箇所を調べます。

== GUIの検査 ==
ゲーム内のUI要素は、gui.debugインスペクタかGUIエディタで検査できます。

gui.debugの方が高速ですが、エディタはゲーム内でのインターフェース編集も可能です。とはいえGUIエディタは非常に制限が多く、非推奨です。

どちらの方法でも、目的のguiファイルをテキストエディタで素早く開き、該当行へジャンプできます。これは少し設定が必要ですが(一度やればOKです)。

適切なテキストエディタがない場合は、VSCodeかSublimeをインストールしてください。

ゲームからguiファイルを開くには:

# テキストエディタの.exeを見つけ、右クリックして「パスのコピー」を選ぶ
# Documents\Paradox Interactive\Crusader Kings III のpdx_settings.txtを開く
# "editor"を検索し、その値にエディタのパスを貼り付けます。例: <code>value="E:\Program Files\Microsoft VS Code\Code.exe"</code>
# その下の"editor_postfix"の値に<code>:$:1</code>を追加します。例: <code>value=":$:1"</code>
# 保存します。

postfixは、該当行へジャンプするようエディタに指示するための指定です。

これでゲームを起動し、.guiファイルを素早く開けるようになります。パスはコンソールの<code>settings</code>コマンドからも変更できますが、ゲームの再起動が必要です。

=== GUI Debug ===
gui.debugはインスペクタを有効にするコンソールコマンドです。
[[File:Tweak gui.debug.png|alt=余分なオプションを外しgui.debugのみオンにしたtweakerメニューのスクリーンショット|thumb|290x290px|gui.debugのtweaker]]
デバッグ情報付きのツールチップを表示し、gui要素の選択、.guiファイルのオープン、該当行へのジャンプを可能にします。

使い方:

# <code>-debug_mode -develop</code>オプション付きでゲームを起動
# Esc下の`キーでコンソールを開く
# <code>tweak gui.debug</code>と入力してEnter
# tweakerメニューで全オプションのチェックを外し、GUI.Debugにチェック
# 以後はtweakerを開かず、gui.debugコマンドを直接使えます。コンソール下のボタンでもトグルできます。

(他のデバッグオプションは全要素をハイライトして見づらくなるため無効化します)

[[File:Gui.debug.png|alt=今週の人物カードが定義されているファイルを示すgui.debugツールチップのスクリーンショット|thumb|289x289px|gui.debugのツールチップ]]
インスペクタが有効な間、カーソル下のgui要素は緑枠でハイライトされ、ツールチップにファイル名が表示されます。後ろの数字は行番号です。

Alt+左クリックで上下の他要素を選択。

Alt+右クリックでその.guiファイルをテキストエディタで開き、当該行にジャンプします。

任意:

毎回tweakerを開かないよう、コンソールに独自ボタンを追加するのも手です。以下のコマンドを使用します:

<code>onclick = "[ExecuteConsoleCommands('gui.DebugRenderOutsideParent;gui.Debug.DrawUnderMouse;gui.Debug.DrawAll;gui.Debug')]"</code>

これで追加オプションをすべてトグルし、gui.debugを有効化します。次に別ボタンでgui.debugのみをトグルします。

また、ホットキー付きの独自ボタンを作り、scripted widgetでスポーンさせることもできます。詳細は後述のscripted widgets参照。

=== GUIエディタ ===
[[File:GUI editor.png|alt=frontend guiファイルの構造を表示するGUIエディタのスクリーンショット|thumb|373x373px|GUIエディタ]]
GUIエディタは、ゲーム内でUIを編集するための開発ツールです。

生ファイル編集より敷居が低く見えるかもしれませんが、制約はさらに多いです。

使用するには、<code>-debug_mode</code>と<code>-develop</code>オプションを付けてゲームを起動します。

エディタを開く方法:
* Ctrl+F8を押す
* `キー(Escの下)でコンソールを開き、GUI Editorをクリック
* コンソールでgui_editorコマンドを実行
'''機能'''

既定ではエディットモードが有効で起動します。上部のウィンドウ(Outliner)で無効化可能です。ホットキー「E」。

* エディットモードはブラウザの検証モードに似ています。有効中はゲームと直接の操作はできませんが、UIパーツを選択し、下部のプロパティウィンドウで変更できます。
* ホイールでフォーカス対象を切り替えます。hud.guiが他ウィンドウの上に重なりがちです。
* 薄い青枠が選択中の要素。ほかの枠を隠すにはOutlinerの「Show Hierarchy」のチェックを外します(ホットキー「L」)。
* 右マウスボタン長押しで選択要素を移動できます。
* Alt+右クリックで該当要素のファイルを開きます。右クリックで誤って要素を動かさないよう注意!
* 元に戻すはCtrl+ZまたはOutlinerのUndo。やり直しはその隣、Ctrl+Y。
* Outlinerの赤い星*は未保存の変更を示します。Ctrl+Sまたは上部の保存ボタンで保存。編集中のguiファイルがMod内にあることを確認してください。でないとゲーム本体フォルダへ書き込みます。(リセットはSteamのファイル整合性の確認)
* 開発用ウィンドウはドラッグで移動、枠のドラッグでリサイズ可能。
* Outlinerの階層でUI要素をドラッグして順序変更できます。右クリックでコンテキストメニュー。
* プロパティウィンドウで、プロパティの変更や(+アイコンから)追加が可能。

Outlinerの「Window」から、UI ComponentsとRegistered Data Typesの2つのウィンドウを開けます。
* UI ComponentsはUIにドラッグできるパレットのようなもの。gui/shared/standard.guiとgui/defaults.guiにボタン、アイコン、テキストなどの基本要素がまとまっています。
* Registered Data Typesは、ゲームデータを表示するために利用可能な関数の参照に使えます。
* Data Types右上のボタンをクリックすると、このデータをログフォルダ(Documents/Paradox Interactive/Crusader Kings III/logs/data_types)にダンプします。<code>dump_data_types</code>コマンドでも可。

注意: 誤ってテンプレートを選択しやすく、それを変更するとUI内の「すべての」インスタンスに影響します。Outlinerでどのファイルを選んでいるか注意してください。プロパティウィンドウの青いヘッダーが「type:」で始まっていたらテンプレートなので、意図しない限り編集しないでください。

== UIコード ==

CK3のUIはコンテナと、その中に入るオブジェクトで構成されています。

多くのウィンドウは<code>window</code>コンテナで作られ、マップアイコンはwidgetやhboxを使います。

ファイル内の記述順が画面上の重なり順を決めます。コードの後ろにあるものほど、より上のレイヤーに表示されます。

多くの要素はほかの要素を含められます。たとえば、ボタンの中のアイコンの中にテキストボックスがある、というように。ネストされた要素(子要素)は親と一緒に移動します。

座標は左上(画面または親)の相対位置で指定します。<code>parentanchor</code>プロパティで変更可能。利用できる値はleft、right、top、bottom、hcenter(水平中央)、vcenter(垂直中央)。<code>parentanchor = right|vcenter</code>のように|で組み合わせ可能です。

各要素は中括弧で開閉します。例: <code>container = { }</code>。

一般的なコードスタイルは、ブロックの開始と終了を同じレベルに置き、中身をタブ1つ分インデントします:

<pre>
widget = {
size = { 50 50 }
alpha = 0.5
}
</pre>

これにより構造が見やすくなり、抜け・余分な括弧に気づきやすく、エディタによってはブロック折りたたみのために必要です。

=== プロモートと関数 ===

各ウィンドウは、利用可能なコマンド(プロモートと関数)のセットをあらかじめ持っています。これらは<code>dump_data_types</code>コマンド実行後、<code>Documents/Paradox Interactive/Crusader Kings III/logs/data_types</code>のdata_types*.logにあります。

それらは、プレイヤー名・所持金・子供の数などゲーム内データの表示や、ボタンの動作設定に使われます。

プロモートはスコープ(CharacterやProvinceなどのゲームオブジェクト)を返し、関数は数値・文字列・真偽値などを返します。

グローバルコマンドはどこでも使用できます(例: GetPlayer=プレイヤーキャラを返す、GetCurrentDateなど)。

他のコマンドは対応するウィンドウ/オブジェクト内でのみ使用できます。たとえばGetParentsはキャラクターウィンドウ内でのみ使え、<code>CharacterWindow.GetParents</code>で始める必要があります。

コマンドは次のようにチェーン可能です:

<code>CharacterWindow.GetCharacter.GetPrimaryTitle.GetHeir.GetPrimarySpouse.GetFather</code>

オブジェクトは親からスコープを継承でき、上記の行を毎回書かずに済みます。例えばウィジェットの<code>datacontext</code>にこの行を設定しておけば、その中のテキストボックスは<code>"[Character.GetNameNoTooltip]"</code>や<code>"[Character.GetGold]"</code>などを使えます。

同様の仕組みはgridbox内のアイテムにも適用されます。

===== 型変換(キャスト) =====
スクリプトと違い、UI内の値には整数、固定小数点、浮動小数点などの型があります。

多くはFixedPointToFloatやIntToFixedPointといった関数で別の型に変換できます。これが型キャストです。

例えば、alphaパラメータはfloatを要求するため、必要に応じてキャストします。

===== 数値を文字列にキャストする =====
(上級者向け。学習中なら読み飛ばしてOK)

現状、数値を文字列に変えるバニラの関数はありません。これは文字列比較や、数値とテキストの連結に必要になることがあります。

回避策は、数値をローカライズに渡し、UI側で文字列として扱わせることです。

以下はプレイヤー年齢の例です:

1. common/customizable_localizationにカスタムローカライズを作成:<syntaxhighlight lang="c">
NumberToString = {
type = all

text = {
localization_key = number_to_string
}
}
</syntaxhighlight>2. guiファイルでその数値を渡します:

<code>raw_text = "[GuiScope.AddScope( 'number', MakeScopeValue(IntToFixedPoint( GetPlayer.GetAge ))).Custom( 'NumberToString' )]"</code>

GetAgeはint32を返し、MakeScopeValueは固定小数点のみ受け付けるため、まずIntToFixedPointでキャストする必要があります。別の型なら適宜調整してください。

3. ローカライズ側で保存した'number'スコープを取得:<syntaxhighlight lang="c">
l_english:
number_to_string: "[SCOPE.GetValue('number')|0]"
</syntaxhighlight>これがUI側へ文字列として渡されます。ローカライズのnumber_to_stringは、実質的に手順2の行全体を置き換える役割です。詳細は[[Customizable localization|カスタマイズ可能ローカライズ]]を参照。

手順2の関数はマクロに置き換えて短く再利用しやすくできます。

そのために、(guiの外側の)data_bindingフォルダに新しいファイルを作ります:<syntaxhighlight lang="c">
macro = {
description = "Returns the int32 as a string."
definition = "GetString_int32(Arg0)"
replace_with = "GuiScope.AddScope( 'number', MakeScopeValue(IntToFixedPoint(Arg0))).Custom('NumberToString')"
}
</syntaxhighlight>これで<code>"[GetString_int32( GetPlayer.GetAge )]"</code>や<code>"[GetString_int32( '(int32)2' )]"</code>のように使えます。

=== UIコンポーネント ===
これはすべてのウィンドウを構成する基本要素です。ゲームにはこれらを基にした多くの定義済みタイプがあり、gui/sharedにあります。

一部はUIライブラリウィンドウでプレビューできます。開くには、コンソールでrelease modeをトグルすると、上にUI Libraryボタンが出ます。

<code>window</code>

* 可動のコンテナ。移動を有効化するには<code>movable = yes</code>を追加。
* 固定サイズにも、子要素に合わせてリサイズにもできます。
* ゲーム内では<code>using = Window_Background</code>や<code>using = Window_Decoration</code>のようなテンプレートで背景を設定します。
* 子要素がウィンドウ外にあるとクリックやツールチップが効きません。<code>allow_outside = yes</code>で変更可能。
* 同レイヤーの他ウィンドウより手前に出すには、クリックで前面化します。順序操作には<code>PdxGuiWidget.StackTop</code>や<code>PdxGuiWidget.StackBottom</code>を使用。

<code>widget</code>

* windowに似た静的コンテナ。クリックしても前面には出ません。

<code>margin_widget</code>

* widgetに似ていますが、マージンでリサイズ可能。(画面サイズに応じてリサイズするウィンドウを、高さ100%・マージン約50のように設定してHUDを見せるなどが可能)

<code>container</code>

* 固定サイズを持ちません(maximumsizeは設定可)。
* 非表示要素も含め、子要素全体に合わせて自動リサイズ。<code>ignoreinvisible = yes</code>で非表示を無視可能。
* 複数要素をまとめて移動するためによく使われます。

<code>flowcontainer</code>

* 子要素を横方向に並べ、全体に合わせてリサイズ。<code>direction = vertical</code>で縦に。
* 既定では非表示の子要素も無視しません。<code>ignoreinvisible = yes</code>で変更。
* 固定サイズ不可。
* 子要素は自動配置されるため位置指定不可。
* 位置調整が必要なら、子をcontainerやwidgetで包み、その親に対する相対位置を調整します。

<code>hbox</code>/<code>vbox</code>

* 子要素を横一列に並べ、幅に沿って配置。vboxは縦版。
* 固定サイズは持てず、子要素に合わせて、または親サイズに合わせてリサイズ(レイアウトポリシー次第)。
* minimumsize、maximumsize、min_width、max_width、marginsで制限可能。
* 既定では非表示の子要素を無視。<code>ignoreinvisible = no</code>で変更可能。
* データモデル(ゲームデータのリスト化)を受け取れます。

<code>dynamicgridbox</code>

* データモデル専用。
* アイテムを縦に並べます。<code>flipdirection = yes</code>で横に。
* 既定では非表示アイテムを無視しません。<code>ignoreinvisible = yes</code>で変更。
* コンテンツに合わせてリサイズし、minimumsize/maximumsizeで制限。
* アイテムは異なるサイズ可。
* 非常に長いリストでは重くなりがち。

<code>fixedgridbox</code>

* dynamic版に似ていますが、すべてのアイテムが固定サイズ(実質テーブル)。
* データモデル専用。
* アイテムを縦に並べます。<code>flipdirection = yes</code>で横に。
* 非表示アイテムを無視できません。
* 固定サイズにも、コンテンツに合わせたリサイズにもでき、minimumsize/maximumsizeで制限可能。
* 長いリストではパフォーマンスに優れます。

<code>overlappingitembox</code>

* データモデル専用。
* アイテムを横方向に並べ、ボックスサイズを超えると重ねて表示します。<code>flipdirection = yes</code>で縦に。
* 固定サイズにも、自動リサイズにもできます。

<code>scrollarea</code>

* コンテンツがサイズを超えるとスクロールバーが出るウィジェット。
* <code>scrollbarpolicy_horizontal = always_off</code>および<code>scrollbarpolicy_vertical = always_off</code>でスクロールバーを無効化可能。
* 固定サイズにも、コンテンツに合わせてリサイズにもでき、minimumsize/maximumsizeで制限可能。
* ゲームファイルでは主に<code>scrollbox</code>タイプ(背景・スクロールバー・マージン付きのscrollarea)を使います。

<code>button</code>

* クリック可能なオブジェクト。<code>onclick</code>や<code>onrightclick</code>を受け付けます。
** 右クリック機能を追加する場合、<code>button_ignore = none</code>を含めてください。
* 既定ではテクスチャなし。
* 固定サイズにも、子要素に合わせてリサイズにもできます。
** サイズのないボタンは、不可視ホットキーの追加に使えます。

<code>icon</code>

* テクスチャを表示。
* 子要素を持つウィジェットとしても使えます。
* <code>mirror = horizontal</code>や<code>mirror = vertical</code>で反転可能。

<code>textbox</code>

* テキストを表示します。
* 固定サイズまたは自動サイズ変更が可能です。
* 長すぎるテキストを切り詰めるには、<code>elide = right</code> または <code>elide = left</code> を使用します。
* <code>multiline = yes</code> を使えば、1行にも複数行にもできます。
* ゲームのファイルでは主に gui/shared/text.gui で定義されたタイプ(例:<code>text_single</code>)を使います。見た目の一貫性を保ち、毎回のコード量を減らすために活用してください。

==== hbox/vbox ====

hbox と vbox は、子要素を並べたりサイズ変更したり(あるいは均等に広げたり)できるリサイズ可能なコンテナです。hbox は子要素を横方向、vbox は縦方向に並べる点だけが異なり、それ以外の動作は同じです。そのため、以下の例は両方に当てはまります。

これらは自分自身と子要素を自動的に中央揃えにします。parentanchor は使用しないでください。レイアウトが崩れます。代わりに <code>expand={}</code> を使って片側へ押しやります。例は以下を参照してください。

ほとんどのウィンドウは、内容を縦に配置するために vbox を使用します。経験則として、vbox には拡張ポリシーを設定し、その後に <code>expand={}</code> を使いましょう。これでレイアウトの問題の 90% は解決します。

'''重要:''' 大きなサイズのオブジェクトはボックスを引き伸ばし、レイアウトを崩すことがあります。これは特にドイツ語やロシア語の長いテキストで、バニラ環境でよく起こります。

テキストに max_width を設定し、とても長い文字列でテストしてください。大量のダミーテキストを挿入するには、LOREM_IPSUM_TITLE と LOREM_IPSUM_DESCRIPTION のローカライズキーを使います。

'''詳細な挙動:'''

以下のスクリーンショットでは、hbox の背景は黒です。すべての例は [https://steamcommunity.com/sharedfiles/filedetails/?id=2579010074 UI Library mod] で利用できます。

{| class="wikitable"
|デフォルトでは、hbox は flowcontainer に似た動作をし、子要素を横方向に並べて、それらに合わせて自身のサイズを調整します。
<pre>
hbox = {
button_round = {}
button_round = {}
}
</pre>
|[[File:simple_hbox_wide.jpg]]
|-
|<code>layoutpolicy_horizontal = expanding</code> を指定すると、親の幅まで広がり、子要素を左右に広げます
<pre>
hbox = {
layoutpolicy_horizontal = expanding
button_round = {}
button_round = {}
}
</pre>
|[[File:Expanded_hbox.jpg]]
|-
|子要素をグループ化するには、<code>expand={}</code> を使って片側へ押しやります。<code>expand</code> はレイアウトポリシーが "growing" に設定されたテンプレートウィジェットです(gui/shared/windows.gui に定義)
<pre>
hbox = {
layoutpolicy_horizontal = expanding
button_round = {}
button_round = {}
expand = {}
}
</pre>
|[[File:Ordered_hbox.jpg]]
|-
|子要素に "expanding" ポリシーを設定すると、子要素のサイズも変更されます

これはタブを作成する際に便利で、サイズを手動で設定する必要がなくなります
<pre>
hbox = {
layoutpolicy_horizontal = expanding
button_standard = { layoutpolicy_horizontal = expanding }
button_standard = { layoutpolicy_horizontal = expanding }
}
</pre>
|[[File:tabs hbox 2.jpg]]
|-
|水平・垂直の両方に "expanding" ポリシーを設定すると、hbox とその子要素は両方向にリサイズされます
<pre>
hbox = {
layoutpolicy_horizontal = expanding layoutpolicy_vertical = expanding
button_standard = {
layoutpolicy_horizontal = expanding layoutpolicy_vertical = expanding
}
button_standard = {
layoutpolicy_horizontal = expanding layoutpolicy_vertical = expanding
}
}
</pre>
|[[File:big hbox.png]]
|}
固定サイズの親に配置した場合、デフォルトでは親のサイズいっぱいまで水平方向・垂直方向の両方に広がります。しかし、他の vbox/hbox の中に配置した場合は、親のサイズいっぱいには広がりません。

==== レイアウトポリシー ====

レイアウトポリシーは、hbox/vbox が子要素をどのようにリサイズするかを制御します。ボックスの中にさらにボックスがある場合にも適用されます。

種類は layoutpolicy_horizontal と layoutpolicy_vertical の2つで、それぞれ横方向・縦方向の挙動を制御します。

ポリシーは5種類あり、デフォルトは fixed です。

#'''fixed''' - 元のサイズを維持します。それ以上大きくも小さくもなりません。"fixed" の hbox/vbox はコンテナのように、子要素に合わせてサイズ変更します。
#'''expanding''' - 親の幅/高さまで広がり、元のサイズより小さくはなりません。他のポリシーの子要素より優先されます。複数の子要素が "expanding" の場合、利用可能なスペースを等分します。
#'''growing''' - 動作は "expanding" と同じですが、優先度が低いです。"expanding" の子要素がある場合、"growing" は拡大しません。つまり、expand = {} ウィジェットは 0 に縮むため、"expanding" と併用したい場合はポリシーを変更してください。
#'''preferred''' - 利用可能なスペースに応じて拡大・縮小します。
#'''shrinking''' - 元のサイズより小さくできますが、それ以上大きくはできません。

レイアウトポリシーは minimumsize、maximumsize、min_width、max_width も尊重します。

{|class="wikitable"
|"expanding" は "growing" より優先されますが、元の(fixed の)サイズより小さくはしません
<pre>
hbox = {
max_width = 400
layoutpolicy_horizontal = expanding

button_standard_small = { layoutpolicy_horizontal = expanding }
button_standard_small = { layoutpolicy_horizontal = growing }
}
</pre>
|[[File:growing hbox 2.png]]
|-
|"preferred" と "shrinking" は、スペースが少ないときにどちらも縮小できます。この効果を見るには、hbox に max_width を設定するか、"shrinking" ポリシーを使う必要があるかもしれません。
<pre>
hbox = {
layoutpolicy_horizontal = shrinking

button_standard_small = { layoutpolicy_horizontal = growing }
button_standard_small = { layoutpolicy_horizontal = preferred }
button_standard_small = { layoutpolicy_horizontal = shrinking }
}
</pre>
|[[File:shrinking hbox 2.png]]
|}

==== アニメーションステート ====
アニメーションは <code>state</code> を使って次のように作成できます: <syntaxhighlight>
state = {
name = _show
alpha = 0.5
duration = 0.5
}
</syntaxhighlight>このステートは、オブジェクトが表示されるときに自動的にトリガーされ、0.5秒かけてアルファ値を50%にします。

ステートでは以下が可能です:

* サイズ、位置、アルファなどのプロパティを変更
* <code>start_sound = { soundeffect = "event:..." }</code> でサウンドを再生
* <code>onclick</code> の代わりに <code>on_start</code> や <code>on_finish</code> を使って、ボタンに似た関数を実行

<code>on_start</code> はアニメーションの開始時にトリガーされます。

<code>on_finish</code> は、duration が設定されている場合は終了時にトリガーされます。設定されていない場合は <code>on_start</code> と同様に即時です。

'''''既知の問題:''''' <code>on_start</code> は現在バグがあり、2回トリガーされます。可能な限り <code>on_finish</code> を使用してください。

'''ステート名'''

自動的にトリガーされるあらかじめ定義された名前がいくつかあります:

* _show - オブジェクトが表示状態になったときにトリガー。これはウィジェット自身にのみ発火し、その子要素には発火しない点に注意
* _hide - オブジェクトが非表示になったとき
* _mouse_hierarchy_enter - このウィンドウまたはボタン上にカーソルが置かれたとき
* _mouse_hierarchy_leave - カーソルがこのウィンドウまたはボタンから離れたとき
* _mouse_press - このボタンが押されたとき
* _mouse_click - このボタンが押されて離されたとき
* _mouse_release - ボタンが離されたとき
* daily_tick - 毎日トリガー
* monthly_tick - 毎月トリガー

一部のウィンドウには専用のステート名があります。例えば、戦闘ウィンドウの <code>phase_change</code> や <code>new_battle_event</code> などです。

'''ステートのトリガー'''

ステートは任意の名前を与えて手動でトリガーできます。

<code>TriggerAnimation</code> はスコープされたオブジェクト内の、指定名のステートを1つトリガーします。

<code>PdxGuiTriggerAllAnimations</code> は、画面上で可視状態にある同名のすべてのステートをトリガーします。

例:

<code>onclick = "[PdxGuiWidget.TriggerAnimation('my_state')]"</code> - ボタン自身に設定されたステートをトリガーします。

<code>onclick = "[PdxGuiWidget.AccessParent.FindChild('widget_with_state').TriggerAnimation('my_state')]"</code> - ボタンの親に移動し、'widget_with_state' という子を見つけ、そのステートをトリガーします。階層を上に戻るには <code>AccessParent.AccessParent</code> を連結する必要がある場合があります。ただし <code>FindChild</code> は複数階層を横断して検索できます。

<code>onclick = "[PdxGuiTriggerAllAnimations('my_state')]"</code> - 同名のステートをすべて(他の可視ウィンドウ内にあるものも)トリガーします。

'''''既知の問題:''''' <code>TriggerAnimation</code> や <code>PdxGuiTriggerAllAnimations</code> が使用された時点でオブジェクトが不可視の場合、そのステートは後で可視になったときにトリガーされます。

'''自動トリガー'''

条件が満たされたときに自動でトリガーさせることもできます。

<code>trigger_on_create = yes</code> - ウィンドウが最初に作成されたときにトリガーされます。可視状態の変更ではトリガーされませんが、一般的には親ウィジェットが可視になったときにトリガーされます。

<code>trigger_when</code> - 条件が満たされたときにトリガーされます。

なお、<code>trigger_when</code> は手動トリガーを防ぎません。イベントのトリガーのようには機能せず、独自の on_action に近いものです。
[[File:Bezier curve.png|alt=a curve that starts with a small dip and grows to a large hump|thumb|Animation_Curve_Default のベジエ曲線]]
'''ベジエ'''

duration を持つステートでは、ベジエ曲線を使って変化速度を制御し、イージングを加えることができます。

例えば、ゲーム内の多くのウィンドウで使われるテンプレート Animation_Curve_Default では次のベジエ曲線が使われています:

<code>bezier = { 0.25 0.1 0.25 1 }</code>

数値は制御点の座標2組です。画像を参照してください。

この曲線は、ウィンドウがわずかな遅延ののちに素早く表示され、最後に 100% へと滑らかに到達することを意味します。

効果は強くはありませんが、より自然な感覚を生み出す助けになります。

異なるベジエ曲線を試すにはこのサイトを使ってください: https://cubic-bezier.com/#.25,.1,.25,1

簡単に言えば、両極端は次のとおりです:

<code>{ 0 1 1 0 }</code> は素早く表示

<code>{ 1 0 0 1 }</code> はゆっくり表示

=== テンプレート ===

テンプレートとタイプは、ボタンやウィンドウ背景、テキストスタイルなど、コード全体で何度も使える名前付きのコードブロックです。スタイルを統一し、記述量を減らすのに役立ちます。

テンプレートはウィンドウ全体の内容を格納することも、1行だけの内容であることもあります。例えば次のようなものです:

<pre>
template Window_Size_Sidebar
{
size = { 610 100% }
}
</pre>

このテンプレートは "using = Window_Size_Sidebar" で使用できます。実質的に "using" 行がテンプレートの内容に置き換わります。

テンプレートはグローバルで、任意の .gui ファイルに定義できます。ゲームのテンプレートの多くは gui/shared に保存されています。ローカル版の local_template は、同じファイル内で定義する必要があります。

よく使われるテンプレートやタイプは UI Library に掲載されています。アクセスするにはコンソールを開き、Release Mode をオンにすると "UI Library" という新しいボタンが表示されます。

==== タイプ ====

テンプレートは1つのプロパティのように小さくできる一方で、タイプは常にボタンやウィジェットのような一つの要素全体です。

text_single と text_multi は、すでに多くのプロパティが定義されたテキストボックスのタイプで、毎回書き直す代わりに次のように簡潔に書けます:

<pre>
text_single = {
text = "my text"
}
</pre>

タイプは少し異なる方法で定義します。まずタイプのグループに名前を付けて作成します:

<pre>
types Standard_Types
{
type text_single = textbox {
...
}
}
</pre>グループ名は何でもよく、動作に影響はありません。

==== Blockoverride ====

テンプレートとタイプには、名前付きの override ブロックを持たせることができ、インスタンス全体を変更せずに一部だけを編集できます。例えば、テンプレートにデフォルトのテキストブロックがあるとします:

<pre>
block "text"
{
text = "default_text"
}
</pre>

これを置き換えるには、同じ名前の blockoverride をインスタンスに追加します:

<pre>
blockoverride "text"
{
text = "actual text"
}
</pre>

インスタンスから削除することもできます: <code>blockoverride "text" {}</code>

==== タイプの置換 ====
個々のテンプレートやタイプを、バニラのファイル全体を上書きせずに置き換えることができます。

そのためには、アルファベット順で最初に来る新しいファイルを作成します。例えば 00_ で始めます。例: 00_my_types.gui。

タイプを置き換える場合は、まず任意の名前でグループを定義するのを忘れないでください。その中でタイプを定義します。

テンプレートなら、テンプレート定義を書くだけで、他に何も追加する必要はありません。

例。button_standard_small のデフォルトサイズを変えたいとします。新しいファイル 00_small_button.gui を作成して、次のように記述します: <syntaxhighlight lang="c">
types SmallButton {
type button_standard_small = button_standard
{
size = { 40 25 }
}
}
</syntaxhighlight>

==== Mod 互換性の追加 ====

2つの Mod が同じ GUI ファイルを上書きし、同時に読み込むと競合するのは一般的です。この場合、読み込み順で後の Mod によるカスタマイズのみがゲーム内に反映されます。

Mod 製作者は GUI タイプやテンプレートを使い、互いの Mod 内に「フック」を作ることで互換性を確保できます。

そのために、どちらか(または両方)の製作者が、相手の Mod のタイプ/テンプレートの使用箇所を、自分の Mod で編集した <code>.gui</code> ファイル内に含めます。

例えば、Mod 製作者が <code>some_other_mods_type = {}</code> や <code>using = some_other_mods_template</code> を <code>window_character.gui</code> などの修正済みバニラファイルに追加します。

この Mod が、そのタイプやテンプレートを定義している Mod と一緒に読み込まれた場合、それがウィンドウに表示されます。そうでない場合、その行は見た目に影響を与えず、起動時に一度だけ <code>error.log</code> に単一のエラーが記録されます。

したがって、バニラの <code>.gui</code> ファイルに加えた独自の追加要素は、たとえ自身の Mod で1回しか使わないとしても、別のカスタム <code>.gui</code> ファイル内のタイプ/テンプレートに抽出しておくのが望ましい実践です。そうすることで、他の Mod 製作者はあなたのタイプ/テンプレートを自分のコードに挿入するだけで、あなたと連携せずとも互換性を実現できる可能性があります。

==== Datamodels ====
Datamodel はリストを表示するために使われます。例:

<code>datamodel = "[CharacterWindow.GetChildren]"</code>

Datamodel は vbox、hbox、flowcontainer、dynamicgridbox、fixedgridbox、overlappingitembox で表示できます。

ウィジェットは次のようにリストから単一の項目を表示することもできます: <syntaxhighlight lang="c">
datacontext_from_model = {
datamodel = "[EventWindowData.GetOptions]"
index = "1"
}
</syntaxhighlight>


多くのバニラのリストはハードコードされており、項目の追加・削除・ソート変更はできません。

ただし、リスト内の一部の項目は <code>visible = "[]"</code> パラメータで非表示にできます。次の2点に注意してください:

* fixedgridbox はセルを動的にリサイズしないため、空白が残ります。
* 複数行や複数列の dynamicgridbox でも空白が生じます。

カスタムソートを作るには、スクリプトで同じリストを変数リストとして用意し、ordered_in_list を使ってソートする必要があります。

参考として Advanced Character Search mod を参照してください。

変数リストを表示するには、<code>[GetPlayer.MakeScope.GetList('list_name')]</code> またはグローバル変数リストには <code>[GetGlobalList]</code> を使います。

プレイヤーに保存されていない場合は、<code>CharacterWindow.GetCharacter.MakeScope...</code> のように適切なオブジェクトにスコープしてください。

詳細は下記を参照。

Datamodel と Datacontext を混同しないでください!

Datacontext は通常、<code>[CharacterWindow.GetCharacter]</code> のように単一のプロモート、つまりゲームオブジェクトを参照します。

珍しい例外の一つがキャラクターウィンドウの特性リストです:

<code>datacontext = "[CharacterWindow.GetTraitArrays]" datamodel = "[TraitArrays.GetPersonalityTraits]"</code>

== スクリプト化 GUI ==

スクリプト化 GUI は本質的には、UI からトリガーされる隠しイベントです。

それらは game/common/scripted_guis の .txt ファイルとして保存されます。

最も単純なスクリプト化 GUI は次のようになります: <syntaxhighlight lang="abap">
my_sgui = {
effect = {
add_gold = 100
}
}
</syntaxhighlight>UI では次のように、プレイヤーをスコープして使用できます:
onclick = "[GetScriptedGui('my_sgui').Execute( GuiScope.SetRoot( GetPlayer.MakeScope ).End )]"
<code>every_player = { add_gold = 100 }</code> のようにグローバルな効果を使う sgui なら、スコープなしで使えます:
onclick = "[GetScriptedGui('my_sgui').Execute( GuiScope.End )]"
他のオプションパラメータは以下のとおりです:

<pre>
gui_name = {
scope = character # ルートスコープ、つまり効果の対象
saved_scopes = {} # 追加の対象

confirm_title = "your_title" # これを追加すると、実行前に確認ダイアログが表示される
confirm_text = "your_text" # 確認ダイアログの追加テキスト

effect = { # 実行内容
custom_tooltip = "" # ツールチップを追加
}

is_shown = {} # UI 上で表示されるか?

is_valid = {} # プレイヤーが使用可能か? ボタンの enabled を使わなくても常にチェックされる

ai_is_valid = {} # AI が使用可能か? デフォルトでは無効
}
</pre>

すべてのブロックが必要なわけではありません。scripted gui は is_shown や is_valid だけを含むこともあります。

常に sgui を参照する代わりに、datacontext で一度宣言しておけば、そのオブジェクトやその子で使われる他の関数は、その sgui を使用します:

<pre>
datacontext = "[GetScriptedGui('gui_name')]"

onclick = "[ScriptedGui.Execute( GuiScope.SetRoot( GetPlayer.MakeScope ).End)]"

visible = "[ScriptedGui.IsShown( GuiScope.SetRoot( GetPlayer.MakeScope ).End)]"

enabled = "[ScriptedGui.IsValid( GuiScope.SetRoot( GetPlayer.MakeScope ).End)]"

tooltip = "[ScriptedGui.BuildTooltip( GuiScope.SetRoot( GetPlayer.MakeScope ).End)]"
</pre>

<code>GetPlayer.MakeScope</code> の代わりに、キャラクターウィンドウ内のキャラクター <code>CharacterWindow.GetCharacter.MakeScope</code> のように別のゲームオブジェクトにスコープすることもできます。あるいは sgui のルートが州(province)なら、<code>HoldingView.GetProvince.MakeScope</code> です。

<code>ScriptedGui.Execute</code> は <code>effect</code> ブロックに記述されたすべてを実行します。ボタンの <code>onclick</code> やアニメーションステートの <code>on_start</code>/<code>on_finish</code> と一緒に使います。

<code>IsShown</code> と <code>IsValid</code> は、それぞれ is_shown と is_valid ブロックの条件をチェックします。どちらのブロックも true/false を返すだけで、AddTextIf や Select_CString など他の関数でも使えます。

<code>BuildTooltip</code> はテキストボックスで使用して、ツールチップをテキストとして表示することもできます。

スクリプト化 GUI に別スコープを追加するには、次のように AddScope を使用します:

"[ScriptedGui.Execute( GuiScope.SetRoot( GetPlayer.MakeScope ).'''AddScope( 'target', CharacterWindow.GetCharacter.MakeScope )'''.End )]"
これで sgui 内では <code>scope:target</code> として参照できます。名前は任意です。

このようにして複数のスコープを保存できます: <code>AddScope().AddScope().AddScope().End</code>

追加したスコープを saved_scopes に宣言する必要はありません。宣言すると、ゲームは常にそれらをチェックし、GUI ファイルで追加されていなければ sgui は動作しません。

<code>MakeScopeValue</code>、<code>MakeScopeFlag</code>、<code>MakeScopeBool</code> を使えば、UI から sgui に値・テキスト・ブール値を渡すこともできます。

例:
onclick = "[GetScriptedGui('give_gold').Execute( GuiScope.SetRoot( GetPlayer.MakeScope ).'''AddScope( 'balance', MakeScopeValue(GetPlayerBalance) )'''.End )]"
数値は他の保存スコープと同様に参照されます: <code>add_gold = scope:balance</code>

MakeScopeValue は固定小数点値を想定しているため、事前に IntToFixedPoint で値を変換する必要があるかもしれません。

数値を直接渡すこともできます: <code>MakeScopeValue('(CFixedPoint)25')</code>

重要: ドットや開き括弧の前にスペースを入れないでください!<code>Execute(</code> は正しく、<code>Execute (</code> は間違いです。他のスペースは省略できますが、可読性のために残すと良いでしょう。

=== 変数やスクリプト値の表示 ===

変数やスクリプト値は、次のようにUIに表示できます。

変数:

<code>text = "[GetPlayer.MakeScope.Var('var_name').GetValue|1]"</code>

スクリプト値(common/script_values で定義):

<code>text = "[GetPlayer.MakeScope.ScriptValue('sval_name')|0]"</code>

AddScope で別のスコープを追加できる代替方法:

<code>text = "[GuiScope.SetRoot( GetPlayer.MakeScope ).AddScope( 'target', CharacterWindow.GetCharacter.MakeScope ).ScriptValue('sval_name')|0]"</code>

これらの例では、プレイヤーキャラクターが変数を持ち、svalue のルートとして機能します。MakeScope を使える限り、他のプロモートも使用可能です。サポートされているかはデータ型のログを検索してください(例: Character.MakeScope)。

末尾の |1 は任意で、小数点以下1桁に切り捨てます。つまり 1.573 は 1.5 と表示されます。これは任意の桁数に設定でき、% を付けるとパーセンテージに変換、= または + を付けると値が正負の場合に色付けできます。

|O は整数と併用して序数表示ができます。<code>['(int32)2'|O]"</code> は <code>2nd</code> と表示されます。注意: 数字のゼロではなくアルファベットの大文字 O です。

スクリプト値を整数に変換するには FixedPointToInt を使います。

UI に表示されるスクリプト値は毎フレーム計算されるため、アイテムのリストが大きかったり、svalue の計算が重い場合はパフォーマンスに影響することがあります。その場合は変数を使う方が良いでしょう。

イベントで値を表示するためにローカライズを使う場合は、次のようにします:

<code>event_var: "[ROOT.GetCharacter.MakeScope.Var('test_var').GetValue|0]"</code>

<code>event_value: "[SCOPE.ScriptValue('test_value')|0]"</code>

保存済みスコープ(ここでは "target")がある場合は、こうなります:

<code>event_var: "[target.MakeScope.Var('test_var').GetValue|0]"</code>

<code>event_value: "[GuiScope.SetRoot( target.MakeScope ).ScriptValue('test_value')|0]"</code>

=== データリストの表示 ===

たとえば社会集団を作るために、キャラクターのカスタムリストを作成できます。

これを行うには、まず変数リストに追加します。イベントやスクリプト化GUIを通じて、次のように行えます:

<pre>
effect = {
every_living_character = {
limit = {
has_trait = paranoid
}

root = {
add_to_variable_list = {
name = secret_society
target = prev
}
}
}
}
</pre>

このエフェクトをプレイヤーに発火すると、彼らが <code>root</code> となり、リストはそこに保存されます。

次に、任意のリストボックス(vbox、dynamicgridbox、fixedgridbox)を使い、datamodel をリストに設定します。

<pre>
dynamicgridbox = {
datamodel = "[GetPlayer.MakeScope.GetList('secret_society')]"

item = {
flowcontainer = {
datacontext = "[Scope.GetCharacter]"

portrait_head_small = {}

text_single = {
text = "[Character.GetNameNoTooltip]"
}
}
}
}
</pre>アイテムにアクセスするには、<code>Scope</code> と、対象オブジェクトに対応する関数(<code>GetCharacter</code> や <code>Title</code> など)を使います。利用可能な <code>Scope</code> 関数は Data Types を確認してください。datacontext で一度宣言すれば、その後は <code>Character</code> を使い続けられます。

注意: datacontext は <code>item = {}</code> ブロックの中ではなく、そこで使うウィジェット(ボタン、テキスト、ここではフローコンテナ)の中に置いてください。

== 新しいウィンドウとトグル ==

新しいウィンドウは別のウィンドウ内に追加することも、別ファイルで作成してスクリプト化ウィジェットとして生成することもできます。

ウィンドウの表示/非表示は、変数と Scripted GUI、または UI Variable System だけで制御できます。

ただし、新しいウィンドウを追加するタイプのMODは互換性が難しくなります。既存ファイルのどこかにウィンドウを開くボタンを追加する必要があるためです。

代わりに、決断・イベント・キャラクターインタラクションでウィンドウを開くこともできますが、HUD上のボタンと比べると利便性は下がります。

=== スクリプト化ウィジェットとしてウィンドウを作成 ===
任意の名前で新しい gui ファイル(例: <code>gui/new_window.gui</code>)を作成します。

その中に次のようにウィンドウを作成し、任意のカスタム名を付けます。<syntaxhighlight>
window = {
name = "my_window"
layer = top
using = Window_Size_MainTab
using = Window_Background_Sidebar
}
</syntaxhighlight>gui/ フォルダに "scripted_widgets" というフォルダを新規作成します。

その中に任意の名前の txt ファイル(例: <code>gui\scripted_widgets\scripted_windows.txt</code>)を作ります。

そこに、先ほどの gui ファイルとウィンドウ名を追加します:

<code>gui/new_window.gui = my_window</code>

ゲームがマップビューまでロードされると、自動的にウィンドウが作成されます。

複数のウィンドウ(その他のウィジェットやボタンも可)を作成でき、各行に1つずつ追加します。例:<syntaxhighlight>
gui/new_window.gui = my_second_window
gui/my_shortcuts.gui = my_button
etc...
</syntaxhighlight>

=== ウィンドウの表示切替 ===
ウィンドウは [[Interface#System Variables|UI 変数システム]]、変数、[[Interface#Scripted GUIs|Scripted GUI]] で切り替えられます。

'''UI システム'''

ウィンドウを隠す:

<code>visible = "[GetVariableSystem.Exists('show_my_window')]"</code>

ボタンの例:

<code>onclick = "[GetVariableSystem.Toggle('show_my_window')]"</code>

UI システムは最も手早く設定できますが、ゲームを終了すると全てのトグルがリセットされます(変数と異なり)。

'''変数と SGUI'''

ウィンドウを隠す:

<code>visible = "[GetPlayer.MakeScope.Var('show_my_window').IsSet]"</code>

ボタンの例:

<code>onclick = "[GetScriptedGui('toggle_my_window').Execute( GuiScope.SetRoot( GetPlayer.MakeScope ).End )]"</code>

そして <code>common/scripted_guis</code> に次の Scripted GUI を用意します。<syntaxhighlight>

toggle_my_window = {
effect = {
if = {
limit = {
has_variable = show_my_window
}
remove_variable = show_my_window
}
else = {
set_variable = show_my_window
}
}
}
</syntaxhighlight>別々の SGUI に分けて、変数を設定するものと削除するものを作っても構いません。

変数はイベントや決断から設定することもできます。

'''SGUI'''

より複雑なトリガーには Scripted GUI も使えます。

この場合、ウィンドウを隠すには:

<code>visible = "[GetScriptedGui('show_my_window').IsShown( GuiScope.SetRoot( GetPlayer.MakeScope ).End )]"</code>

そして次のような Scripted GUI を用意します。<syntaxhighlight>
show_my_window = {
is_shown = {
# various triggers
is_adult = yes
is_female = yes
is_landed = yes
}
}
</syntaxhighlight>ここでは変数と Scripted GUI がプレイヤーをスコープしています。代わりに別のオブジェクトやグローバル変数/SGUI を使うこともできます。例えば:

* <code>visible = "[GetGlobalVariable('show_my_window').IsSet]"</code>
* <code>visible = "[GetScriptedGui('show_my_window').IsShown(GuiScope.End)]"</code>

このウィンドウはプレイヤーがキャラクターを選択する前に作成される点に注意してください。そのため、プレイヤーへスコープする際は、まずプレイヤーが存在するかをチェックする必要があるかもしれません。

例えば、100% サイズの不可視ウィジェットを設定し、<code>visible = "[GetPlayer.IsValid]"</code> と <code>alwaystransparent = yes</code> を付けて、クリックを透過させるようにできます。

その中に、変数や SGUI のチェックを行う実際のウィンドウを置きます。

他にもウィンドウの作成や切り替え方法はいくつかありますが、やや面倒です。
*[[#Toggles_with_PdxGuiWidget|PdxGuiWidget]] - 単純だが、トグルが複数だと複雑になる
*[[#Toggles_with_animation|アニメーション]] - 記述は長くなるが、複数の処理をまとめてトリガーしやすい
*コンソールコマンド <code>GUI.CreateWidget</code> - コンソールはマルチプレイでは使えず、実績も無効化されます。
以下は以前の例です。

=== PdxGuiWidget によるトグル ===

PdxGuiWidget は、名前付き要素の表示/非表示に使うシンプルな関数です。

この例では、隠れたサブメニューを持つコンテナと、それを表示するボタン、隠すボタンがあります。
# 1つ目のボタンはクリックされると親に戻り、サブメニューともう一方のボタンを探して表示し、自分自身は隠れます
# 2つ目のボタンは要素とボタンを探してそれらを表示し、自分自身は隠れます

<pre>
container = {
button = {
name = "show submenu"

onclick = "[PdxGuiWidget.AccessParent.FindChild('submenu').Show]"
onclick = "[PdxGuiWidget.AccessParent.FindChild('hide submenu').Show]"
onclick = "[PdxGuiWidget.Hide]"
}

button = {
name = "hide submenu"
visible = no

onclick = "[PdxGuiWidget.AccessParent.FindChild('submenu').Hide]"
onclick = "[PdxGuiWidget.AccessParent.FindChild('show submenu').Show]"
onclick = "[PdxGuiWidget.Hide]"
}

widget = {
name = "submenu"
visible = no
}
}
</pre>

要素同士がさらに親子で離れている場合は、次のように AccessParent を繰り返せます:
onclick = "[PdxGuiWidget.AccessParent.AccessParent.AccessParent.AccessParent.FindChild('submnenu').Show]"

各ボタンは、タイプにかかわらず複数の要素を表示・非表示にできます。必要なのは名前だけです。

長所:
* 単純なトグルなら設定が簡単

短所:
* ゲームを再起動するたびにトグルはリセットされる
* 複数の要素やトグルを扱うとコードが膨らみ管理が難しくなる
* データリスト(dynamicgridbox など)の項目を隠そうとすると、最初のインスタンスしか隠れない

=== アニメーションによるトグル ===

ボタン(や条件)でトリガーされるアニメーションを設定して、要素の表示/非表示や移動まで行えます。こうすることで、ボタンとウィンドウの間にどれだけ親があるかを数える必要がなくなり、1つの onclick で多くの処理を同時に起動できます。

前の例は次のようになり、動作は同じです。1つ目のボタンは "show_submenu" をトリガーし、自身を隠して残りを表示、2つ目のボタンは "hide_submenu" をトリガーし、自分とウィジェットを隠し、1つ目のボタンを表示します。

<pre>
container = {
button = {

state = { # これはアニメーションです
name = show_submenu
on_start = "[PdxGuiWidget.Hide]"
}
state = {
name = hide_submenu
on_start = "[PdxGuiWidget.Show]"
}

onclick = "[PdxGuiTriggerAllAnimations('show_submenu')]"
}

button = {
visible = no

state = {
name = show_submenu
on_start = "[PdxGuiWidget.Show]"
}
state = {
name = hide_submenu
on_start = "[PdxGuiWidget.Hide]"
}

onclick = "[PdxGuiTriggerAllAnimations('hide_submenu')]"
}

widget = {
visible = no

state = {
name = show_submenu
on_start = "[PdxGuiWidget.Show]"
}
state = {
name = hide_submenu
on_start = "[PdxGuiWidget.Hide]"
}
}
}
</pre>

やや長くなりますが、アニメーションはテンプレートとして保存し、<code>using = hide_animation</code> の1行で再利用できます。Fullscreen Barbershop ではアニメーションが広く使われています。より良い例が必要ならそちらを参照してください。

長所:
* 多くの要素をまとめてリンクしやすく、別ウィンドウを開いてその中のアニメーションを起動することも可能

短所:
* アニメーションブロックはかなり長くなりがち
* ゲームを再起動すると全てのトグルがリセットされる

=== システム変数 ===

システム変数はゲーム内部で使用され、セーブに保持されず、スクリプトから直接アクセスすることはできません。

.gui ファイル内で直接作成できるため、セットアップは不要です。

システム変数の構文:

<code>onclick = "[GetVariableSystem.Toggle( 'var_name' )]"</code>

または:

<pre>datacontext = "[GetVariableSystem]"
onclick = "[VariableSystem.Toggle( 'var_name' )]"</pre>

利用可能な関数:
* Clear - <code>Clear( 'var_name' )</code> 変数をクリア
* ClearIf - <code>ClearIf( 'var_name', Condition )</code> 条件が真なら変数をクリア
* Exists - <code>Exists( 'var_name' )</code> ブール、変数が存在すれば true
* Get - <code>Get( 'var_name' )</code> CString、変数に保存された文字列を返す
* HasValue - <code>HasValue( 'var_name', 'string' )</code> ブール、与えた文字列を持っていれば true
* Set - <code>Set( 'var_name', 'string' )</code> 変数を指定の文字列に設定
* Toggle - <code>Toggle( 'var_name' )</code> 存在すればクリア、なければ作成
* SetOrToggle - <code>SetOrToggle( 'var_name', 'string' )</code> 同じ文字列ならクリア、そうでなければ指定の文字列に設定

==== システム変数でのトグル ====

このファイル内での基本的なトグルは次のようになります:

<pre>
container = {
button = {
onclick = "[GetVariableSystem.Toggle( 'gui_toggle' )]"
}

widget = {
visible = "[GetVariableSystem.Exists( 'gui_toggle' )]"
}
}
</pre>

クリックされると、システム変数は存在有無に応じてトグルされ、その結果がウィジェットの表示/非表示に使われます。

==== システム変数でのタブ ====

3つのタブの基本設定は次のとおりです:

<pre>
container = {
button = {
onclick = "[GetVariableSystem.Set( 'gui_tabs', 'tab_1' )]"
}
button = {
onclick = "[GetVariableSystem.Set( 'gui_tabs', 'tab_2' )]"
}
button = {
onclick = "[GetVariableSystem.Set( 'gui_tabs', 'tab_3' )]"
}

widget = {
visible = "[GetVariableSystem.HasValue( 'gui_toggle', 'tab_1' )]"
}
widget = {
visible = "[GetVariableSystem.HasValue( 'gui_toggle', 'tab_2' )]"
}
widget = {
visible = "[GetVariableSystem.HasValue( 'gui_toggle', 'tab_3' )]"
}
}
</pre>

変数は最初は値を持たないため、いずれのウィジェットも表示されません。

デフォルトのタブを設定するには、ウィンドウを開くボタンで変数を設定します:

<pre>
button = {
onclick = "[GetVariableSystem.Toggle( 'gui_toggle' )]" # これでウィンドウを開く
onclick = "[GetVariableSystem.Set( 'gui_tabs', 'tab_1' )]" # これでデフォルトタブを設定
}
</pre>

または、ウィンドウが表示されたときの state ブロックを使います:

<pre>
state = {
name = _show
on_start = "[GetVariableSystem.Set( 'gui_tabs', 'tab_1' )]"
}
</pre>

あるいは、変数が存在しないときに表示されるように1つのウィジェットを設定し、初期値の設定を避けることもできます:

<pre>
container = {
button = {
onclick = "[GetVariableSystem.Clear( 'gui_tabs' )]"
}
button = {
onclick = "[GetVariableSystem.Set( 'gui_tabs', 'tab_2' )]"
}
button = {
onclick = "[GetVariableSystem.Set( 'gui_tabs', 'tab_3' )]"
}

widget = {
visible = "[Not( GetVariableSystem.Exists( 'gui_toggle' ) )]"
}
widget = {
visible = "[GetVariableSystem.HasValue( 'gui_toggle', 'tab_2' )]"
}
widget = {
visible = "[GetVariableSystem.HasValue( 'gui_toggle', 'tab_3' )]"
}
}
</pre>

これは、最初の例を別の方法でデフォルト値を設定したものと同等です。

長所:
* シンプルで覚えやすい構文
* 複数の要素、別ウィンドウ間でもリンクしやすい
* 追加のコマンド(下記参照)でまったく新しいウィンドウの表示にも拡張でき、hud.gui にウィジェットを置く必要がなくなる

短所:
* スクリプトと直接やり取りできず、GUI から設定・クリアする必要がある
* 管理が難しくなることがある
* ゲームを再起動するとすべてのトグルがリセットされる

=== 新しいウィジェットの作成 ===

<code>ExecuteConsoleCommand( ... )</code> コマンドを使うと、まったく新しいウィンドウを作成できます。

まず、表示するための .gui ファイル内にウィンドウを作成します。例: "gui/custom_windows/my_window.gui"。メインウィンドウに名前を付けておきます。

<pre>
window = {
name = "my_custom_window"
parentanchor = center
layer = middle
size = { 100 100 }
using = Window_Background
}
</pre>

上記は画面中央に 100 x 100 のウィンドウを作成します。重要なのは名前で、これがウィンドウ作成時に使われます。

ウィンドウを作成するボタンは次のようになります:

<pre>
button = {
onclick = "[ExecuteConsoleCommand('gui.createwidget gui/custom_windows/my_window.gui my_custom_window')]"
}
</pre>

閉じるには:

<pre>
button = {
onclick = "[ExecuteConsoleCommand('gui.ClearWidgets my_custom_window')]"
}
</pre>
gui.ClearWidgets と my_custom_window の間はスペース 1 つだけにしてください。スペースが 1 つより多いと、gui.ClearWidgets は my_custom_window だけでなくコンソールで作成したすべてのウィジェットをクリアしてしまいます。

あるいはトグルとしてまとめることもできます:

<pre>
button = {
onclick = "[ExecuteConsoleCommand( Select_CString( GetVariableSystem.Exists('my_window_open'), 'gui.ClearWidgets my_custom_window', 'gui.createwidget gui/custom_windows/my_window.gui my_custom_window' ) )]"
onclick = "[GetVariableSystem.Toggle('my_window_open')]"
}
</pre>

このトグルは、システム変数を設定し、その値に基づいて実行するコマンドを選択することで動作します。

<code>Select_CString( ... )</code> は 3 つの引数を取ります:
* 条件
* 条件が true の場合の文字列
* 条件が false の場合の文字列

条件が true を返したら最初の文字列が使われ、そうでなければ 2 番目が使われます。
上記のトグルでは、システム変数が存在する場合はウィンドウを破棄し、存在しない場合は作成します。

==既存のゲームビュー一覧==
次の名前は、OpenGameView や IsGameViewOpen といったコマンドで使用できます。例: IsGameViewOpen('intrigue_window')
{| class="wikitable mw-collapsible mw-collapsed"
|+
! colspan="3" |Game Views
|-
|intrigue_window||dynasty_house_view||artifact_kill_list
|-
|military||dynasty_house_customization||action_item_handler
|-
|men_at_arms||dynasty_tree_view||transfer_vassal
|-
|knights||dynasty_legacy_window||title_election
|-
|levy||dynasty_customization||court_window
|-
|men_at_arms_type||dynasty_house_members||ruler_designer
|-
|select_maa_origin_province||factions_window||royal_court
|-
|army||succession_event||inventory
|-
|holding_view||lineage_view||reforge_artifact
|-
|character||religion||appoint_position
|-
|character_finder||faith||artifact_details
|-
|combat||faith_creation||language
|-
|end_of_combat||faith_conversion||memories
|-
|siege||culture_window||ruler_designer_save
|-
|raid||hybridize_culture||ruler_designer_load
|-
|rally_point||diverge_culture||activity_planner
|-
|place_rally_point||add_culture_tradition||activity_window
|-
|find_title||replace_culture_pillar||activity_list_window
|-
|character_focus||great_holy_war||activity_list_detail_host_window
|-
|lifestyle||hired_troop_detail_view||activity_list_detail_invite_window
|-
|decisions||lease_out_baronies||activity_locale
|-
|decision_detail||outliner||activity_guest_list
|-
|title_view_window||my_realm||activity_intent_selection
|-
|war_overview||succession_law_change||activity_log
|-
|struggle||war_declared_overview||travel_planner
|-
|struggle_involvement||war_results||travel_option_selection_window
|-
|pause_menu||designate_heir||travel_route_edit_window
|-
|load_game||change_ghw_target||accolade_view
|-
|save_game||barbershop||create_accolade_view
|-
|resign_confirmation||concubine_interaction||diarchy
|-
|in_front_topbar||title_history||manage_tax_slots
|-
|select_commander||title_add_law||tax_slot_appoint_tax_collector
|-
|tutorial||title_customization||tax_slot_obligations
|-
|council_window||kill_list||tax_slot_vassals
|-
| || ||tax_slot_assign_vassal
|}

== トラブルシューティング ==

=== 既知のクラッシュ理由 ===

* hbox と vbox に 100% の size を設定する
* 同じ親要素で複数の子に <code>resizeparent = yes</code> を設定する

そのうち 1 つだけが可視でも、やはりクラッシュします

* 自己参照する型。RAM を使い切るまで無限のロード画面になります

* 構文エラー(波括弧や引用符の不足など)

ファイル内で <code>{</code> を検索し、次に <code>}</code> を検索して数を比較してください。数は一致しているはずです。

型のセットアップを再確認してください。型の構文は他の要素と異なります。

引用符の欠落については、エディタで正規表現検索を有効にし、<code>= \[</code> と <code>\]\n</code> を検索してください。通常、角括弧のまわりに引用符がない状態にはなりません。

== 便利なリンク ==
0 から 1 の正規化値を持つカラーピッカー。tintcolor などに便利です。

https://rgbcolorpicker.com/0-1

UI プロトタイピングに適したツール:

https://www.figma.com/<nowiki/>{{Modding navbox}}
[[Category:Modding]]
98

回編集

案内メニュー