界面

本頁面講述的內容長期有效

CK3的用戶界面(UI)是高度可修改的,但由於這個原因,UI修改器會禁用成就,因為玩家可能會用它們作弊。

遊戲還包含一個GUI編輯器,可以在遊戲中檢查UI元素並編輯它們。

模組作者可以:

  • 改變界面的視覺風格
  • 使窗口可以移動和調整大小。
  • 更改和刪除要素
  • 增加新的按鈕
  • 從代碼中顯示更多信息
  • 增加新的窗口(有一個變通辦法)

模組作者不能:

  • 增加新的熱鍵(只能重複使用現有的熱鍵)。
  • 在另一個窗口中顯示一個窗口的信息,除非開發者包含這種可能性。


基礎[編輯 | 編輯原始碼]

CK3的界面是通過game/gui文件夾中的.gui文件創建的,它有點類似於html文件。

因此,你可以用任何文本編輯器來編輯它們,比如 VS Code, Sublime 或 Atom。語法高亮選擇Python或Perl 6,它們很適合。

CK3使用.dds文件來製作紋理,它保存在game/gfx/文件夾內。

要編輯或保存.dds文件,可以使用帶有Intel 插件的Photoshop或帶有此插件的GIMP。


要在遊戲中重新加載gui文件並使用GUI編輯器,添加-debug_mode和-develop啟動選項。

  • 在Steam上右擊遊戲,選擇屬性,設置啟動選項,添加-debug_mode -develop

你可以使用以下控制台指令:

  • reload gui - 重載所有的gui文件,以顯示遊戲中的任何變化。
  • reload texture - 重新加載所有紋理文件。
  • gui editor - 打開GUI編輯器。
  • tweak gui.debug - 允許啟用UI元素的高亮顯示。提示工具條將顯示其名稱、大小和位置。
  • DumpDataTypes - 將在你的日誌文件夾(Documents/Paradox Interactive/Crusader Kings III/logs)中創建一個data_types.log,列出每個窗口/遊戲對象的所有可用的GUI函數。

其他提示。

  • 始終打開錯誤日誌,查看代碼中是否有錯誤(在同一個日誌文件夾中)。
  • 點擊控制台下方的「Toggle Release Mode」啟用遊戲中的錯誤跟蹤器,查看你的更改是否導致任何新的錯誤。
  • 當使用reload gui命令時,將遊戲靜音,因為每次都會觸發前奏的聲音。
  • 使用Reload GUI mod,它增加了一個重新加載gui的按鈕(用熱鍵),並在你重新加載時關閉設置窗口。
  • 將gui文件夾添加到你的文本編輯器中,這樣它就會使用它來自動完成。
  • 你可以在gui/debug中使用test_gui.gui進行測試。要顯示這個窗口,打開控制台並點擊測試窗口。
  • 摺疊代碼,這樣更容易看到它的結構。通常的熱鍵是Ctrl+K、Ctrl+1(其中1是摺疊代碼的級別)。

創建GUI模組[編輯 | 編輯原始碼]

1. 啟動遊戲啟動器,進入Mods,Mod工具,填寫所有欄位,包括標籤。

點擊創建會在Documents/Paradox Interactive/Crusader Kings III/mod中新建一個文件夾和一個.mod文件。

2. 接下來,在你的模組裡創建一個「gui」文件夾,然後從game/gui里複製你要修改的文件到那裡。

  • 如果你不知道需要哪個文件,請使用GUI編輯器,並在遊戲中檢查它。

GUI編輯器[編輯 | 編輯原始碼]

GUI編輯器(GUI Editor)是一款用於編輯遊戲中UI的開發者工具。

要使用它,你需要在-debug_mode-developer選項下啟動遊戲。


