编程语言基础 link
在我们详细介绍Ren’Py的编程语言之前,我们必须首先介绍一下Ren’Py脚本的结构。这包括:脚本文件如何分割为语句块(block),而语句块(block)是由多行脚本构成;各行脚本如何分割为基本元素(element),并构成语句(statement)。
文件 link
Ren’Py游戏脚本由 game/
目录下众多扩展名为 .rpy
的文件组成。Ren’Py会依次检查每一个文件(按照他们路径的Unicode码顺序),并把文件内容用作脚本。
总之,把一份脚本打散成多个文件,与一份脚本保存在一个大文件中,两种方法是等效的。主控流程可以在文件之间通过调用 脚本标签 跳转。把一份脚本切分为多个文件跟创作者个人风格有关——有些游戏制作者喜欢使用小文件(像一个事件一个文件,或者每一天一个文件),而其他制作者倾向于使用一个完整的大脚本文件。
为了提高加载速度,Ren’Py启动时会把 .rpy
文件编译为 .rpyc
文件。当一个 .rpy
文件发生变更时, .rpyc
只有重启Ren’Py进程才会更新。另外,如果一个 .rpyc
文件并没有对应的 .rpy
源文件,这个 .rpyc
文件依然会被使用。当 .rpy
文件已经删除而 .rpyc
文件没有删除的情况下,可能会导致运行时出现问题。
文件名必须以字母或者数字开头,并且开头不能用“00”,因为“00”开头的文件是Ren’Py内部使用的。
根目录 link
根目录是包含构建该游戏所有文件的目录(尽管有些文件并不参与游戏构建)。其他内容参见 构建发行版 。像README(须知)类文件应该放在根目录中。
创建新游戏时,根目录会被创建在“项目目录(Projects Directory)”中。“项目目录”可以在启动器中进行设置。
例如,如果项目目录名为 renpygames
,新建游戏名为“HelloWorld”,那么根目录将会是 renpygames/HelloWorld
。
game目录 link
game目录是在根目录下一个名为“game”的目录。例如,根目录是 renpygames/HelloWorld
,game目录就是 renpygames/HelloWorld/game
。
game目录包含了游戏中用到的所有文件。对于game目录包含的所有子目录:
Ren’Py会搜索所有 .rpy
和 .rpyc
文件,合并为游戏脚本;
Ren’Py会搜索 .rpa
归档文件,自动在游戏中使用。
当Ren’Py遇到指定路径的文件时,该路径都是game目录下的相对路径(但 config.searchpath
可以修改这个默认路径)。
注释(comment) link
Ren’Py脚本文件可能会包含一些注释(comment)。每条注释都以记号 (#
) 开头,直到该行文本结束。不过有一种情况例外,注释(comment)不能用在某个字符串的一部分。
# 这是一条注释。
show black # 这也是一条注释。
"# 这不是一条注释因为它是一个字符串的一部分。"
Ren’Py忽略注释, 所以脚本处理过程中那段注释如同不存在。
逻辑行(logical line) link
一个脚本文件可以被切割为一些逻辑行(logical line)。一条logical line(逻辑行)往往在文件中顶格起始,换行结束,有一些例外:
该行结尾是反斜杠 (
\
)。该行包含几个开括号字符(
(
、{
或[
),而该行没有匹配到对应的闭括号字符 ()
、}
或]
)。任何 字符串中出现换行,包括单引号中的字符串。这条规则与Python的规范不同。
一个逻辑行结束后,下一行就是另一个逻辑行的开始。
Ren’Py编程语言中大多数语句都只有一个逻辑行。
"这是一条逻辑行"
"因为这条逻辑行包含一个字符串,
所以换行也依然是同一条逻辑行。"
$ a = [ "由于括号的存在,这也是一条",
"可以突破换行的逻辑行。" ]
空的逻辑行会被忽略。
缩进和语句块(block) link
缩进 是我们指代Ren’Py语句每个逻辑行开头的空间。在Ren’Py中,缩进必须使用空格。
缩进被用来将一些语句分组形成语句块(block)。一个语句块是一组逻辑行,通常也是一组语句。将一个文件分割为语句块的原则是:
一个语句块在文件开头被打开加载。
在某个逻辑行使用了比上一行更多缩进量时,就表示开始了一个新语句块。
在一个语句块中所有的逻辑行必须保持同样的缩进量。
当一个非空逻辑行的缩进量比其他行更少,表示上一个语句块的结束。
对Ren’Py来说,缩进非常重要。错误的缩进量能引起语法或者逻辑错误。同时,缩进的使用表明了语句块结构,比使用其他标识的语言简单。
"这是一个语句,后面跟着的是if语句,那是一个语句块的一部分。"
if True:
"这个语句是新语句块的一部分。"
"这个语句也是新语句块的一部分。"
"这个语句又是第一个语句块的一部分了。"
语句元素 link
Ren’Py语句由一些基本部分组成。
- 关键词(keyword)
关键词是一个英文单词,必须在游戏脚本中合法出现。关键词通常用于出现在语句和属性中。
关键词主要用于引入语句(statement)和特性(property)。
- 名称(name)
名称以一个字母或者下划线开头,之后跟随着0个或者若干个字母、数字或者下划线。出于我们的需求,在“U+00a0”和“U+fffd”之间的unicode字符都被认为是字母。
Warning
以一个下划线 (_) 开头的名称都是Ren’Py内部预留的,除非文档另有说明。
以两个下划线 (__) 开头但不以两个下划线结尾的名称,会被转为那个名称的特定文件类型版本。
- 图像名(image name)
图像名(image name) 由一个或多个部分构成,以空格分隔。 图像名的第一部分称作 图像标签(image tag) 。图像名后面的部分都是 图像属性(image attributes) 。图像的各部分都是由字符、数字和下划线组成的字符串。
例如,一个图像名为
mary beach night happy
。图像标签(tag)就是mary
,而图像属性(attribute)就是beach
、night
和happy
。单词
at
、as
、behind
、onlayer
、with
和zorder
都不能用于图像名中。- 字符串(string)
字符串以一个引用字符(”、’或者`)开头,包含一些字符,并以同样的引用字符结尾。
反斜杠(\)用于字符转义,一些特殊字符,比如%(需要写作\%)、[(需要写作\[)、{(需要写作\{)。它还用于包含下一行,此时使用\n串。
在Ren’Py字符串中,连续多个空格会被压缩为一个空格字符,除非某个空格前面有一个反斜行。
'Strings can\'t contain their delimiter, unless you escape it.' "There will be a space between the two following words." "There will be a line break between\nthese." "And there will be three spaces between\ \ \ these."
可以使用前缀
r
,用法与Python语法规则一样。其他前缀则不能用,比如u
、b
和f
。 连续3个引号不能在普通字符串中使用,其实际用途请参考 独白模式 。- 简单表达式(simple expression)
简单表达式就是一个Python表达式,用于在Ren’Py脚本中运行Python。一个简单表达式使用以下类型作开头:
一个变量名。
一个字符串。
一个数字。
圆括号中包含的任意表达式。
其后可以接续任意数量的:
名称前的一个英文句号字符。
圆括号内的Python表达式。
举例,
3
、(3 + 4)
、foo.bar
和foo(42)
都是简单表达式。但3 + 4
则不是“简单”表达式,因为该表达式是一个算式字符串且没有使用圆括号。- 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)
整数是一个不带小数点的数字。
3
和42
就都是整数。- 浮点数(float)
浮点数是一个带小数点的数字。
.5
、7.
和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表达式中可以使用变量。通过 define语句 语句或者 default语句 语句定义变量,数值可以存放在变量中。 变量名的规则与 语句元素 中的 name 相同。例如:
playername 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")
我们调用了
Character()
函数。其给定了一个固定位置参数,也就是字符串”Eileen”。其给定了两个关键词参数:type
被赋值为adv
,而color
被复制为字符串"#0f0"
。除了函数之外,其他可以调用的对象都统称为 callables 。
阅读此份文档时,你可能会看到这样的函数声明:
- Sample(name, delay, position=(0, 0), **properties) link
这个样例函数并不真正在Ren’Py中使用,而只存在这份文档中。
这个函数:
函数名为“Sample”
有两个固定位置参数,分别是name和delay。真实情况下,在文档中应该有参数的详细说明。
有一个关键词参数position,其默认值为(0, 0)。
由于函数结尾是 **properties
,这意味着其可以使用 样式特性 作为额外的关键词参数。 其他的特殊形式结尾还有 *args
,表示其可以使用任意数量的固定位置参数,而 **kwargs
表示在文档中已详细描述过的关键词参数。
若在函数签名中出现了 /
符号,表示该符号 之前 的参数都是固定位置参数,不可以用关键词参数。
若在函数签名中出现了 *
符号,表示该符号 之后 的参数都是关键词参数,只有使用 name=value
语法传参。
Python的强大,远非我们这份文档所能完全展现。若希望学习python的更多细节,我们推荐Python入门教学, python.org 。由于我们认为对于Ren’Py来说,更深一层的python知识不是必要的,了解python语句和表达式通常就足够了。