Godot入門到棄坑:GDScript精要——變量


3樓貓 發佈時間:2024-01-27 10:32:31 作者:cameLcAsE Language

上一篇中我們實際上已經講解了函數的很多重要內容。這一篇中我們來學習另一個重要的基礎概念——變量。
單刀直入,變量(variable)是給數據取的名字。
每次需要用到1的時候,我可以直接寫1。但是每次寫114514的時候,我每次都要按6下,就顯得麻煩了,更不用說圓周率這麼長的東西了。所以我可以給它取個名字:
在GDScript中定義變量時,首先寫下關鍵字var(variable的縮寫),後面是變量名稱,等號後面是它的初始值。
這句話我們讀作“定義變量n,並將114514賦值(assign)給它”。這裡的=是等於號沒錯,但是在這裡它不是“等於”的意思,而是要從右往左看,是“將114514賦值給n”的意思。而在編程語言中表達“等於”含義的符號將在未來講解。
這樣在每次需要用到這一數字時,我都可以通過這個變量來獲得。例如在之前的可以動的logo的腳本中,我們是讓它固定每次移動5個單位。現在我們來定義一個叫speed的變量,用來代表它的移動速度:
要改變速度,只需要改變speed的值就好了。

數據類型

對於計算機——準確地說是對於CPU來說,數據沒有任何意義,它只需要完成它的工作就好。但是對於人類來說,數據在具體的問題中有具體的含義。將數據分為不同類型有助於我們防止錯誤地使用數據。比如我們一般不希望將一隻熊貓除以數字2。
GDScript中可以為變量標註數據類型。除了上述提到的那樣可以幫助我們防止錯誤,還能讓編輯器為我們提供更準確的自動補全建議。例如,我們將speed標註為int類型:
int是integer的縮寫,即整數。我們會不斷遇到各種各樣的數據類型,不同的類型有不同的操作可用。實際上,各節點也有自己的所屬類型。
此外,除了整數,也有小數類型,在GDScript中,小數用float表示,float指的是浮點數(floating-point):
此前我們在輸出Hello World時已經用到了字符串。字符串的類型名稱為String(其本意就是“串”的意思,在編程中一般就指字符串,也是弦的意思):
前面提到,類型系統的作用之一就是防止對數據進行誤操作。你可以試試看把數字2給這個標註為String的變量。
編輯器會報錯:Cannot assign a value of type "int" as "String".即無法把類型為int的值作為String賦給變量。要想要一個內容為2的字符串我們要寫"2"。
你可能已經注意到,整數和小數之間可以自動轉換不會報錯。但是字符串和數字沒法,我們需要一些函數來實現數字到字符串的轉換,例如:
這裡用到了num函數,它可以把數字轉換為String。
前面我們也看到在實現獲得鍵盤輸入時出現了Input.is_action_pressed這種寫法,這裡該怎麼解讀呢?這個點就相當於說後面這個東西是屬於前面這個東西的,和前面這個東西是相關的。在你輸入這個點時,你可以提供自動補全建議看到String的更多可用函數,可以先自己探索一下。

我怎麼知道哪個函數有什麼用呢

告訴你一個秘密。按住Ctrl鍵,然後移動鼠標,你會發現腳本編輯器中很多東西下方就會出現一條下劃線:
變成小手的時候點一下,此時就來到了文檔中對應的部分。
這就是各類開發工具必不可少的文檔(Documentation,簡稱docs)。會介紹各種類型、函數等等。這裡就提到了這個num函數有什麼用,以及它的頭部是如何定義的。
可以注意到左側的方框中顯示的是已打開的文件(包括腳本文件和文檔),文檔看夠了點擊自己要編輯的文件回去就好了。右上方的箭頭符號也是和各種瀏覽器一樣功能,可以前進後退。
同樣,按住Ctrl點擊類型名稱可以直接進入某個類型的文檔,在裡面會介紹該類型的各種可用函數。也可以在在線文檔中查詢
由於各種類型的函數有很多,不可能有人會隨時記得有哪些函數、各個函數怎麼用。合格的開發者的基本技能之一就是要會查文檔。

