RenPy中文空间

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 59|回复: 0

层叠式图像(新)

[复制链接]
发表于 2018-5-4 12:30:06 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
本帖最后由 BuErShen 于 2018-5-4 12:11 编辑

层叠式图像(翻译:被诅咒的章鱼)

当某个精灵(sprite)集的复杂度到达某个等级后,分别定义出每种可能的组合就会变得非常不便。 例如,某个精灵有4套服装、4种发型和6种表情,就总共有96种组合方式。 针对每种可能的组合创建静态图像会占用很多磁盘空间和编程时间。

为了应对这种使用场景,Ren’Py提供了一种办法,可以定义多个图层(layer)组成的图像。 (此处的图层跟Ren’Py中其他地方的“图层”不太一样,更类似于Photoshop或者GIMP等绘图程序中图层的概念。) 图层可以无条件显示,也可以通过图像属性(attribute)或者实时计算的条件表达式选择是否显示。

为了让定义层叠式图像更加方便,Ren’Py提供了layeredimage语句。以layeredimage开头的语句表示,允许传作者使用特定域的语言定义一个层叠式图像。 还有 LayeredImage() 对象,虽然它不是一般的图像(image)对象,但可以使用image语句声明,也可以和一般图像一样使用。

定义层叠式图像
层叠式图像的特定域脚本语言由几种语句组成。其中一种语句就是一个引入图像的普通脚本语言语句,跟在它后面的语句则引入图层和图层组。

为了介绍这种特殊的语言,这里的例子是一个使用了多种功能特性(feature)的层叠式图像,所有的隐式项全都使用显式标明。
[RenPy] 纯文本查看 复制代码
layeredimage augustina:

    always:
        "augustina_base"

    group outfit:

        attribute dress:
            "augustina_outfit_dress"

        attribute jeans:
            "augustina_outfit_jeans"

    group eyes:

        attribute open default:
            "augustina_eyes_open"
            default True

        attribute wink:
            "augustina_eyes_wink"

    group eyebrows:

        attribute normal default:
            "augustina_eyebrows_normal"

        attribute oneup:
            "augustina_eyebrows_oneup"

    group mouth:

        pos (100, 100)

        attribute smile default:
            "augustina_mouth_smile"

        attribute happy:
            "augustina_mouth_happy"

    if evil:
        "augustina_glasses_evil"
    else:
        "augustina_glasses"

这段脚本有点长,不过它很规整易读。下面我们将展示如何简化这段脚本。

首先,layeredimage 语句以精灵(sprite)的名字开头。这条语句属于 Ren’Py脚本语言,运行在初始化阶段。

层叠式图像的语句块(block)可能包含 always、group和 if 语句。group语句可以包含属性(attribute)。always 和 if 语句必须带可视组件(displayable),而同个组内的attribute语句可以任何选择带一个。 以上所有语句都可以带特性(property)。

always 语句定义的图层(layer)总是显示,就像是 精灵(sprite)的背景。

group 语句引入一组属性(attribute),同一时间内只显示某一项属性(attribute)。 所以层叠式图像其实只显示角色的一套服装、一对眼睛、一对眉毛和一张嘴。指定的属性组特性(property)会传入对应的 attribute,并让属性组自动定义各个属性。

attribute 语句引入的图层,是仅在图像需要对应属性(attribute)时才会显示。例如,“augustina_outfit_dress”仅在出现“dress”属性时才会显示。 如果使用了关键词default,在没有其他有冲突的属性出现时,就会默认显示的属性;在这个样例中,只要不使用“wink”属性,就显示“augustina_eyes_open”属性的图像。

最后,if 语句添加了一个图层,该图层使用一条Python语句在不同的可视组件间选择显示的对象。Python语句总是重新计算,并显示第一个满足条件表达式为 True 的图像。

特性(property)由特性名称和一个简单表达式构成,可以传给每一个图层。一些特性(property)会改变某些语句的功能。 如果含有某一个或多个 变换特性 ,会创建一个 Transform() 对象并 wrap 出显示结果。 at 后面的某个变换(transform)或者变换列表也会让可视组件产生变化。例如,这里的pos特性(property)创建了一个变换(transform),移动角色“mouth”图像的位置。

最终图像的尺寸等于能包围所有图层的方框尺寸。所以最好直接让某一个图层就是最终图像的完整尺寸,这个尺寸能包含所有的图层。 第一个图层在最终图像最后面,而最后的图层在最终图像最前面——在这个样例中,glass图层覆盖在所有其他图层上。

group 和 attribute 语句在某个层叠式图像中可以出现多次,所有指定属性(attribute)的图像都会显示。

