層疊式圖像 link

當某個精靈(sprite)集的複雜度到達某個等級後,分別定義出每種可能的組合就會變得非常不便。 例如,某個精靈有4套服裝、4種髮型和6種表情,就總共有96種組合方式。 針對每種可能的組合創建靜態圖像會占用很多磁碟空間和編程時間。

為了應對這種使用場景,Ren’Py提供了一種辦法,可以定義多個圖層(layer)組成的圖像。 (此處的圖層跟Ren’Py中其他地方的“圖層”不太一樣,更類似於Photoshop或者GIMP等繪圖程序中圖層的概念。) 圖層可以無條件顯示,也可以通過圖像屬性(attribute)或者即時計算的條件表達式選擇是否顯示。

為了讓定義層疊式圖像更加方便,Ren’Py提供了 layeredimage 語句。以layeredimage開頭的語句表示,允許傳作者使用特定域的語言定義一個層疊式圖像。 還有 LayeredImage() 對象,雖然它不是一般的圖像(image)對象,但可以使用image語句聲明,也可以和一般圖像一樣使用。

定義層疊式圖像 link

層疊式圖像的特定域腳本語言由幾種語句組成。其中一種語句就是一個引入圖像的普通腳本語言語句,跟在它後面的語句則引入圖層和圖層組。

為了介紹這種特殊的語言,這裡的例子是一個使用了多種功能特性(feature)的層疊式圖像,所有的隱式項全都使用顯式標明。

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)。 alwaysif 語句必須帶可視組件(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”圖像的位置。

最終圖像的尺寸等於能包圍所有圖層的方框尺寸。所以最好直接讓某一個圖層就是最終圖像的完整尺寸,這個尺寸能包含所有的圖層。 第一個圖層在最終圖像最後面,而最後的圖層在最終圖像最前面——在這個樣例中,眼鏡的圖層覆蓋在所有其他圖層上。 建議避免在包含的圖像中使用變換特性(property),比如 xcenterxalign, 因為圖像尺寸未知的情況下那些特性可能會出問題。

group和attribute語句在某個層疊式圖像中可以出現多次,所有指定屬性(attribute)的圖像都會顯示。

if 語句是個例外,所有Python表達死都會在初始化階段進行計算。

使用層疊式圖像 link

要使用這個層疊式圖像,而不是其他層疊式圖像,變數evil必須給定一個值,比如::

default evil = True

這樣,層疊式圖像就可以像其他圖像(image)一樣顯示了。幾乎可以肯定,至少需要給定角色的一套服裝——雖然Ren’Py不強制要求,圖像還是需要顯示一套的::

show augustina jeans

當精靈(sprite)顯示時,額外屬性(attribute)的要素只要不衝突就都會添加到圖像上。 (使用Ren’Py時廣泛存在的,匹配不到已定義圖像的情況,基本上不會發生在層疊式圖像上。) 所以,

show augustina wink

會啟動與wink屬性(attribute)關聯的圖層。我們可以可以關閉wink屬性,使用:

show augustina open

因為open狀態的眼睛與wink狀態的眼睛衝突。我們還可以直接移除wink屬性,使用:

show augustina -wink

這樣也會顯示open屬性,因為它是默認項。

層疊式圖像還可以使用在scene語句中。

自動化屬性 link

我們的第一個樣例中有不少重複,很多屬性(attribute)的名稱已經在可視組件中定義過。 為了幫助創作者節約冗餘的輸入時間,Ren’Py可以根據圖像名稱、組名稱和屬性名稱自動決定可視組件的名稱。 使用下劃線連接上述名稱就能實現這一點。

這樣做的時候,創作者還可以利用屬性(attribute)的另一項功能特性——第一行可以添加任意特性(property),並省略整個語句塊(block)。

之前的樣例可以這樣寫:

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)也可以採用同樣的方式。

樣例的最終格式如下:

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)時,自動定義功能節省的時間更多。 如果我們不需要默認屬性,還可以減少幾行腳本。那樣,每個組都只需要一行。

alwaysif 語句中不能省略可視組件的名稱,所以在這些地方使用的圖像名稱需要儘可能簡短。合理的圖片命名可以很輕鬆地定義出成千上萬種圖層的組合方式。

語句特點 link

需要注意,當層疊式圖像首次定義時, if 語句中的所有條件表達式都在初始化階段就會被計算。

layeredimage語句 link

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語句 link

attribute 語句添加了一個圖層(layer),當使用給定的屬性(attribute)時顯示對應的圖像(image)。同一個屬性可以用在多個圖層中,並響應這個屬性一齊顯示(if_also和if_not特性可以更改這點)。

attribute語句使用一個屬性(attribute)名稱。其可以使用兩個關鍵字。 default 關鍵字表示,在沒有同組衝突屬性出現的情況下作為預設的屬性。 null 關鍵字防止Ren’Py自動搜索對應屬性的可視組件,對某些有使用條件 if_allif_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語句 link

group 語句將一些轉換後的圖層(layer)組成一個組。一個組(group)中不能包含不同的屬性(attribute)。 (不過一個組中可以包含同樣的屬性兩次。使用關鍵字 multiple 能解除這個限制。)

group 語句使用一個名稱(name)。該名稱並不常用,但可以用於生成組內屬性的默認名稱。

這個名稱後面可能跟著關鍵字 auto 。如果在組內的任意屬性後面的確存在auto,Ren’Py會掃描自己的圖像列表以匹配組的正則表達式(詳見下面內容)。找到的所有圖像,如果匹配不到已定義的屬性,就會自動在組內添加屬性,就像使用attribute語句定義屬性一樣。