函數的類型

前面講解函數的時候沒有提到的是,函數本身也有類型,這裡主要指的是它返回值的類型。上面提到的num函數的類型應該如何解讀呢?
String num(number: float, decimals: int = -1) static
最開頭的是返回值類型,後面和函數定義時類似,依次是函數名稱和參數列表。這裡有兩處我們暫時不太清楚。
首先是最後的static,鼠標放到這個詞上面會彈出提示:
它說,這個方法(暫時視作一類特殊函數)不需要實例來調用,可以直接用類名來調用。很抱歉,這裡暫時要賣個關子。但是它就是說我們可以直接通過String類型的類型名String加上點來調用就行了,而不需要通過某個具體的String來調用。比如我們不需要寫"haha".num(123)來將123轉換為字符串(儘管可以這樣做,但是純粹多此一舉)。
還有一處就是decimals: int後面的=-1,這是在之前將定義函數時沒有提到的。這裡就是說這個decimals參數是可選的,也就是說你可以不給它傳遞一個具體參數,沒有給定的時候就採用默認值,所以我們前面只傳遞一個參數就可以調用num。從文檔中可以得知,這個參數決定了轉換為字符串時數字保留多少位小數。-1就是默認最多保留14位。
不過,在定義函數時,函數的返回值類型並不是像文檔中這樣標註的(寫到前面)。我們可以看看模板中的_process函數:
參數列表的括號後面有一個箭頭(實際上是->),然後後面有個void。void和float一樣是綠色的,暗示它也是一個類型。但是void是啥意思呢?它也是啥也沒有的意思,就是說這個函數沒有返回值。
還記得double嗎?我們來完善它的定義:
這樣一來,就不怕有人亂用了!現在試試看正確使用double函數然後把結果保存到一個變量中吧!如果你給了一個字符串給它,編輯器是會報錯的。
提醒一下,在函數中也可以定義變量。但是一定要記得縮進。
同樣,我們可以自己給函數定義默認參數,這裡的語法和文檔中是一樣的:
調用時就可以省略了:
但是要注意的是,如果有帶默認值的參數,這些參數必須放在最後面:
這樣是不行的,因為有默認值的參數可以省略,但要是省略了第一個參數,傳入兩個參數,怎麼確定你是傳入的前兩個,還是後兩個呢?所以要這樣:

常量

一個變量可以隨意改變其值:
和變量相對的,常量(constant)的值無法改變。對於一些在遊戲運行過程中不會發生改變、且不應該改變的值,應當定義為常量。常量用const關鍵字定義:
嘗試修改常量的值會直接報錯。最為人熟知的數學常量π,在GDScript定義為常量PI。

變量和函數在何處可用

先思考一個問題,這裡的代碼輸出了什麼?
輸出了1、2、2。
首先看函數g的定義。前面講到,定義變量要用var,這裡沒有var。所以這個x指的是函數外面這一級的這個x。要是你把x換成一個哪裡都沒出現過的變量(比如y),那麼就會出現錯誤。變量需要先定義再使用。而函數f呢,它自己定義了一個x,在後面它要print(f)時,首先就會找到函數內部定義這個x,因此也就輸出1。
這裡的問題是變量作用域(scope)的問題。在編程中,作用域指的就是某個名字在哪個範圍內可見。簡單來說,Godot會從內向外逐級查找一個名字是否已經定義,如果找到了,那就是它,也不用再往外找了。如果一直找到頂級都沒找到,那就報錯!
此外可以注意到,在一個腳本文件級別上,變量和函數定義的先後順序無關,它總能找到其定義:
交換定義順序也沒問題。但是你必須確保在使用某個名字的時候,能找到它的定義。

問題