使用层叠式图像
要使用这个层叠式图像,而不是其他层叠式图像,变量evil必须给定一个值,比如:
[RenPy] 纯文本查看 复制代码
default evil = True

这样,层叠式图像就可以像其他图像(image)一样显示了。几乎可以肯定,至少需要给定角色的一套服装——虽然Ren’Py不强制要求,图像还是需要显示一套的:
[RenPy] 纯文本查看 复制代码
show augustina jeans

当精灵(sprite)显示时,额外属性(attribute)的要素只要不冲突就都会添加到图像上。 (使用Ren’Py时广泛存在的,匹配不到已定义图像的情况,基本不会发生在层叠式图像上。) 所以,
[RenPy] 纯文本查看 复制代码
show augustina wink

会激活与wink属性(attribute)关联的图层。我们可以可以关闭wink属性,使用:
[RenPy] 纯文本查看 复制代码
show augustina open

因为open状态的眼睛与wink状态的眼睛冲突。我们还可以直接移除wink属性,使用:
[RenPy] 纯文本查看 复制代码
show augustina -wink

这样也会显示open属性,因为它是默认项。

层叠式图像还可以使用在scene语句中。

自动化属性
我们的第一个样例中有不少重复,很多属性(attribute)的名称已经在可视组件中定义过。 为了帮助创作者节约冗余的输入时间,Ren’Py可以根据图像名称、组名称和属性名称自动决定可视组件的名称。 使用下划线连接上述名称就能实现这一点。

这样做的时候,创作者还可以利用属性(attribute)的另一项功能特性——第一行可以添加任意特性(property),并省略整个语句块(block)。

之前的样例可以这样写:
[RenPy] 纯文本查看 复制代码
layeredimage augustina:

    always:
        "augustina_base"

    group outfit:
        attribute dress
        attribute jeans

    group eyes:
        attribute open default
        attribute wink

    group eyebrows:
        attribute normal default
        attribute oneup

    group mouth:
        pos (100, 100)
        attribute smile default
        attribute happy

    if evil:
        "augustina_glasses_evil"
    else:
        "augustina_glasses"

这个样例跟之前那个是等价的(前提是使用的是相同名称的可视组件)。例如,在outfit组中的dress属性使用名为“augustina_outfit_dress”的可视组件。

还可以更进一步,让一个组内自动定义属性(attribute)。在定义组时使用关键词auto,就能让这个组自动搜索正则表达式匹配到的图像,并在属性不存在的情况下自动定义组内图像属性。

与属性(attribute)一样,特性(property)也可以放在组的第一行并省略语句块。always语句中的可视组件和特性(property)也可以采用同样的方式。

样例的最终格式如下:
[RenPy] 纯文本查看 复制代码
layeredimage augustina:

    always "augustina_base"

    group outfit auto

    group eyes auto:
        attribute open default

    group eyebrows auto:
        attribute normal default

    group mouth auto:
        pos (100, 100)
        attribute smile default

    if evil:
        "augustina_glasses_evil"
    else:
        "augustina_glasses"

这是定义同样的图像时,最精简的方法。当每个组中需要添加更多属性(attribute)时,自动定义功能节省的时间更多。 如果我们不需要默认属性,还可以减少几行脚本。那样,每个组都只需要一行。

语句参考
需要注意,当层叠式图像首次定义时,if语句中的所有条件表达式都在初始化阶段就会被计算。

Layeredimage语句

layeredimage 语句在Ren’Py用作某个层叠式图像定义的开头语句。layeredimage语句开始处是图像名称,后面的语句块内包含attribute、group和if语句。

层叠式图像使用下列特性(property):

image_format
    如果给定的图像是一个字符串,并且提供了image_format特性,就将 image_format 插入到图像名,根据得到的名称找对应的图片文件。 例如,“sprites/eileen/{image}.png”会在sprites子目录下搜索所有png图片文件。 (auto组不使用image_format特性,因为auto组自动搜索图像(image)而不是图片文件。)

format_function
    这是一个函数,用于代替 layeredimage.format_function 函数,将图像信息格式化并传入某个可视组件。

transform properties
    如果存在变换特性,都会用于构建一个应用于可视组件的 Transform()

at
    应用于层叠式图像的一个变换(transform)或变换的列表。

Attribute语句
attribute 语句添加了一个图层(layer),当使用给定的属性(attribute)时显示对应的图像(image)。同一个属性可以用在多个图层中,并响应这个属性一齐显示(if_also和if_not特性可以更改这点)。