要打開編輯器,可以選擇

  • 按Ctrl+F8鍵
  • 用`鍵(在Esc下面)打開控制台,點擊GUI編輯器。
  • 打開控制台,運行gui_editor命令。

特點[編輯 | 編輯原始碼]

默認情況下,編輯器啟動時啟用了編輯模式。你可以在頂部窗口中禁用它,稱為Outliner。熱鍵「E」。

  • 編輯模式類似於瀏覽器中的檢查模式。當它被啟用時,你不能與遊戲互動,但它允許你選擇UI的一部分,並在下面的屬性窗口中更改它們。
  • 滾動滑鼠滾輪來改變編輯器應該關注的元素,因為hud.gui往往會被放在其他窗口之上。
  • 黃色邊框表示選中的元素。要隱藏其他邊框,請取消勾選Outliner中的「Show Hierarchy」(熱鍵「L」)。
  • 按住滑鼠右鍵可以移動所選元素。
  • 要撤銷任何操作,請按Ctrl+Z或Outliner中的撤銷按鈕。重做是旁邊的按鈕,Ctrl+Y。
  • Outliner中的紅星*表示未保存的更改。按Ctrl+S或頂部的保存按鈕來保存它們。確保你正在編輯的gui文件在你的模組中,否則它會將更改寫入遊戲文件夾。(要重置它們,請從Steam的屬性窗口驗證完整性)
  • 你可以通過拖動任何開發窗口來移動它們,並通過拖動邊緣來調整它們的大小。
  • 您可以在Outliner的層次結構中拖動UI元素來重新排序。右鍵單擊以顯示上下文菜單。
  • 您可以在「屬性(Properties)」窗口中更改或添加新屬性(通過單擊加號)。


通過單擊Outliner中的「窗口(Window)」,您可以打開另外兩個窗口。UI組件(UI components)和註冊數據類型(Registered Data Types)。

  • UI組件就像一個調色板,你可以拖動新的元素到UI中。gui/shared/standard.gui和gui/defaults.gui包含最常見的東西,如按鈕、圖標和文本。
  • 註冊數據類型可以用來查找能夠顯示遊戲中數據的函數。
  • 點擊數據類型中的右上角按鈕會將這些數據轉儲到你的日誌文件夾(Documents/Paradox Interactive/CK3/logs)。你也可以使用「DumpDataTypes」控制台指令。


注意:通常很容易誤選一個模板,改變模板會影響UI中的所有實例。留意你在Outliner中選擇了哪些文件。如果你在屬性(Properties)窗口中看到一個以「type:」開頭的藍色標題,那就是一個模板,所以要注意不要編輯這部分內容(除非你打算編輯)。

UI代碼[編輯 | 編輯原始碼]

CK3的UI是由容器和容器內的對象組成的。

例如,大多數窗口都是使用window容器創建的,而地圖圖標則使用widget或hbox。

文件中的順序決定了屏幕上的順序:代碼中較低的東西會出現在較高的一層。

大多數元素也可以包含其他元素,例如,在一個按鈕的圖標裡面可以有一個文本框。嵌套的元素,也就是子元素,將和它們的父元素一起移動。

位置是相對於左上角設置的(屏幕或父元素)。這可以通過parentanchor屬性來改變。可用的選項有:左、右、上、下、hcenter(水平中心)和vcenter(垂直中心)。它們可以像這樣與|組合。parentanchor = right|vcenter

每個元素都用大括號打開和關閉,像這樣。container = { }

常見的代碼風格是在同一層次上打開和關閉塊,同時用一個標籤縮進內容。

widget = {
  size = { 50 50 }
  alpha = 0.5
}

這可以幫助你更好地看到代碼的結構,注意到任何缺失的或額外的括號,對於一些編輯器來說,這也是正確摺疊代碼塊所需要的。

提升和功能[編輯 | 編輯原始碼]

每個窗口都有一個預定義的命令集合-提升和功能(promotes and functions) -可供使用。這些命令可以在使用 DumpDataTypes 控制台命令後,在 Documents/Paradox Interactive/Crusader Kings III/logs/ 目錄下的 data_types.log 文件中找到。

它們用於顯示遊戲中的所有數據,例如您的姓名、金幣、子女,並且可以設置按鈕操作。

一個「promote」返回一個作用域(scope),即一個遊戲對象,如角色或省份,而一個 "function" 返回一個數字、一個字符串或一個布爾值(true/false)等等。

全局命令可以在任何地方使用,比如 GetPlayer (返回玩家角色)或 GetCurrentDate (獲取當前日期)。

其他命令只能在它們所屬的窗口/對象中使用,例如,GetParents 只能在角色窗口中使用,並且必須以 CharacterWindow.GetParents 開始。

命令可以像這樣連結起來:

CharacterWindow.GetCharacter.GetPrimaryTitle.GetHeir.GetPrimarySpouse.GetFather

對象可以繼承其父對象的範圍,這意味著我們不需要重新鍵入上面的行來顯示關於此角色的信息。相反,我們可以將小部件的數據上下文設置為此行,然後其中的每個文本框都將使用 "[Character.GetNameNoTooltip]" , "[Character.GetGold]" 等等。

同樣的規定也適用於gridboxes的條目


UI組件[編輯 | 編輯原始碼]

window

  • 唯一可移動的容器。要啟用移動,添加movable = yes屬性。
  • 可以固定大小,也可以由其子代調整大小。
  • 在遊戲中,背景是通過模板設置的,比如using = Window_Backgroundusing = Window_Decoration
  • 如果一個孩子在窗口之外,它將無法被點擊,也不會顯示工具提示。使用allow_outside = yes來改變這一點。沒有z坐標,所以z的排序方式是第一個寫的子代會被放在第二個寫的子代後面。為了使組件可以點擊,並且在其他組件的頂部,你需要把它放在其他組件的後面。

widget

  • 一個靜態容器。在其他方面類似於一個窗口。

margin_widget

  • 類似於widget,但可以用margins調整大小。(這使我們可以通過將高度設置為100%,將邊距設置為約50,使窗口可以調整到不同大小的屏幕上,以顯示hud)

container

  • 沒有固定的尺寸(但你可以設置最大尺寸)。
  • 自動調整大小以適應所有子代,包括不可見的子代。使用 ignoreinvisible = yes 來忽略它們。
  • 常用於將多個元素組合在一起移動。

flowcontainer

  • 將其所有子代排列成水平行。使用direction = vertical使其垂直。
  • 默認情況下不會忽略不可見的子代。使用ignoreinvisible = yes 來改變它。
  • 沒有固定的大小,可以使用ignoreinvisible = yes來改變。
  • 它的子代不能有位置,因為它們是自動設置的。
  • 如果你需要調整它的子代的位置,你可以把它放在一個容器或widget裡面,然後改變相對於這個父代的位置。

hbox vbox

  • 將所有的子代排列成水平的一行,並沿其寬度分布。Vbox也是一樣的,但是是垂直的。
  • 不能有固定的大小,而是取其父代的寬度作為自己的寬度(忽略父代的邊距)。Vbox取其高度。
    • 如果它的父體不能有固定的大小(如flowcontainer),這可能會導致遊戲崩潰。
  • 可受最小尺寸/最大尺寸和邊距的限制。
  • 默認情況下,忽略不可見的子代,使用ignoreinvisible = no來更改。
  • 在另一個hbox中的hbox將有0個大小,並且不會展開其子代。使用layoutpolicy_horizontal = expanding來調整它的大小(或者在vbox中使用layoutpolicy_vertical = expanding)。
  • 接受數據模型(從遊戲數據中創建列表)。

dynamicgridbox

  • 僅用於數據模型。
  • 將所有條目垂直排列。使用flipdirection = yes可以使其水平排列。
  • 默認情況下不忽略不可見項目。使用 ignoreinvisible = yes 來進行更改。
  • 根據內容的調整大小,並受到最小尺寸和最大尺寸的限制。
  • 條目可以有不同的尺寸。
  • 有時候在處理非常長的列表時可能會出現卡頓的情況。

fixedgridbox

  • 類似於動態框,但其所有項目都是固定尺寸的(本質上是一個表格)。
  • 僅用於數據模型。
  • 將所有項目垂直排列。使用 flipdirection = yes 可以使其水平排列。
  • 無法忽視不可見的條目。
  • 可以固定大小,根據內容進行調整,並受最小尺寸和最大尺寸的限制。
  • 處理長列表性能更好。

overlappingitembox

  • 僅用於數據模型。
  • 將所有項目水平排列,並在列表長度超過盒子大小時重疊顯示。使用 flipdirection = yes 使其水平排列。
  • 可以是固定尺寸或自動調整大小。

scrollarea

  • 一個帶有滾動條的小部件,如果內容超過其大小,滾動條將出現。
  • 滾動條可以通過 scrollbarpolicy_horizontal = always_offscrollbarpolicy_vertical = always_off 來禁用。
    • 可以使用沒有滾動條的滾動區域來裁剪列表或圖片。
  • 可以固定大小,根據內容進行調整,並受最小尺寸和最大尺寸的限制。

button

  • 一個可點擊的對象。 接受 onclickonrightclick.
    • 在添加右鍵功能時,請包括 button_ignore = none.
  • 無默認材質
  • 可以根據其子代進行固定大小或調整大小。
    • 一個0x0按鈕可以用來添加不可見的快捷鍵。

icon

  • 顯示一個材質
  • 可用作小部件來存儲子代。
  • 可以通過 mirror = horizontalmirror = vertical 進行翻轉.

textbox

  • 顯示文字內容。
  • 可以是固定尺寸或自動調整大小。
  • 使用 elide = rightelide = left 來截斷過長的文本。
  • 可以是單行或多行,當 multiline = yes 時為多行。
  • 遊戲文件通常使用在gui/shared/text.gui中設置的模板,比如 text_single 。使用它們可以保持視覺一致性,並減少每次編寫代碼的工作量。

hbox/vbox[編輯 | 編輯原始碼]

Hboxes和vboxes是可調整大小的容器,它們對其子代進行排序、調整大小或展開。hbox水平排序子代,vbox垂直排序子代,除此之外它們的工作方式相同,所以這裡的所有示例都適用於兩者。

以下是屏幕截圖,hboxes 帶有黑色背景。所有示例都可以在 UI Library mod 中找到。

默認情況下,hbox 的工作方式類似於流容器(flowcontainer):它以水平方向排列子項,並調整大小以適應它們。
hbox = {
    button_round = {}
    button_round = {}
}
Simple hbox wide.jpg
使用 layoutpolicy_horizontal = expanding 時,它會擴展到其父級的寬度,並展開其子元素。
hbox = {
    layoutpolicy_horizontal = expanding
    button_round = {}
    button_round = {}
}
Expanded hbox.jpg
要將其子項分組,我們可以使用 expand = {} ,這是一個模板部件,其布局策略設置為"增長(growing)"(在gui/shared/windows.gui中定義)。
hbox = {
    layoutpolicy_horizontal = expanding
    button_round = {}
    button_round = {}
    expand = {}
}
Ordered hbox.jpg
"擴大(expanding)"策略涉及到子代的規模調整。

這對於創建標籤頁非常有用,無需手動設置它們的大小。

hbox = {
    layoutpolicy_horizontal = expanding
    button_standard = { layoutpolicy_horizontal = expanding }
    button_standard = { layoutpolicy_horizontal = expanding }
}
Tabs hbox 2.jpg
隨著水平和垂直方向的「擴展(expanding)」策略,hbox 及其子代將在兩個方向上進行調整大小。
hbox = {
    layoutpolicy_horizontal = expanding   layoutpolicy_vertical = expanding
    button_standard = {
        layoutpolicy_horizontal = expanding   layoutpolicy_vertical = expanding
    }
    button_standard = {
        layoutpolicy_horizontal = expanding   layoutpolicy_vertical = expanding
    }
}
Big hbox.png

如果放置在固定大小的父級容器中,默認情況下,它會水平和垂直方向上擴展到整個父級容器的大小。但如果放置在其他垂直盒子或水平盒子中,它們將不會擴展到父級容器的大小。


布局策略[編輯 | 編輯原始碼]

布局策略控制著hboxes和vboxes中子元素的調整大小方式。這同樣適用於嵌套boxes中的boxes。

有兩種類型,layoutpolicy_horizontallayoutpolicy_vertical ,分別控制水平和垂直行為。

默認情況下有五項固定的策略:

  1. fixed(固定的) - 組件保持原始大小,無法放大或縮小。設置"fixed"屬性的hboxes/vboxes將根據子代的大小進行調整,就像一個容器。
  2. expanding(擴展的) - 根據父代的寬度/高度自動調整大小,但不會縮小到原始尺寸以下。優先級高於其他策略的子代。如果多個子代設置為 "expanding" ,它們將平均分配可用空間。
  3. growing (增長的)- 與"expanding"相似,但優先級較低。如果存在帶有"expanding"屬性的子代,"growing"將不會發生。這還意味著 expand = {} 部件將調整大小為0,因此如果想要與"expanding"策略的組件一起使用,需要更改其策略。
  4. preferred (偏好的)- 隨著可用空間的變化,它會隨之放大或縮小。
  5. shrinking (縮水的)- 可以縮小至原始大小以下,但無法放大超過原始大小。

布局策略還會遵循最小尺寸 (minimumsize) 、最大尺寸 (maximumsize) 、最小寬度 (min_width)和最大寬度 (max_width) 。

"Expanding" 優先於 "growing" ,但不會使其縮小到小於原始(固定)尺寸。
hbox = {
	max_width = 400
	layoutpolicy_horizontal = expanding

	button_standard_small = { layoutpolicy_horizontal = expanding }
	button_standard_small = { layoutpolicy_horizontal = growing }
}
Growing hbox 2.png
"preferred" 和 "shrinking" 在空間不足時都可以收縮。為了看到這種效果,可能需要通過 max_width 或 "shrinking" 策略來限制 hbox 的寬度。
hbox = {
	layoutpolicy_horizontal = shrinking

	button_standard_small = { layoutpolicy_horizontal = growing }
	button_standard_small = { layoutpolicy_horizontal = preferred }
	button_standard_small = { layoutpolicy_horizontal = shrinking }
}
Shrinking hbox 2.png

請注意,具有較大尺寸的物塊 (objects) 可能會拉伸 hbox/vbox,即使看起來適合。設置 max_width ,並測試所有文本欄位是否能夠容納很長的字符串,以確保窗口不會改變。

模板[編輯 | 編輯原始碼]

模板 (Templates) 和類型是代碼的命名代碼塊,可以在代碼中多次使用,例如按鈕、窗口背景和文本樣式。這有助於保持相同的樣式,並減少我們編寫的代碼量。編輯模板將同時編輯所有的實例!

模板可以存儲整個窗口的內容或僅存儲一行,就像這個例子:

template Window_Size_Sidebar
{
		size = { 610 100% }
}

這個模板可以與"using = Window_Size_Sidebar"一起使用,基本上會用模板的內容替換 "using" 行。

模板是全局的,可以在任何.gui文件中定義。大部分遊戲模板存儲在 gui/shared 目錄下。本地版本 local_template 必須在同一文件中定義。

常用的模板和類型可以在UI庫中找到。要訪問它,打開控制台,切換到發布模式(Release mode),然後會出現一個名為 "UI Library" 的新按鈕。

類型[編輯 | 編輯原始碼]

雖然模板可以只包含一個屬性,但類型(Types)始終是完整的部件,比如按鈕或小部件。

text_singletext_multi 是具有許多預定義屬性的文本框類型,因此我們不需要每次重新輸入它們,只需簡單地寫:

text_single = {
	text = "my text"
}

類型的定義方式略有不同,首先要創建一個命名的組:

types Standard_Types
{
	type text_single = textbox {
	...
	}
}

塊覆寫[編輯 | 編輯原始碼]

模板和類型可以具有命名的覆寫塊(blockoverride),這使我們能夠在不更改整個模板的情況下編輯實例的一部分。例如,一個模板可以有一個默認的文本塊:

block "text"
{
	text = "default_text"
}

為了覆蓋其中的內容,我們可以在我們的實例中添加一個具有相同名稱的覆蓋代碼塊:

blockoverride "text"
{
	text = "actual text"
}

我們也可以像這樣從我們的實例中移除它: blockoverride "text" {}

增加模組兼容性[編輯 | 編輯原始碼]

在製作一個涉及gui的mod時,所寫的gui代碼會與其他mod的代碼覆蓋同一個文件而無法同時運行。

模組開發者可以通過使用模板並為另一個模組創建一個「鉤子(hook)」來實現兼容性。

為了做到這一點,每個模組開發者在已修改的窗口中包含其他模組的模板,而模板本身則在單獨的文件中定義。

例如,全屏理髮店模組包括了來自社區風味包(Community Flavor Pack)的「using = cfp_bg_buttons」模板,該模板添加了額外的背景。如果用戶未訂閱社區風味包,則無法找到該模板,也不會對遊戲產生任何影響。如果用戶同時訂閱了這兩個模組,則模板會被應用。

當然,這需要模組開發者合作,為彼此的模組設置模板。

腳本化的圖形用戶界面[編輯 | 編輯原始碼]

腳本化的GUI (Scripted guis) 實質上是從用戶界面觸發的隱藏事件。

它們以 .txt 文件的形式存儲在遊戲 /common/scripted_guis 目錄下,與 .gui 文件不同,無法直接從遊戲中重新加載。

腳本化GUI的基本結構如下:

gui_name = { 
	scope = character 		# 根作用域, 即:影响的目标
	saved_scopes = {} 		# 其他额外的目标

	is_shown = {} 		# 在用户界面可见吗?

	ai_is_valid = {} 		# AI可用吗? 默认不可用.

	is_valid = {} 		# 玩家可用吗?

	effect = {			# 它做什么
		custom_tooltip = ""	# 添加提示
	}	 			
}

不是所有代碼塊都是必須的。 有時一個腳本gui文件中可能只有一個作用域 (scope) 、一個是否顯示 (is_shown) 、一個效果 (effect) 。

.gui 文件中我們用如下的代碼:

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)]"

沒有數據內容 (datacontext) 是無法將腳本GUI與元素聯繫起來的。

在這個例子中,腳本GUI 是通過全局函數 GetPlayer.MakeScope 來為玩家設定範圍的。它可以更改為角色窗口中的某個人,例如 CharacterWindow.GetCharacter.MakeScope,或者如果範圍是一個省份,則使用 HoldingView.GetProvince.MakeScope

ScriptedGui.Execute 用於按鈕,它將執行腳本界面中 effect 塊中列出的所有內容。

在 is_shown 和 is_valid 塊中,IsShownIsValid 用於檢查條件。

BuildTooltip 可以與文本框一起使用,在用戶界面中顯示自定義文本。

為了將另一個作用域保存到我們的腳本化圖形用戶界面中,我們使用 AddScope

 "[ScriptedGui.Execute( GuiScope.SetRoot( GetPlayer.MakeScope ).AddScope('target', CharacterWindow.GetCharacter.MakeScope ).End )]"

然後我們在腳本化GUI中使用相同的名稱:

 saved_scope = {
   target
 }

通過這種方式可以保存多個作用域。

在點號或左括號之前不要加空格!Execute( 是正確的寫法,Execute ( 是錯誤的寫法。其他空格可以省略,但它們有助於提高可讀性。

顯示變量或腳本值[編輯 | 編輯原始碼]

變量或腳本值可以在用戶界面顯示,就像這樣:

變量:

text = "[GetPlayer.MakeScope.Var('test_var').GetValue|1]"

腳本值:

text = "[GuiScope.SetRoot( GetPlayer.MakeScope ).ScriptValue('test_value')|0]"

在這個例子中,變量存儲在玩家角色中,名為 test_var 。可以觸及任何其他作用域,只需記得添加 MakeScope 即可。

結尾的 "|1" 是可選的,它會截取所有小數位,只保留一個小數位,所以不論是 1.573 還是 1.5,您都將看到 1.5。請注意,這不會對數值進行四捨五入。您可以將其設置為任意數字,並添加 % 以將其轉換為百分比,如果數值為正或負,還可以添加 = 或 + 以給數值上色。

在事件中使用本地化顯示值時,我們使用以下方法:

  event_var: "[ROOT.GetCharacter.MakeScope.Var('test_var').GetValue|0]"
  
  event_value: "[SCOPE.ScriptValue('test_value')|0]"

如果你有一個保存的作用域(這裡命名為"target"),它會變成這樣:

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

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

展示數據列表[編輯 | 編輯原始碼]

我們可以創建自定義字符列表,例如用於構建社交網絡(societies)。

要實現這一點,首先需要將它們添加到一個變量列表(list)中。可以通過事件或腳本GUI來完成,就像這樣:

effect = {
	every_living_character = {
		limit = {
			has_trait = paranoid
		}

		root = {
			add_to_variable_list = {
				name = secret_society
				target = prev
			}
		}
	}
}

如果我們將此效果應用於玩家,他們將成為root,因此列表將存儲在他們身上。

然後,我們使用任何列表框(vbox、dynamicgridbox、fixedgridbox),將數據模型設置為我們的列表:

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

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

			portrait_head_small = {}

			text_single = {
				text = "[Character.GetNameNoTooltip]"
			}
		}
	}
}

新窗口和開關[編輯 | 編輯原始碼]

沒有簡單的方法來創建一個新窗口並使您的模組與其他模組兼容。我們必須要麼覆蓋hud或其他窗口,要麼使用事件來打開窗口。

通常的方法是將新窗口添加到ingame_topbar部件的hud.gui文件中,然後添加一個按鈕來顯示/隱藏它。

另一種方法是重寫一個未使用的窗口,比如test_gui,並添加一個帶有顯示該窗口的控制台命令的按鈕。

有幾種方法可以添加切換功能,以下三種方法在遊戲會話之間不保持持久性,但可以在.gui文件本身中輕鬆設置:

  • P社GUI組件(PdxGuiWidget) - 直接簡單,但在有多個切換功能時會變得複雜
  • 動畫(Animations) - 長一些,但更容易觸發多個操作
  • 系統變量(System variables) - 有些複雜,但非常靈活和方便

腳本GUI允許我們將切換功能保存到保存文件中作為變量,並觸發多個操作,但它們更複雜,並且必須在單獨的文件夾中進行設置。

P社GUI組件的開關[編輯 | 編輯原始碼]

PdxGuiWidget 是一個簡單的功能,用於隱藏或顯示指定的元素。

在這個示例中,我們有一個包含隱藏子菜單的容器,一個按鈕用於顯示子菜單,另一個按鈕用於隱藏子菜單。

  1. 當點擊第一個按鈕時,它會返回到其父代,在父代中搜索子菜單和另一個按鈕,然後顯示它們並隱藏自身。
  2. 第二個按鈕會搜索元素和按鈕,並顯示它們並隱藏自身。
container = {
	button = {
		name = "show submenu"

		onclick = "[PdxGuiWidget.AccessParent.FindChild('submnenu').Show]"
		onclick = "[PdxGuiWidget.AccessParent.FindChild('hide submenu').Show]"
		onclick = "[PdxGuiWidget.Hide]"
	}
		
	button = {
		name = "hide submenu"
		visible = no

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

	widget = {
		name = "submenu"
		visible = no
	}
}

如果元素之間有更多的父/子級別分隔,我們可以像這樣重複使用 AccessParent

onclick = "[PdxGuiWidget.AccessParent.AccessParent.AccessParent.AccessParent.FindChild('submnenu').Show]"

每個按鈕都可以隱藏或顯示多個任意類型的元素。您只需要提供名稱。

優點:

  • 對於簡單的切換設置很容易。

缺點:

  • 切換將在每次重新啟動遊戲時重置。
  • 如果您想要隱藏多個事物或切換,代碼會變得非常冗長和難以管理。
  • 嘗試隱藏數據列表中的條目(如動態網格框)將只隱藏第一個實例。

動畫開關[編輯 | 編輯原始碼]

我們可以設置通過按鈕(或條件)觸發的動畫來隱藏/顯示元素,甚至移動它們。這樣一來,我們就不需要計算按鈕和窗口之間有多少個父級,而且可以通過一個 onclick 同時觸發多個動作。

前面的示例將如下所示,並且以相同的方式工作。第一個按鈕觸發 "show_submenu",隱藏該按鈕並顯示其他內容,而第二個按鈕觸發 "hide_submenu",隱藏此按鈕和小部件,並顯示第一個按鈕。

container = {
	button = {

		state = { # this is an animation
			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]"
		}
	}
}

雖然較長,但動畫可以保存為模板並以一行代碼的方式重複使用,就像 using = hide_animation 。全屏理髮器使用很多動畫,如果你需要更好的例子。

優點:

  • 更容易將許多事物連結在一起,甚至打開一個不同的窗口並觸發其中的動畫

缺點:

  • 動畫塊可能相當冗長
  • 當遊戲重新啟動時,所有開關都會重置

腳本GUI開關[編輯 | 編輯原始碼]

腳本化的GUI允許我們使用腳本來切換可見性。它們使得管理多個項目更加容易,但在大量使用時可能會影響性能。

對於一個腳本化的切換,我們需要在common/scripted_guis/目錄下創建一個.txt文件。文件的名稱可以任意取。

這個文件中的基本切換看起來像這樣:

gui_toggle = {
	scope = character
	
	is_shown = {
		has_variable = gui_toggle
	}
	
	effect = {
		if = {
			limit = {
				has_variable = gui_toggle
			}
			remove_variable = gui_toggle
		}
		else = {
			set_variable = gui_toggle
		}
	}
}

單擊時,它會向作用域角色添加一個變量,並在再次單擊時將其刪除。

然後,如果該變量存在,我們的窗口將可見。如果不存在,則隱藏。

這就是 GUI 文件的外觀。我們可以只使用一個按鈕,因為其功能會隨著每次點擊而改變。

container = {
	button = {
		datacontext = "[GetScriptedGui('gui_toggle')]"
		onclick = "[ScriptedGui.Execute( GuiScope.SetRoot( GetPlayer.MakeScope ).End )]"
	}

widget = {
	datacontext = "[GetScriptedGui('gui_toggle')]"
	visible = "[ScriptedGui.IsShown( GuiScope.SetRoot( GetPlayer.MakeScope ).End )]"
	}
}

"GetPlayer" 是一個全局Promote函數,返回玩家角色。

"Player.MakeScope" 將我們的玩家設定為腳本化圖形界面的作用域,這意味著我們將變量存儲在這裡。

如果你想要為兩個按鈕設置不同的工具提示,可以使用兩個按鈕。在這種情況下,將 'visible' 屬性複製到兩個按鈕上,但是在其中一個上添加 'Not' ,這樣它就會默認隱藏起來。

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

優點:

  • 切換設置會保存在角色中,除非角色死亡或開始新遊戲,否則不會重置。
  • 更容易連結多個對象,甚至在不同的窗口中。

缺點:

  • 更難記住語法(從這裡複製代碼以減少錯誤的機會)。

系統變量[編輯 | 編輯原始碼]

系統變量是遊戲內部使用的,它們不會被保存,也不能通過腳本直接訪問。 對於它們不需要進行設置,因為可以直接在.gui文件中創建。 使用系統變量的語法為:

onclick = "[GetVariableSystem.Toggle( 'var_name' )]"

或者:

datacontext = "[GetVariableSystem]"
onclick = "[VariableSystem.Toggle( 'var_name' )]"

可用的函數如下:

  • Clear - Clear( 'var_name' ) 清除變量
  • ClearIf - ClearIf( 'var_name', Condition ) 如果條件為真,則清除變量
  • Exists - Exists( 'var_name' ) 布爾型,如果變量存在則返回true
  • Get - Get( 'var_name' ) 字符串型,返回存儲在變量中的值
  • HasValue - HasValue( 'var_name', 'string' ) 布爾型,如果存儲的值與提供的值匹配則返回true
  • Set - Set( 'var_name', 'string' ) 將存儲的值設置為提供的值
  • Toggle - Toggle( 'var_name' ) 如果變量存在則清除它,如果不存在則創建它

系統變量開關[編輯 | 編輯原始碼]

一個使用此文件中的系統變量的基本切換如下所示:

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

	widget = {
		visible = "[GetVariableSystem.Exists( 'gui_toggle' )]"
	}
}

點擊時,系統變量根據其存在與否切換,然後用於顯示/隱藏部件。

帶有系統變量的選項[編輯 | 編輯原始碼]

一個包含三個選項的基本設置如下:

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' )]"
	}
}

注意,變量最初沒有值,因此沒有任何小部件會顯示。

要設置默認選項卡,需要由打開窗口的按鈕設置變量的值。

button = {
	onclick = "[GetVariableSystem.Toggle( 'gui_toggle' )]" # this opens the window
	onclick = "[GetVariableSystem.Set( 'gui_tabs', 'tab_1' )]" # this set the default tab
}

或在窗口顯示時使用狀態塊:

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

或者,可以將其中一個小部件設置為在變量不存在時顯示,避免初始值的需求。

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' )]"
	}
}

這相當於第一個示例,其中之一是設置默認值的方法。

優點:

  • 簡單且易於記憶的語法
  • 更容易連接許多事物,甚至在不同的窗口中
  • 可以通過附加命令進行擴展(見下文)以顯示全新的窗口,避免在hud.gui中使用小部件的需要

缺點:

  • 無法直接與腳本交互,必須使用GUI進行設置和清除
  • 可能更難跟蹤
  • 遊戲重新啟動時,所有切換將重置

添加新的UI元素[編輯 | 編輯原始碼]

在1.5.0版本之前只能通過控制台指令ExecuteConsoleCommand( ... ) 來創建任何新窗口。

在1.5.0版本之後可以在Crusader Kings III\game\gui\scripted_widgets下添加新的UI元素,這對模組的兼容性和UI的動畫支持有很大的改善。

首先需要在 .gui 文件創建一個可以被顯示的window元素,例如 "gui/custom_windows/my_window.gui"。這個window必須要有一個名字。

window = {
	name = "my_custom_window"
	parentanchor = center
	layer = middle
	size = { 100 100 }
	using = Window_Background
        GetVariableSystem.Exists('my_menu_open')
}

上面的代碼以屏幕中心未基準創建了一個100x100像素的window,最上面的name被用於在其他代碼中創建這個window。

在1.5.0版本之後可以在 Crusader Kings III\game\gui\scripted_widgets 下添加新的UI元素,這可以取代之前使用控制台創建widget的方法。

首先需要在 gui\scripted_widgets 目錄下創建一個.txt文件,例如 "gui/custom_windows/my_scripted_widgets.txt"。並且在文件中調出之前創建好的window:

gui/test_custom_widget.gui = my_custom_window

以上為:文件的路徑/文件的名字.gui = 文件中window的名字。

所有被添加進這個文件的UI元素都將在遊戲開始時被創建,每行代碼都將創建一個新的元素,所有我們可以創建兩個相同的UI元素,不過由於參數相同會重疊在一起。

所有在這個文件夾下的.txt文件都將被加載,所以這對不同mod的兼容性有所提升,除非文件重名或者UI位置重疊。

我們不僅僅可以創建window,好可以是widget和其他任何可以被創建的元素,這令模組作者們可以在不修改 hud.gui 的情況下新增自己的hud元素,例如頂部的資源顯示和側邊的按鈕。

我們需要做一個開關按鈕來控制這個window的顯示:

button = {
	onclick = "[GetVariableSystem.Toggle('my_menu_open')]"
}

以上通過開關一個系統參數來實現UI元素的顯現和消失。