後面還可以跟關鍵字 multiple 。出現時,可以同時選中某個組的多個成員。這個功能可以用於某個自動定義多個屬性的組,而各個屬性是很多圖像公用的。但是與關鍵字 default 定義的屬性會有衝突。

特性(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` 匹配圖像。 如果帶一個 bluevariant ,就會根據正則表達式 augustina_work_eyes_blue_`attribute` 進行匹配。

Always語句 link

always 語句定義一個保持顯示的圖層。always語句必須提供一個可視組件,當然也可以使用特性(property)。 這兩部分可以放在同一行,也可以放在一個語句塊(block)中。

always語句使用下列特性:

if_all
屬性(attribute)名稱的字串或字串列表。如果出現了這項特性,只有所有特定的屬性都出現時,才顯示圖層(layer)。
if_any
屬性(attribute)名稱的字串或字串列表。如果出現了這項特性,只要有任意特定的屬性出現時,就顯示圖層(layer)。
if_not
屬性(attribute)名稱的字串或字串列表。如果出現了這項特性,只有所有特定的屬性都不出現時,才顯示圖層(layer)。
transform properties
如果存在變換特性,都會用於構建一個應用於圖層的 Transform()
at
應用於圖層的一個變換(transform)或變換的列表。

If語句 link

if 語句(或者更完整的if-elif-else語句)允許創作者設置一個或多個條件表達式。這些條件表達式會運行時進行計算。 每個條件表達式與某個圖層(layer)關聯,第一個結果為True的條件表達式對應的圖像會被顯示。如果沒有條件表達式為True,else語句對應的圖像就會顯示。

一個稍微複雜的 if 語句樣例如下:

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語句條件表達式會導致沒有預載入的圖像就被使用。 這種設計主要用於很少變化的角色自訂選項。

姿勢 link

一個角色對應的精靈(sprite)可能有多個姿勢,不同姿勢的所有內容——至少有趣的所有內容——都是不同的。 例如,如果某個角色有站立和坐著兩種姿勢,所有的圖像部件就都在不同的位置。

在那種情況下,可以根據同一個圖像標籤(tag)定義多個層疊式圖像。 layeredimage 語句可以允許創作者包含屬性(attribute)作為圖像名稱的一部分。所以我們可以這樣:

layeredimage augustina sitting:
    ...

layeredimage augustina standing:
    ...

使用層疊式圖像合成一個對話框頭像(side image)特別好用。不同角色的對話框頭像不會與其他角色的有任何關係。

layeredimage side eileen:
    ...

layeredimage side lucy:
    ...

幾個建議 link

在圖像名稱中使用下劃線。 默認情況下,Ren’Py中的層疊式圖像使用下劃線作為圖像名各段的分隔符。 可以在圖像中臨時使用空格,不過後面很可能會導致問題和故障。

Ren’Py的一條規則是,如果創作者想要顯示一個圖像,那個圖像有一個同名圖像正在顯示,那麼就顯示那個同名圖像。 這個規則也貫徹在層疊式圖像中。創作者可以直接定義並顯示圖層,不過也會導致奇怪的問題,比如一雙眼睛懸浮在空中。

每個圖像使用的圖像標籤(tag)都與主圖像不同的話,就不存在這個問題了。

不需要剪裁圖層。 Ren’Py讀取圖像並載入到RAM之前會進行最佳化,將所有圖像剪裁到非透明像素的包圍框(bounding box)。 因此,在圖像被正確預載入的前提下,創作者剪裁圖像並不會提升性能或減少圖像尺寸。

Python link

當然, layeredimage 語句有一個Python等效語句。group語句則沒有——group應用 attribute 的值,而auto功能可以通過 renpy.list_images() 來實現。

Attribute(group, attribute, image=None, default=False, **kwargs) link

這個函數用於由某個屬性(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) link

這個函數用於由某個條件表達式控制展現層疊式圖像中的某個圖層(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) link

這是一個類圖像對象,如果顯示某個合適的屬性(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) link

調用這個函數可以將屬性(attribute)或條件表達式的訊息格式化並傳入可視組件中。創作者可以用自訂函數替換這個函數,不過新的函數會忽略未知的關鍵字入參。

what
一個字串,表示格式化內容的描述訊息,常用於創建更詳盡的錯誤訊息。
name
圖像屬性(attribute)名稱。
group
屬性的組(group)名,如果不支持組或者其是條件表達式的一部分則為None。
variant
組(group)內的variant入參,如果沒有則為None。
attribute
屬性(attribute)本身。
image
一個可視組件或者字串。
image_format
LayeredImage函數的image_format入參。

如果 image 的值是None,那麼就用下劃線連接 namegroup (如果非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入參的同名圖像。

層疊式圖像生成代理對象 link

有時候,為了在多個地方使用同一個層疊式圖像,需要給層疊式圖像生成一個代理對象(proxy)。這樣設計的原因之一是,各處可能使用同一個精靈(sprite)的不同圖像尺寸;另一個原因則是,可以使用層疊式圖像作為對話框頭像(side image)。

LayeredImagePorxy() 對象實現了這個功能,為層疊式圖像創建出可以在各處使用的副本。

舉例:

image dupe = LayeredImageProxy("augustina")

這行腳本創建了一個可以獨立顯示的圖像副本。這個副本能搭配上某個變換(transform)入參,並用於設定對話框頭像(side image)的位置,像這樣:

image side augustina = LayeredImageProxy("augustina", Transform(crop=(0, 0, 362, 362), xoffset=-80))
LayeredImageProxy(name, transform=None) link

這是一個類圖像對象。可以將某個層疊式圖像的屬性(attribute)傳給另一個層疊式圖像。

name
一個字串,表示需要生成代理對象的層疊式圖像名。
transform
若給定了這個入參,表示生成代理對象後,會應用於圖像上的某個變換(transform)或變換列表。