attribute语句使用一个属性(attribute)名称。其可以使用两个关键词。 default 关键词表示,在没有同组冲突属性出现的情况下作为默认的属性。 null 关键词防止Ren’Py自动搜索对应属性的可视组件,对某些有使用条件 if_all 、 if_any 或 if_not 的属性时很有用。

如果没有直接给出可视组件(displayable),Ren’Py会将图层(layer)、组(group)、组变种(group variant)和属性(attribute)用下划线连接,算出一个可视组件的名称。 所以如果我们有一个名为“augustina”的图像,组名“eyes”,属性名“closed”,那么最终使用的图像名为“augustina_eyes_closed”。 (层叠式图像的格式化函数就负责处理这个工作,默认的格式化函数是 layeredimage.format_function() 。)

如果某个属性(attribute)不在某个组(group)里,就会使用相同的属性名放入那个组中,但那个组并不会用于计算可视组件的名称。(Ren’Py会搜索“image_attribute”,而不是“image_attribute_attribute”。)

attribute语句使用下列特性(property):

if_all
    属性(attribute)名称的字符串或字符串列表。如果出现了这项特性,只有所有特定的属性都出现时,才显示图层(layer)。

if_any
    属性(attribute)名称的字符串或字符串列表。如果出现了这项特性,只要有任意特定的属性出现时,就显示图层(layer)。

if_not
    属性(attribute)名称的字符串或字符串列表。如果出现了这项特性,只有所有特定的属性都不出现时,才显示图层(layer)。

transform properties
    如果存在变换特性,都会用于构建一个应用于可视组件的 Transform() 。

at
    应用于层叠式图像的一个变换(transform)或变换的列表。

Group语句
group 语句将一些转换后的图层(layer)组成一个组。一个组(group)中不能包含不同的属性(attribute)。(不过一个组中可以包含同样的属性两次。)

group语句使用一个名称(name)。该名称并不常用,但可以用于生成组内属性的默认名称。

这个名称后面可能跟着关键词 auto 。如果在组内的任意属性后面的确存在auto,Ren’Py会扫描自己的图像列表以匹配组的正则表达式(详见下面内容)。找到的所有图像,如果匹配不到已定义的属性,就会自动在组内添加属性,就像使用attribute语句定义属性一样。

特性(property)可以定义在组的第一行,后面带一个语句块,包含特性(property)和属性(attribute)。

有两个特性是专门用于组的:

variant
    这应该是一个字符串。如果存在这项特性,它会添加一个变种元素。这个变种元素最终会成为自动生成图像名的一部分,以及搜索自动定义属性的正则表达式的一部分。

prefix
    给定的prefix前缀会加根下划线,并添加到手动或自动定义的属性名称前面。如果 prefix 为“leftarm”,遇到的属性名为“hip”,定义的最终属性名就是“leftarm_hip”。

group语句使用的特性(property)与 attribute 语句相同。应用于组(group)的特性会传给组内的属性(attribute),除非某项属性自身重写了同名的属性。

正则表达式 。使用的图像正则表达式由下列部分构成:

  • 图像名称,空格使用下划线替换。
  • 组(group)名称。
  • 变种(variant)名称。
  • 属性(attribute)名称。

全部使用下划线组成。例如,我们有一个名为“augustina work”的图层图像,名为“eyes”的组,就会根据正则表达式augustina_work_eyes_attribute 匹配图像。 如果带一个 blue 的 variant ,就会根据正则表达式augustina_work_eyes_blue_attribute 进行匹配。

Always语句
always 语句定义一个保持显示的图层。always语句必须提供一个可视组件,当然也可以使用特性(property)。 这两部分可以放在同一行,也可以放在一个语句块(block)中。

always语句使用下列特性:

if_all
    属性(attribute)名称的字符串或字符串列表。如果出现了这项特性,只有所有特定的属性都出现时,才显示图层(layer)。

if_any
    属性(attribute)名称的字符串或字符串列表。如果出现了这项特性,只要有任意特定的属性出现时,就显示图层(layer)。

if_not
    属性(attribute)名称的字符串或字符串列表。如果出现了这项特性,只有所有特定的属性都不出现时,才显示图层(layer)。

transform properties
    如果存在变换特性,都会用于构建一个应用于图层的 Transform() 。

at
    应用于图层的一个变换(transform)或变换的列表。

If语句
if 语句(或者更完整的if-elif-else语句)允许创作者设置一个或多个条件表达式。这些条件表达式会运行时进行计算。 每个条件表达式与某个图层(layer)关联,第一个结果为True的条件表达式对应的图像会被显示。如果没有条件表达式为True,else语句对应的图像就会显示。

