創作者定義的語句(CDS)允許創作者在Ren’Py中添加自己的語句。這個機制允許添加和使用當前Ren’Py不支持的語法。 CDS比直接使用Python代碼更靈活。在大多數情況下,CDS用於某些重複使用的地方。例如,使用一個入參調用某個函數。 Ren’Py並不知道該函數的功能和執行機制,所以Ren’Py不會執行任何處理,除非遇到代碼執行報錯。 發生異常的情況下,使用CDS允許創作者檢查語法是否正確(比如入參是否是合法的字串),忽略不正確的數據(對於非關鍵函數的報錯,跳過往往比拋異常更有效),預載入可視組件(如果函數使用可視組件), 並使用lint檢查代碼時列印額外日誌訊息(如果runtime忽略就可以在這裡生成報告)。CDS並不保證所有執行能可以成功,只要傳作者編寫的語句代碼越優質,Ren’Py就能“理解”並實現創作者希望需要的功能。
創作者定義的語句必須在python early語句塊(block)中定義。還有,包含創作者定義的語句的文件必須在使用這個語句的文件之前載入。由於Ren’Py按照unicode順序載入文件,通常合理的做法是,在註冊創作者自訂語句的文件加上前綴“01”之類一個不大的數字。
創作者定義的語句不能在定義語句的文件中使用。
創作者定義的語句使用 renpy.register_statement()
函數註冊。
renpy.
register_statement
(name, parse=None, lint=None, execute=None, predict=None, next=None, scry=None, block=False, init=False, translatable=False, execute_init=None, init_priority=0, label=None, warp=None, translation_strings=None, force_begin_rollback=False, post_execute=None, post_label=None, predict_all=True, predict_next=None) link這個函數註冊了一條創作者定義的語句。
判斷下一個語句時調用的函數。
如果 block 的值不是字串“script”,這個函數的入參只有一個,即 parse 返回的對象。如果 block 的值是字串“script”,就會多一個入參,即語句塊(block)第一條語句名對應的對象。
這個函數應該返回一個字串,表示跳轉的腳本標籤(label)名,第二個入參將主控流程切換到標籤對應的語句塊;這個函數也可以返回None,表示繼續執行下一條語句。
menu
和 call screen
語句,該項應設置為True。該項是一個腳本標籤(label),在本條語句執行後將運行對應腳本標籤內的語句。
本條語句後面的語句運行後調用該項實現後續語句的預載入,需要的返回值是一個腳本標籤(label)列表或者SubParse對象。當 predict_all 為True時,該項不會被調用。
parse方法使用一個Lexer對象:
Lexer
linkerror
(msg) link在檢測到的處理錯誤列表(當前位置)中添加一個 msg 元素。這個方法將中斷當前語句的執行,但不妨礙後續語句的處理。
require
(thing, name=None) link嘗試處理 thing ,如果無法完成則報一個錯誤。
如果 thing 是一個字串,嘗試使用 match()
進行處理。
其他情況下, thing 必須是一個lexer對象的其他other方法,並且該方法調用時沒有入參。
如果沒有指定 name 的值,方法的名稱將會用於報錯消息(thing`為字串則直接使用該字串)。
否則,報錯訊息使用 `name 。
eol
() link如果Lexer對象處於這行結尾則返回True。
expect_eol
() link如果Lexer對象不處於某一行腳本的結尾,則產生一個錯誤。
expect_noblock
(stmt) link調用該方法判斷當前語句後面是否為語句塊。 如果找到語句塊則產生一個錯誤。 stmt 應是一個字串,並被添加到報錯消息中。
expect_block
(stmt) link調用該方法判斷當前語句後面是否需要一個非空語句塊。 stmt 應是一個字串,並被添加到報錯消息中。
has_block
() link當前行含有一個非空語句塊時返回True。
match
(re) link匹配一個任意的正則表達式(regexp)字串。
Lexer對象中的所有語句都會使用這個方法。首先跳過空白,嘗試在一行中匹配。如果匹配成功,返回匹配到的文本。否則,返回None。
keyword
(s) link匹配關鍵字 s 。
name
() link匹配一個名稱。名稱不會是內建的關鍵字。
word
() link匹配任何詞,包括關鍵字。返回匹配目標詞所在的整段文本。
image_name_component
() link匹配一個圖像名組件。與word不同,圖像名組件可以用數字開頭。
string
() link匹配一個Ren’Py字串。
integer
() link匹配一個整數,返回包含這個整數的字串。
float
() link匹配一個浮點數,返回包含這個浮點數的字串。
label_name
(declare=False) link匹配一個腳本標籤(label)名,可以是絕對或關聯名稱。 當 declare 為True時,設置為全局腳本標籤名。 (注意該方法實際上不能定義腳本標籤——定義腳本標籤需要使用 label 函數。)
simple_expression
() link匹配一個簡單Python表達式,並將其作為字串返回。 常用於需要一個變數名的情況。不建議修改得到的結果。 正確的做法是將返回結果直接用作計算。
delimited_python
(delim) link匹配一個以 delim 結尾的Python表達式,比如‘:’。 常用於獲取某個分隔符之前表達式的情況。不建議修改得到的結果。 正確的做法是將返回結果直接用作計算。 如果在行尾未匹配到分隔符則產生一個報錯。
arguments
() link在使用括號內的入參列表之前必須先調用該方法。如果入參沒有指定值就返回None,否則返回一個對象。
返回對象有一個 evaluate
方法和一個可選的 scope 字典,返回一個元組。返回元組的第一個元素是固定位置入參的元組,第二個元素是關鍵字入參字典。
rest
() link跳過空白,返回一行的其他內容。
checkpoint
() link返回一個不透明對象,這個對方表現出Lexer當前狀態。
revert
(o) link當 o 是一個checkpoint()返回的對象時,將Lexer恢復為調用checkpoint()時的狀態。(用於回溯。)
subblock_lexer
() link返回一個Lexer對象,用於當前行相關聯的語句塊(block)。
advance
() link在一個子塊(subblock)Lexer中,前進到下一行。在第一行之前必須調用這個方法,這樣第一行才會被處理。
renpy_statement
() link調用該方法後,將當前代碼行當作Ren’Py腳本語句處理,如果處理失敗則生成一個錯誤。
該方法返回一個不透明對象。這種不透明對象也可以從get_next()方法返回,可以傳給 renpy.jump()
和 renpy.call()
函數處理。
除非這種不透明需要作為語句處理結果的一部分,一般不進行存儲。
包含該方法的語句執行完畢後,主控流程會切換為CDS語句之後的語句。(很可能是使用post_execute創建的語句。)
renpy_block
(empty=False) link該方法將當前語句塊中剩餘的代碼行都當作Ren’Py腳本處理,並返回一個SubParse對象,該對象相當於後續整個代碼塊的第一條語句。 代碼塊中所有語句將串聯起來並順序運行,然後主控流程切換到CDS之後的那條語句。 注意該方法只處理當前代碼塊。在很多情況下,我們還需要處理當前語句的子塊(subblock),正確的做法如下:
def mystatement_parse(l):
l.require(':')
l.expect_eol()
l.expect_block("mystatement")
child = l.subblock_lexer().renpy_block()
return { "child" : child }
pass
語句。)
若為False,空代碼塊將觸發報錯。catch_error
() link該方法是一個上下文裝飾器(context decorator),與 with 語句協同使用,捕獲和記錄lexer上下文語句塊內的報錯,然後繼續執行語句塊後面的內容。 這是一個樣例,使用該方法並在一個子塊(subblock)中記錄多個錯誤:
def mystatement_parse(l):
l.require(':')
l.expect_eol()
l.expect_block("mystatement")
strings = [ ]
ll = l.subblock_lexer()
while ll.advance():
with ll.catch_errors():
strings.append(ll.require(ll.string))
ll.expect_noblock("string inside mystatement")
ll.expect_eol()
return { "strings" : strings }
在編寫lint函數時,下列函數很有用。
檢查文本標籤 s 的正確性。如果存在錯誤則返回錯誤字串,沒有錯誤則返回None。
renpy.
error
(msg) link將字串 msg 作為錯誤訊息報給使用者。通常作為parse或lint錯誤記錄日誌,其他情況會拋出異常。
renpy.
try_compile
(where, expr, additional=None) link嘗試編譯一個表達式,如果失敗則將錯誤寫入lint.txt文件。
renpy.
try_eval
(where, expr, additional=None) link嘗試計算一個表達式,如果失敗則將錯誤寫入lint.txt文件。
這裡創建了一種新的語句“line”。“line”語句允許不帶引號的文本行。
python early:
def parse_smartline(lex):
who = lex.simple_expression()
what = lex.rest()
return (who, what)
def execute_smartline(o):
who, what = o
renpy.say(eval(who), what)
def lint_smartline(o):
who, what = o
try:
eval(who)
except:
renpy.error("Character not defined: %s" % who)
tte = renpy.check_text_tags(what)
if tte:
renpy.error(tte)
renpy.register_statement("line", parse=parse_smartline, execute=execute_smartline, lint=lint_smartline)
使用時這樣寫:
line e "這裡的引號不會顯示" 艾琳說, "也不需要反斜槓轉義符。"