製作外掛程式

關於外掛程式

外掛程式是一種擴充編輯器功能、加入實用工具的好方法。它可以完全以 GDScript 與標準場景製作,甚至無須重新載入編輯器。與模組不同,外掛程式不需要撰寫 C++ 程式碼或重新編譯引擎。雖然功能上不及模組強大,但你仍然可以用外掛程式做到許多事。請注意,外掛程式性質上就像你平常建立的場景,只是它是透過腳本強化編輯器功能。

本教學將帶你實作兩個外掛程式,幫助你理解其運作方式,並能自行開發。第一個是可加到任何場景中的自訂節點,第二個是加到編輯器內的自訂面板(Dock)。

建立外掛程式

在開始前,請先於任意位置建立一個空專案。這個專案將作為開發與測試外掛程式的基礎。

編輯器要識別新外掛程式,必須先建立兩個檔案:一個 plugin.cfg 設定檔,以及一個包含功能的工具腳本。外掛程式應放在專案資料夾下的標準路徑,如 addons/plugin_name。Godot 提供對話方塊來產生這些檔案,並自動放置到正確的位置。

於主工具列中,點擊 Project 下拉選單,接著點選 Project Settings...。切換到 Plugins 分頁,然後點擊右上角的 Create New Plugin 按鈕。

你會看到出現如下的對話方塊:

../../../_images/making_plugins-create_plugin_dialog.webp

每個欄位的預設提示文字會說明它如何影響外掛程式檔案的產生與設定值。

請依下列範例值繼續操作:

Plugin Name: My Custom Node
Subfolder: my_custom_node
Description: A custom node made to extend the Godot Engine.
Author: Your Name Here
Version: 1.0.0
Language: GDScript
Script Name: custom_node.gd
Activate now: No

警告

在 C# 中,必須取消勾選 立即啟用?,因為 EditorPlugin 腳本就像其他 C# 腳本一樣需要編譯,這意味著必須先建置專案。建置完成後,便可於 專案設定外掛程式 分頁啟用外掛程式。

你最後應該會看到類似如下的目錄結構:

../../../_images/making_plugins-my_custom_mode_folder.webp

plugin.cfg 是一個 INI 格式檔,存放外掛程式的中繼資料。名稱與描述讓其他人明白它的用途,作者欄則確保你獲得正確署名。版本號可讓他人分辨是否為舊版本;若不知如何編寫版本號,請參考 Semantic Versioning。主腳本檔則會在外掛程式啟用時告訴 Godot 如何運作你的外掛程式。

腳本檔案

建立外掛程式後,對話方塊會自動幫你打開 EditorPlugin 腳本。此腳本有兩項必要條件:必須標記為 @tool,否則無法在編輯器正確載入;且必須繼承 EditorPlugin

警告

除了 EditorPlugin 腳本外,你的外掛程式所用的其他 GDScript 也*必須*是工具腳本。若沒有 @tool 標記,該 GDScript 在編輯器中將形同空檔!

妥善處理資源的初始化與清理很重要。建議在 _enter_tree() 虛擬函式中初始化外掛程式,並於 _exit_tree() 負責清理。幸好 Godot 會自動為你產生這些回呼函式。你的腳本大致如下所示:

@tool
extends EditorPlugin


func _enter_tree():
    # Initialization of the plugin goes here.
    pass


func _exit_tree():
    # Clean-up of the plugin goes here.
    pass

這是建立新外掛程式時的良好範本。

自訂節點

有時你會希望多個節點具有某種共通行為,例如可重用的自訂場景或控制元件。實體化(Instancing)很實用,但若在多個專案反覆使用會顯得繁複。一個好方法是製作一個能自動加入自訂行為節點的外掛程式。

警告

透過 EditorPlugin 加入的節點屬於「CustomType(自訂型別)」節點。雖然這適用於所有腳本語言,但功能不如 腳本類別系統。如果你用 GDScript 或 NativeScript,建議優先使用腳本類別(Script Class)功能。

若要建立新的節點型別,可使用 EditorPlugin 類別的 add_custom_type() 方法。這個方法可讓你在編輯器新增自訂型別(節點或資源)。但在建立型別前,需要有一支作為該型別邏輯的腳本。這支腳本不一定要加 @tool,但加上後便可於編輯器內即時運作。

本教學將製作一個按下時會顯示訊息的按鈕。你需要寫一支繼承自 Button 的腳本,或若偏好也可繼承 BaseButton

@tool
extends Button


func _enter_tree():
    pressed.connect(clicked)


func clicked():
    print("You clicked me!")

這樣就完成了基本的按鈕。請將此腳本儲存為外掛程式資料夾內的 my_button.gd。你還需要一張 16×16 的圖示用於場景樹顯示。若沒有現成圖示,可從引擎抓取預設圖示,存到 addons/my_custom_node 資料夾並命名為 icon.png,或直接使用 Godot 預設標誌圖示(preload("res://icon.svg"))。

小訣竅

作為自訂節點圖示的 SVG 圖片,建議在 匯入選項 中啟用 Editor > Scale With Editor Scale**(編輯器縮放同步)及 **Editor > Convert Colors With Editor Theme (隨編輯器主題色彩轉換)。只要圖示顏色與 Godot 編輯器一致,這兩個選項就能讓圖示隨編輯器縮放與主題變換自動調整。

../../../_images/making_plugins-custom_node_icon.png

接下來要將它註冊為自訂型別,才能在 新增節點 對話框中看到。請將 custom_node.gd 腳本改寫如下:

@tool
extends EditorPlugin


func _enter_tree():
    # Initialization of the plugin goes here.
    # Add the new type with a name, a parent type, a script and an icon.
    add_custom_type("MyButton", "Button", preload("my_button.gd"), preload("icon.png"))