一个稍微复杂的if语句样例如下:
[RenPy] 纯文本查看 复制代码
if glasses == "evil":
    "augustina_glasses_evil"
elif glasses == "normal":
    "augustina_glasses"
else:
    "augustina_nose_mark"

每个图层必须给定一个可视组件。if语句还可以使用下列特性(property):

if_all
    属性(attribute)名称的字符串或字符串列表。如果出现了这项特性,条件表达式检查是否所有的命名属性(attribute)都出现了。

if_any
    属性(attribute)名称的字符串或字符串列表。如果出现了这项特性,条件表达式检查是否任意的命名属性(attribute)出现了。

if_not
    属性(attribute)名称的字符串或字符串列表。如果出现了这项特性,条件表达式检查是否所有的命名属性(attribute)都未出现。

transform properties
    如果存在变换特性,都会用于构建一个应用于图层的 Transform() 。

at
    应用于图层的一个变换(transform)或变换的列表。

当 layeredimage 语句运行时,if语句就会转换为 ConditionSwitch() 。

predict_all不为True时,应该避免修改if语句的条件表达式。因为层叠式图像要么显示要么即将显示,修改if语句条件表达式会导致没有预加载的图像就被使用。 这种设计主要用于很少变化的角色自定义选项。

姿势
一个角色对应的精灵(sprite)可能有多个姿势,不同姿势的所有内容——至少有趣的所有内容——都是不同的。 例如,如果某个角色有站立和坐着两种姿势,所有的图像部件就都在不同的位置。

在那种情况下,可以根据同一个图像标签(tag)定义多个层叠式图像。 layeredimage 语句可以允许创作者包含属性(attribute)作为图像名称的一部分。所以我们可以这样:
[RenPy] 纯文本查看 复制代码
layeredimage augustina sitting:
    ...

layeredimage augustina standing:
    ...

使用层叠式图像合成一个对话框头像(side image)特别好用。不同角色的对话框头像不会与其他角色的有任何关系。
[RenPy] 纯文本查看 复制代码
layeredimage side eileen:
    ...

layeredimage side lucy:
    ...

几个建议

在图像名称中使用下划线。
默认情况下,Ren’Py中的层叠式图像使用下划线作为图像名各段的分隔符。 可以在图像中临时使用空格,不过后面很可能会导致问题和故障。

Ren’Py的一条规则是,如果创作者想要显示一个图像,那个图像有一个同名图像正在显示,那么就显示那个同名图像。 这个规则也贯彻在层叠式图像中。创作者可以直接定义并显示图层,不过也会导致奇怪的问题,比如一双眼睛悬浮在空中。

每个图像使用的图像标签(tag)都与主图像不同的话,就不存在这个问题了。

不需要剪裁图层。 Ren’Py读取图像并加载到 RAM之前会进行优化,将所有图像剪裁到非透明像素的包围框(bounding box)。 因此,在图像被正确预加载的前提下,创作者剪裁图像并不会提升性能或减少图像尺寸。

Python
当然, layeredimage 语句有一个Python等效语句。group语句则没有——group可以传入 Attribute() 和auto功能可以使用 renpy.list_images() 来实现。

Attribute(group, attribute, image=None, default=False, **kwargs)
    这个函数用于由某个属性(attribute)控制展现层叠式图像中的某个图层(layer)。单个的属性可以控制多个图层,在这种情况下那个属性的图层会同时响应并显示。

    group
        一个字符串,表示属性控制的组名称。可以是 None,表示由属性名创建同名的组。

    attribute
        一个字符串,表示属性的名称。

    image
        如果不是 None,这项应该是受属性控制显示的可视组件。

    default
        如果是 True,并且组内其他属性没有设置为默认属性,就使用 attribute 作为默认属性。

    还有下面几个关键词入参:

    at
        应用于图像的一个变换(transform)或者变换列表。

    if_all
        一项属性(attribute)或属性列表。只有所有属性都显示时,对应的可视组件才会显示。

    if_any
        一项属性(attribute)或属性列表。只要不是空列表,任意属性显示时,对应的可视组件都会显示。

    if_not
        一项属性(attribute)或属性列表。所有属性都不显示的情况下,可视组件才显示。

    其他关键词入参都可以集成为变换(transform)的特性(property)。如果出现了这样的关键词入参,就会创建一个变换用于warp图像。 (例如,pos=(100, 200)可以用于让图像在水平方向偏移100像素、在垂直方向偏移200像素。)

    如果 image 参数省略或者为None,并且 LayeredImage() 传入了 image_format 参数,image_format就用于生成图像文件名。