還記得講Hello World那篇文章最後我們複製了好幾個logo出來嗎?如果我們更改腳本中speed的值,大家的移動速度都跟著變了。要是我們想讓它們有不同的移動速度要怎麼辦呢?
你可能會想,新建一個腳本,然後設置speed為不同的值。就這個需求來說,可以,但是沒有靈活性。如果需要十種不同的速度難道要複製十個差不多的腳本嗎?
針對這個問題有一種更簡單、地道的做法。

export變量

我們已經介紹過右側檢視面板,提到可以在其中修改節點的各屬性。我們能不能把自己的變量也暴露在這裡直接修改呢?當然有!這裡用到@export標記(annotation):
現在在場景中選中你的某個有logo腳本的節點,或者是某個已經放到了場景中的logo場景,然後看右邊:
看,這裡出現了一個可以編輯的項目!就是我們自己的speed變量!如果沒看到的話,可以按一下保存。此外,如果你的場景中和我一樣有好幾個logo,還可以分別給它們設置不同的speed:
Godot會根據變量類型,在檢視面板中對不同類型的export變量顯示不同的編輯控件。

省略類型標註

實際上在一開始,我們在很多地方省略了類型標註。這在GDScript中是允許的。數字可以和數字做運算,這很自然,不需要多說。
然而要是試圖將a和b相加,在編輯代碼時並不會提示錯誤。
但是要是運行遊戲的話,你會被一個錯誤打斷:
就是說不能把加號用到int和String上。但如果我們主動寫上類型標註:
遊戲還沒運行,編輯器就報錯了,你也不能就這樣運行遊戲。
不標註類型,Godot對代碼就行的類型檢查就被延後到了遊戲運行時。開發者常說的“靜態”和“動態”,在很多時候談的就是“編譯時”(處理源代碼的軟件處理人類編寫的代碼的時候)和“運行時”。大家說Python語言是動態類型語言,指的是它在運行代碼之前不檢查代碼中和類型相關的約束,但是在運行時如果出現了類似於上面這種字符串和數字相加的情況就會報錯;相應的,C#是靜態類型語言,說的就是它會儘可能地在運行代碼之前就會執行類型檢查(除非開發者用到了某些故意延遲類型檢查的東西比如dynamic),發現問題。
對於GDScript來說,如果沒標註類型,Godot就不會提前檢查,而是在運行時檢查;反之,我們就會在運行遊戲之前檢查。
當然如你所見,如果處處寫上類型,有時候會很麻煩,但是不寫類型,我們就沒法獲得保障。所以GDScript給你足夠的自由,如果你能確定你的代碼很簡單,或者不需要編輯器根據類型自動補全,那麼你就可以省略類型標註。在後面我們會看到,省略類型標註還有一種重要的作用就是讓函數接受不止一種類型的參數的同時保持函數正常工作。

全局作用域

前面提到了作用域的概念。不過似乎還是解釋不通為什麼print這樣的函數隨處可用?
如果按照前面講的方法打開print的文檔,我們可以在左側看到,這是一個叫@GlobalScope的文檔的一部分,說明print屬於@GlobalScope。
Global Scope的字面意思就是全局作用域,簡單來說這就相當於最外層的作用域,其中定義的各種東西在任何地方都能找到。舉例來說,sin、cos這樣的函數,以及PI這樣的常量都定義在這裡。試著探索一下里面還有哪些東西。

數值型變量的常見操作

這裡快速簡單展示一些數值類型變量的常見操作:

字符串的常見操作

更多操作我們後續會逐漸講到。你也可以自行探索!
在這一篇文章中,我們對於編程中最重要的概念之一變量有了一個穩固的基礎,我們也討論了什麼是類型,什麼是類型系統,以及類型標註的重要性。下一篇文章我們要認識另一個重要的基礎類型,以及和函數不同的一種代碼塊!

© 2022 3樓貓 下載APP 站點地圖 廣告合作:asmrly666@gmail.com