func _exit_tree():
    # Clean-up of the plugin goes here.
    # Always remember to remove it from the engine when deactivated.
    remove_custom_type("MyButton")

完成以上步驟後,外掛程式就會出現在 專案設定 的外掛程式清單中,可依 檢查結果 章節說明啟用它。

然後試著加入你剛剛創建的新節點:

../../../_images/making_plugins-custom_node_create.webp

當你新增該節點時,會發現已自動掛上你寫的腳本。請為按鈕設定文字,然後儲存並執行場景。按下按鈕後,你會在終端機(Console)看到訊息:

../../../_images/making_plugins-custom_node_console.webp

自訂面板

有時你會想讓編輯器內隨時有特定工具。最簡單的方法就是做一個外掛程式,新增一個面板(Dock)。面板其實就是以 Control 為基礎的場景,建立方式和一般 GUI 場景相同。

建立自訂面板的步驟和自訂節點一樣。請在 addons/my_custom_dock 資料夾中建立新的 plugin.cfg,並加入以下內容:

[plugin]

name="My Custom Dock"
description="A custom dock made so I can learn how to make plugins."
author="Your Name Here"
version="1.0"
script="custom_dock.gd"

接著在同一資料夾下建立 custom_dock.gd 腳本,內容可參考前述的 標準範本

既然要新增自訂面板,就需要設計面板內容。這其實就是一個標準 Godot 場景:直接在編輯器裡新建場景再編輯即可。

編輯器面板的根節點**必須**是 Control 或其子類別。本教學建議只放一個按鈕。根節點的名稱會顯示在面板標籤上,請取簡明易懂的名字,並記得為按鈕設置顯示文字。

../../../_images/making_plugins-my_custom_dock_scene.webp

將場景儲存為 my_dock.tscn。接著,我們要將這個場景作為面板加入編輯器。請用 EditorPlugin 類別的 add_control_to_dock() 方法。

你需要選擇面板停靠的位置,並指定要加入的控制元件(就是你剛建立的場景)。外掛停用時,記得要**移除該面板**。腳本範例如下:

@tool
extends EditorPlugin


# A class member to hold the dock during the plugin life cycle.
var dock


func _enter_tree():
    # Initialization of the plugin goes here.
    # Load the dock scene and instantiate it.
    dock = preload("res://addons/my_custom_dock/my_dock.tscn").instantiate()

    # Add the loaded scene to the docks.
    add_control_to_dock(DOCK_SLOT_LEFT_UL, dock)
    # Note that LEFT_UL means the left of the editor, upper-left dock.


func _exit_tree():
    # Clean-up of the plugin goes here.
    # Remove the dock.
    remove_control_from_docks(dock)
    # Erase the control from the memory.
    dock.free()

請注意,雖然面板初始會出現在指定位置,但使用者可隨意拖曳並儲存佈局。

檢查結果

現在可以檢查你的成果了。打開 專案設定,切到 外掛程式 分頁。你的外掛程式應該會出現在清單中。

../../../_images/making_plugins-project_settings.webp

你會發現外掛程式預設為未啟用狀態。勾選 啟用 後,面板會立即出現在編輯器主畫面上,無需關閉設定視窗。現在你就擁有一個自訂面板了:

../../../_images/making_plugins-custom_dock.webp

於外掛程式中註冊自動載入/單例

編輯器外掛程式可以在啟用時自動註冊 自動載入,且會在外掛程式停用時自動解除註冊。

這樣一來,若你的外掛程式需要用到自動載入,使用者就不必再手動於專案設定中新增,能更快完成外掛程式設定。

你可用以下程式碼於編輯器外掛程式註冊單例(Singleton):

@tool
extends EditorPlugin

# Replace this value with a PascalCase autoload name, as per the GDScript style guide.
const AUTOLOAD_NAME = "SomeAutoload"


func _enable_plugin():
    # The autoload can be a scene or script file.
    add_autoload_singleton(AUTOLOAD_NAME, "res://addons/my_addon/some_autoload.tscn")


func _disable_plugin():
    remove_autoload_singleton(AUTOLOAD_NAME)

使用子外掛程式

有時一個外掛程式會新增多種功能,例如同時包含自訂節點與自訂面板。這時將各功能拆分成不同的外掛腳本會更有彈性。子外掛程式(sub-plugins)正是為此設計。

首先,先將主要外掛與子外掛都建立成一般外掛:

../../../_images/sub_plugin_creation.webp

然後將子外掛(sub-plugins)移到主外掛資料夾下:

../../../_images/sub_plugin_moved.webp

Godot 會自動將子外掛從外掛程式列表中隱藏起來,使用者無法直接啟用或停用。主外掛腳本需透過程式碼,像這樣啟用或停用子外掛:

@tool
extends EditorPlugin

# The main plugin is located at res://addons/my_plugin/
const PLUGIN_NAME = "my_plugin"

func _enable_plugin():
    EditorInterface.set_plugin_enabled(PLUGIN_NAME + "/node", true)
    EditorInterface.set_plugin_enabled(PLUGIN_NAME + "/panel", true)

func _disable_plugin():
    EditorInterface.set_plugin_enabled(PLUGIN_NAME + "/node", false)
    EditorInterface.set_plugin_enabled(PLUGIN_NAME + "/panel", false)

進階應用

現在你已學會製作基本外掛程式,便能用多種方式擴充編輯器功能。透過 GDScript 就能實現大量編輯器功能,這是打造專業化編輯器又無須深入 C++ 模組的強大方式。

你可以開發自用或分享用的外掛程式,並上傳到 素材庫,讓其他人也能受惠於你的成果。