Condition(condition, image, **kwargs)
    这个函数用于由某个条件表达式控制展现层叠式图像中的某个图层(layer)。当条件表达式为True时,显示图层。否则不显示。

    condition
        这是一个字符串,表示Python条件表达式,决定是否显示图层。

    image
        若不是None,这是一个可视组件,条件表达式condition为True时显示。

    if_all
        一项属性或属性列表。只有所有属性都显示时,才计算条件表达式的值。

    if_any
        一项属性或属性列表。只要列表不是空列表,任意属性显示时都计算条件表达式的值。

    if_not
        一项属性或属性列表。只有所有属性都不显示时,才计算条件表达式的值。

    at
        应用于图像的一个变换(transform)或者变换列表。

    其他关键词入参都可以集成为变换(transform)的特性(property)。如果出现了这样的关键词入参,就会创建一个变换用于warp图像。 (例如,pos=(100, 200)可以用于让图像在水平方向偏移100像素、在垂直方向偏移200像素。)

LayeredImage(attributes, at=[], name=None, image_format=None, format_function=None, attribute_function=None, **kwargs)
    这是一个类图像对象,如果显示某个合适的属性(attribute)的集合,使用集合中那些属性对应的可视组件合成一个可视组件并显示。

    attribute
        这必须是一个Attribute对象列表。每个Attribute对象影响一个可视组件作为最终图像的一部分是否显示。列表中的元素是从后往前顺序排列,第一个元素距离观察者(viewer)最远,最后一个元素距离最近。

    at
        一个变换(transform)或变换列表,应用于可视组件。

    name
        属性名称。这项用作图像组成名称的一部分。

    image_format
        如果给定的图像是一个字符串,并且出现了image_format,就在图像名中插入image_format,用作图片文件。例如,“sprites/eileen/{image}.png”会搜索sprites子目录下的图像。 (这项不用在auto组中,auto组只搜索图像而不搜索图片文件。)

    format_function
        一个函数,用于代替 layeredimage.format_function 函数,将图像信息格式化并传入某个可视组件。

    attribute_function
        如果不是 None,这个函数使用应用于图像的属性(attribute)集作为参数,并返回选择的图层所使用的属性集。 在属性自身被选中后,决定显示图层时,调用这个函数。它可以用于表示属性或随机选择的属性间复杂的依赖关系。

    额外的关键词入参会传入一个固定布局(Fixed),这个固定布局用于防止图层。除非显示重写,固定布局的xfit和yfit都设置为True,表示所有图层图像显示时固定布局会收缩为最小尺寸。

    层叠式图像 不是 可视组件(displayable),能使用的范围比可视组件小。这是因为很多地方需要提供一个图像名(通常包含image属性)。 比如,层叠式图像可以使用 scene 和 show 语句显示,也可以通过图像名字符串当作一个可视组件使用。

layeredimage.format_function 函数用作将属性(attribute)和可视组件格式化为图片文件。创作者可以查看这个函数的结构和使用的入参,在需要的情况下可以使用自己的 format_function 函数替换它。

layeredimage.format_function(what, name, group, variant, attribute, image, image_format, **kwargs)
    调用这个函数可以将属性(attribute)或条件表达式的信息格式化并传入可视组件中。创作者可以用自定义函数替换这个函数,不过新的函数会忽略未知的关键词入参。

    what
        一个字符串,表示格式化内容的描述信息,常用于创建更详尽的错误信息。

    name
        图像属性(attribute)名称。

    group
        属性的组(group)名,如果不支持组或者其是条件表达式的一部分则为None。

    variant
        组(group)内的variant入参,如果没有则为None。

    attribute
        属性(attribute)本身。

    image
        一个可视组件或者字符串。

    image_format
        The image_format argument of the LayeredImage.

    如果 image 的值是None,那么就用下划线连接 name 、 group (如果非None)、 variant (如果非None)和 attribute ,组合并创建出 image 。这个创建的 image 是一个字符串。

    如果 image 是一个字符串,并且 image_format 不是None, image 会被函数格式化,并得到最终使用的可视组件。

    所以,如果 name 是“eileen”, group 是“expression”, attribute 是“happy”, image 就被设置为“eileen_expression_happy”。 如果 image_format 是“mages/{image}.png”,Ren’Py找到的最终图像就是“images/eileen_expression_happy.png”。 但是注意,Ren’Py还会找到不带 format入参的同名图像。



您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|RenPy中文空间 ( 苏ICP备17067825号 | 苏公网安备 32092302000068号 )

GMT+8, 2018-8-21 06:30 , Processed in 0.028437 second(s), 20 queries , File On.

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表