[譯]Godot系列教程四 - 編寫腳本


編寫腳本(Scripting)

簡介

關於無需編程即可創建視頻游戲的那些工具的談論有很多。不用學習編程知識對很多獨立開發者來說就是一個夢想。這種需求 - 游戲開發者、甚至在很多公司內部,希望對游戲流程擁有更多控制權,已經有很長一段時間了。

很多引擎產品號稱是無需編程的環境,但相對於傳統的編碼開發流程,這些產品的最終使用結果,經常是做不出完整的作品、或者是能做出也太復雜或太低效。整個過程在這種環節反而耗費太多時間。實際上,游戲引擎的通常趨勢是:在這種為了實現特定任務而必須要編碼的地方,添加一些工具來盡量減少編碼量、提升開發速度。

基於這種理解,Godot針對這個目標采用了一些有效的設計決策。第一點也是最重要的一點就是場景體系。起初它的目標並不明顯,但之后很好的發揮了作用:減輕了程序員為實現架構而需要額外編碼的工作。

在使用場景體系設計游戲時,整個項目被划分成互補的場景 - 不是獨立的。場景之間互補,而不是相互獨立。后續會有大量示例,但請先記住這點。

對於已有了專業編程知識的人來說,這是一種與MVC不同的設計模式。Godot承諾:你丟棄MVC編程習慣並換用"場景即complement"模式后,還能擁有高效。

Godot也使用 繼承 <http://c2.com/cgi/wiki?EmbedVsExtend>__ 模式進行腳本編程,意味着腳本可以繼承所有可用的引擎類。

GDScript

:ref:doc_gdscript 是Godot環境內運行的一種動態類型腳本語言。它的設計目標為:

  • 簡單、熟悉、盡可能易於學習
  • 代碼可讀性高、錯誤安全處理;語言多數借鑒自Python

開發者通常只需花幾天學習它,兩周內就能完全適應。

和大多數動態類型語言一樣,更高的生產效率 - 易於學習、編碼很快、無需編譯等,意味着對應的是性能的降低,但引擎的大多數關鍵代碼是由C++編寫(矢量運算、物理學、數學、索引等),這使得游戲最后的性能比多數類型的游戲要好。

而且如果真有更高的性能要求,關鍵部分的代碼可以用C++重寫並暴露給腳本調用。這時,你只需要將GDScript類替換成C++類即可,不需要修改游戲其它地方。

編寫場景腳本

在此之前,請確保你已經讀過 :ref:doc_gdscript 參考文檔了。它是一種簡單的語言,並且參考文檔也很短,大致過一遍應該花不了幾分鍾。

場景設置

本文將開始編寫一個簡單的GUI場景。用“addnode”對話框創建下述的節點和層級:

  • Panel
    • Label
    • Button

在場景樹面板,看起來效果是這樣:

在2D編輯器面板中要像這樣:

最后保存場景,取名"sayhello.scn"

.. _doc_scripting-adding_a_script:

添加腳本

右鍵點擊Panel節點,然后在右鍵菜單中選擇"Add Script":

彈出“script creation”對話框。對話框中可以選擇語言、類名等。腳本文件中,GDScript是不用類名的,所以這個字段是不可編輯的。這個腳本相應的繼承自“Panel”,即繼承該Panel類型的節點(代碼其實會自動填充的)。

填寫腳本的路徑名然后選擇“Create”:

完成操作后,腳本被創建並添加到該節點下。在該節點下可以看到一個額外的圖標及“script”屬性:

打開腳本編輯器后,你就會發現已經自動填充了一段前面所述的模板代碼:

當該節點(包括它所有的子節點)進入當前活躍狀態的場景時,"_ready()"函數會被執行。記住哦:這個不是構造函數,構造函數是“_init()”。

腳本的角色

腳本給節點添加了行為,它用於控制該節點以及其它節點(子節點、父節點、同一級的兄弟節點等)的功能。腳本的局部作用域僅是該節點;該節點的那些虛擬函數被該腳本捕捉。

處理信號

在大多數GUI節點中都用到了信號(Signal),其實其它節點也有。當一些特定類型的動作發生時,信號就會被“發射”出來,可以連接到任意腳本實例的任意函數。這一步中,按鈕的"pressed"信號會被連接到一個自定義函數。

編輯器中有連接信號到腳本的界面:選中場景樹中的節點,然后選擇“節點”選項卡,再選中其中的"Signals"選項卡。

此時我們主要對"pressed"信號感興趣。除了可用可視化界面來完成信號連接操作,也可以通過腳本代碼來進行。

針對這種情況,Godot的程序員可能用得最多的一個函數:
:ref:Node.get_node() <class_Node_get_node>.
該函數使用路徑來獲取該場景中當前樹狀結構或任意地方的節點。

要獲取到該按鈕,需要用下述寫法:


    get_node("Button")

下一步,當按鈕被按下時,會添加一個回調 - 改變標簽的文本內容:


    func _on_button_pressed():  
        get_node("Label").set_text("HELLO!")

最后,該按鈕的"pressed"信號需要在_ready()函數中進行連接, :ref:Object.connect() <class_Object_connect>.


    func _ready():
        get_node("Button").connect("pressed",self,"_on_button_pressed")

腳本的最終內容大致如下:


    extends Panel

    # member variables here, example:

    # var a=2
    # var b="textvar"

    func _on_button_pressed():
        get_node("Label").set_text("HELLO!")

    func _ready():
        get_node("Button").connect("pressed",self,"_on_button_pressed")

運行場景,按下按鈕后應該就能得到預期的結果:

注意: get_node(path)僅會在節點的最直接的下一節范圍內查找,即Button必須是Panel子一級節點,否則是訪問不到的。假如ButtonLabel的子一級節點,代碼就需要修改了:


    # not for this case
    # but just in case
    get_node("Label/Button") 

而且要記住,這里引用的是節點的名稱而不是節點的類型名。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM