程式語言基礎 link

在我們詳細介紹Ren’Py的程式語言之前,我們必須首先介紹一下Ren’Py腳本的結構。這包括:腳本文件如何分割為語句塊(block),而語句塊(block)是由多行腳本構成;各行腳本如何分割為基本元素(element),並構成語句(statement)。

文件 link

Ren’Py遊戲腳本由game目錄下眾多副檔名為 .rpy 的文件組成。Ren’Py會依次檢查每一個文件(按照拉丁字母順序),並把文件內容用作腳本。

總之,把一份腳本打散成多個文件,與一份腳本保存在一個大文件中,兩種方法是等效的。主控流程可以在文件之間透過調用腳本標籤(label)跳轉。把一份腳本切分為多個文件跟創作者個人風格有關——有些遊戲製作者喜歡使用小文件(像一個事件一個文件,或者每一天一個文件),而其他製作者傾向於使用一個完整的大腳本文件。

為了提高載入速度,Ren’Py啟動時會把 .rpy 文件編譯為 .rpyc 文件。當一個 .rpy 文件發生變更時, .rpyc 只有重啟Ren’Py進程才會更新。另外,如果一個 .rpyc 文件並沒有對應的 .rpy 源文件,這個 .rpyc 文件依然會被使用。當 .rpy 文件已經刪除而 .rpyc 文件沒有刪除的情況下,可能會導致運行時出現問題。

檔案名必須以字母或者數字開頭,並且開頭不能用“00”,因為“00”開頭的文件是Ren’Py內部使用的。

基礎目錄 link

基礎目錄是包含構建該遊戲所有文件的目錄。(這可能也包含一些跟遊戲無關的文件。)像README(須知)類文件應該放在基礎目錄中。

基礎目錄會被創建在Ren’Py目錄下,以創作者遊戲的名稱命名。例如,如果創作者的Ren’Py目錄名為renpy-6.11.2,遊戲名為“HelloWorld”,創作者的基礎目錄就是renpy-6.11.2/HelloWorld。

遊戲目錄 link

遊戲目錄很可能是在基礎目錄下一個名為“game”的目錄。例如,如果創作者的基礎目錄是renpy-6.11.2/HelloWorld,創作者的遊戲目錄就會是renpy-6.11.2/HelloWorld/game。

Ren’Py會按以下順序依次搜索所有目錄:

  • 根據可執行檔案名,去掉後綴名。比如,如果可執行文件被命名為moonlight.exe,Ren’Py將會尋找基礎目錄下名為“moonlight”的目錄。
  • 根據可執行檔案名,去掉後綴名,再去掉下劃線“_”及其前綴。比如,如果可執行文件是moonlight_en.exe,Ren’Py會尋找名為“en”的目錄。
  • 按照“game”、“data”、“launcher”的順序。

啟動器(launcher)只會識別“game”和“data”目錄。

遊戲目錄包含了遊戲內使用的所有文件。其自身及其所有子目錄下,會被掃描出所有 .rpy.rpyc 文件,並拼裝組合成整個遊戲的腳本。 .rpa 文件也會被掃描到,這些文件會被遊戲自動使用。當遊戲指定某個需要載入的文件路徑時,文件路徑會與遊戲目錄關聯。(注意config.searchpath可以控制並修改這一點。)

注釋(comment) link

Ren’Py腳本文件可能會包含一些注釋(comment)。每條注釋都以記號(‘#’)開頭,直到該行文本結束。不過有一種情況例外,注釋(comment)不能用在某個字串的一部分。

# 這是一條注釋。
show black # 這也是一條注釋。

"# 這不是一條注釋因為它是一個字串的一部分。"

Ren’Py忽略注釋, 所以腳本處理過程中那段注釋如同不存在。

邏輯行(logical line) link

一個腳本文件可以被切割為一些邏輯行(logical line)。一條logical line(邏輯行)往往在文件中頂格起始,換行結束,有一些例外:

  • 該行結尾是反斜槓(‘’)。
  • 該行包含幾個開括號字元(‘(‘, ‘{‘, or ‘[‘),而該行沒有匹配到對應的閉括號字元(‘)’, ‘}’, or ‘]’。
  • 一個包含換行的字串中。

一個邏輯行結束後,下一行就是另一個邏輯行的開始。

Ren’Py程式語言中大多數語句都只由一個邏輯行,有一些語句則包含多行。

"這是一條邏輯行"

"因為這條邏輯行包含一個字串,
 所以換行也依然是同一條邏輯行。"

$ a = [ "由於括號的存在,這也是一條",
        "可以突破換行的邏輯行。" ]

空的邏輯行會被忽略。

縮進和語句塊(block) link

縮進 是我們指代Ren’Py語句每個邏輯行開頭的空間。在Ren’Py中,縮進必須使用空格。

縮進被用來將一些語句分組形成語句塊(block)。一個語句塊是一組邏輯行,通常也是一組語句。將一個文件分割為語句塊的原則是:

  • 一個語句塊在文件開頭被打開載入。
  • 在某個邏輯行使用了比上一行更多縮進量時,就表示開始了一個新語句塊。
  • 在一個語句塊中所有的邏輯行必須保持同樣的縮進量。
  • 當一個邏輯行的縮進量比其他行更少,表示上一個語句塊的結束。

對Ren’Py來說,縮進非常重要,甚至縮進量能引起語法或者邏輯錯誤。同時,縮進的使用表明了塊結構,使我們不需要瀏覽整個腳本文件就能識別出塊結構。

"這是一個語句,後面跟著的是if語句,那是一個語句塊的一部分。"

if True:

    "這個語句是新語句塊的一部分。"

    "這個語句也是新語句塊的一部分。"

"這個語句又是第一個語句塊的一部分了。"

語句元素 link

Ren’Py語句由一些基本部分組成。

關鍵字(keyword)

關鍵字是一個英文單詞,必須在遊戲腳本中合法出現。關鍵字通常用於出現在語句和屬性中。

名字以一個下劃線(_)開頭的關鍵字僅限Ren’Py內部使用,除非文件另有說明。當一個關鍵字名字開頭是兩條下劃線__但結尾沒有兩條下劃線__,在使用時它會改變為特定文件版本號。

名稱(name)
名稱以一個字母或者下劃線開頭,之後跟隨著0個或者若干個字母、數字或者下劃線。出於我們的需求,在“U+00a0”和“U+fffd”之間的unicode字元都被認為是字母。
圖像名(image name)

圖像名(image name) 由一個或多個部分構成,以空格分隔。 圖像名的第一部分稱作 圖像標籤(image tag) 。圖像名後面的部分都是 圖像屬性(image attributes) 。圖像的各部分都是由字元、數字和下劃線組成的字串。

例如,一個圖像名為 mary beach night happy 。圖像標籤(tag)就是 mary ,而圖像屬性(attribute)就是 beachnighthappy

字串(string)

字串以一個引用字元(“、’或者`)開頭,包含幾串文字,並以同樣的引用字元結尾。

反斜槓(\)用於字元轉義,一些特殊字元,比如%(需要寫作\%)、[(需要寫作\[)、{(需要寫作\{)。它還用於包含下一行,此時使用\n串。

在Ren’Py字串中,連續多個空格會被壓縮為一個空格字元,除非某個空格前面有一個反斜行。

'Strings can\'t contain their delimiter, unless you escape it.'
簡單表達式(simple expression)

簡單表達式就是一個Python表達式,用於在Ren’Py腳本中運行Python。一個簡單表達式使用以下類型作開頭:

  • 一個變數名。
  • 一個字串。
  • 一個數字。
  • 圓括號中包含的任意表達式。

其後可以接續任意數量的:

  • 名稱前的一個英文句號字元。
  • 圓括號內的Python表達式。

舉例,3(3 + 4)foo.barfoo(42) 都是簡單表達式。但 3 + 4 則不是“簡單”表達式,因為該表達式是一個算式字串且沒有使用圓括號。

at列表(At List)
at列表由一系列簡單表達式組成,使用英文逗號字元分隔。
python表達式
python表達式是指任意的、可能不包含分號的python表達式。這些表達式常用於if和while語句中,處理對應的情況。

通用語句語法 link

大多數Ren’Py語句使用通用的語法。而say語句是個例外,其使用開頭的某個關鍵字標識say語句。如果語句中包含變數的話,會跟在該關鍵字後面。

變數後面會跟著一個或多個特性(property)。特性(property)可以使用任意順序排列,每個屬性均只會出現一次。一項特性(property)以一個關鍵字開頭。對大多數的特性(property)來說,屬性名字會跟之前出現的語法元素(element)之一保持一致。

若該語句包含一個語句塊(block),那行語句會以冒號(:)結尾。否則的話,以換行結尾。

python表達式語法 link

Note

本段內容現在可以先跳過不看。當你覺得無法理解某個樣例,或者你覺得需要理解更深層次的運行機制時,可以再返回來看本段內容。

Ren’Py的很多地方都會用到python表達式。例如,定義一個新角色就意味著調用charactre(角色)的函數。由於python表達式功能十分強大,只是用其很小部分就足以實現一個基本的Ren’Py遊戲。

這是一個python表達式的概要。

整數(integer)
整數是一個不帶小數點的數字。 342 就都是整數。
浮點數(float)
浮點數是一個帶小數點的數字。 .57.9.0 就都是浮點數。
字串(string)
python字串以英文符號的雙引號(“)或單引號(‘)開頭,並使用同樣的符號結尾。斜槓(\)被用來轉義換行符,並可以使用特殊字元(\n)表示換行。與Ren’Py字串不同,python字串不能分多行。
True, False, None
這是三個特殊的值。 True 表示真值, False 表示假值。 None 表示空值。
元組(tuple)

元組(tuple)是一種容器,其元素(item)數量非常重要。例如,我們可以使用一個2維元組(也被稱作pair)來裝寬度和高度數據,或者使用一個4維元組(包含x、y、寬度和高度)來裝一個三角形的數據。

元組(tuple)開頭有一個左括號 ( ,可以由0個或若干個逗號分隔的python表達式,並以一個右括號 ) 結束。比較特殊的是,只有一個元素(item)的元組中,元素後面必須帶一個逗號。各種例子如下:

()
(1,)
(1, "#555")
(32, 24, 200, 100)
列表(list)

列表(list)是一種容器,用來裝各種類型的數據。列表以 [ 開頭,包含一系列逗號分隔的表達式,並以 ] 結束。舉例如下:

[ ]
[ 1 ]
[ 1, 2 ]
[ 1, 2, 3 ]
變數(variable)

python表達式中允許使用變數。透過定義語句或者python語句產生的數值可以存放在變數中。變數以字母或者下劃線開頭,後接0個或若干個字母、數據或下劃線。舉例如下:

name
love_love_points
trebuchet2_range

以下劃線“_”開頭的變數是預留給Ren’Py專用,創作者不應使用。

欄位(field)訪問

python模組(module)和對象(object)都有欄位(field)的概念,可以在欄位(field)後接一個英文句號“.”和一個表達式(通常是一個變數),實現對欄位的訪問。例如:

config.screen_width

實現了對config中screen_width欄位的訪問。

調用(call)

python表達式可以調用一個函數並獲得一個返回值。函數調用以一個表達式開頭(通常是函數名),後面跟著一對圓括號,括號內有一系列參數。參數列表開頭是個python表達式,也是固定位置參數。後面則是關鍵字參數,由參數名、等號和表達式組成。下面是一個例子:

Character("Eileen", type=adv, color="#0f0")

我們調用了Charactre函數。其給定了一個固定位置參數,也就是字串“Eileen”。其給定了兩個關鍵字參數: type 被賦值為 adv ,而 color 被複製為字串“#0f0”。

構造器是一類專門用於返回一個新對象的函數,且會被使用相同的方式調用。

閱讀此份文件時,你可能會看到這樣的函數聲明:

Sample(name, delay, position=(0, 0), **properties) link

這個樣例函數並不真正在Ren’Py中使用,而只存在這份文件中。

這個函數:

  • 函數名為“Sample”
  • 有兩個固定位置參數,分別是name和delay。真實情況下,在文件中應該有參數的詳細說明。
  • 有一個關鍵字參數position,其預設值為(0, 0)。

由於函數結尾是 **properties, 這意味著其可以使用 樣式特性 作為額外的關鍵字參數。 其他的特殊形式結尾還有*args,表示其可以使用任意數量的固定位置參數,而**kwargs表示在文件中已詳細描述過的固定位置參數。

Python的強大,遠非我們這份文件所能完全展現。若希望學習python的更多細節,我們推薦Python入門教學, python.org 。由於我們認為對於Ren’Py來說,更深一層的python知識不是必要的,了解python語句和表達式通常就足夠了。