界面語言最佳化 link

Ren’Py有一些技巧能最佳化界面語言的速度。當使用Ren’Py創建複雜的UI時,例如模擬遊戲使用的介面,理解界面語言的工作機制有助於創作者實現最佳的畫面表現。

這篇指導適用於界面語言的第二種實現方式,其是在Ren’Py的6.18版本中加入的。如果你的項目使用Ren’Py6.17或者更早的版本創建,最好在啟動器(launcher)選擇“強制重新編譯”,確保界面工具升級到最新版本。

本指導並不能替代良好養成編程習慣的練習。如果某個界面使用循環嵌套實現一大堆無效的工作,就會比不需要這種循環的界面運行得慢。由於理解本指導中的技巧十分重要,完全避免這種辣雞行為總是比讓Ren’Py最佳化你的項目代碼更好。

參數列表 link

為了實現最好的畫面效果,所有界面都應該定義一個參數列表。如果某個界面不使用參數,應該使用一個空的參數列表。這個界面:

screen test():
    vbox:
        for i in range(10):
            text "[i]"

比下面那個運行得更快:

screen test:
    vbox:
        for i in range(10):
            text "[i]"

當不使用參數列表定義一個界面時,界面使用的任何名稱都可以在界面顯示時被重定義。這要求Ren’Py分析界面時更加保守,限制最佳化級別。

預載入 link

提前預載入界面可以提升表現效果。因為Ren’Py在預載入時間內依然會執行界面,並載入界面使用的圖像。

總共有兩種Ren’Py自動預載入界面的方法:

  • Ren’Py會預載入通過 show screencall screen 語句顯示的界面。

  • Ren’Py會預載入通過 Show()ShowMenu() 行為顯示的界面。

如果界面直接通過Python語句顯示,最好在顯示之前就開始預載入界面。開始預載入某個界面可以使用 renpy.start_predict_screen() 函數。停止預載入某個界面可以使用 renpy.stop_predict_screen() 函數。

可視組件的復用 link

當編譯解釋界面語言中創建某個可視組件時,Ren’Py會檢查固定位置參數和特性(property)是否與最近編譯解釋過的語句中有一樣的。如果存在同樣的可視組件,Ren’Py就不會創建新的,而是復用原來的。

可視組件的復用具有一系列性能表現方面的意義。它節省了創建新可視組件的消耗,很明顯新創建可視組件包含大量的內部環節。更為重要的是,很多情況下的可視組件復用,Ren’Py顯示時就不需要重新渲染了,可以導致另一項明顯的速度提升。

要比較固定位置參數和特性(property)時,Ren’Py使用的相等概念即Python內嵌的 “==” 操作符。我們已經擴展了行為(action)的相等概念,可以判斷兩個行為是否相等,也就是行為間沒有差異的情況——當無論哪個行為被調用,行為的需求和最終作用於組件的可用性和可選擇性都相同。

所有Ren’Py提供的行為(action)都符合該定義。當定義你自己的行為(action)時,也需要讓行為符契約樣的相等概念。可以通過提供一個合適的 __eq__ 函數實現。例如:

class TargetShip(Action):
    def __init__(self, ship):
        self.ship = ship

    def __eq__(self, other):
        if not isinstance(other, TargetShip):
            return False

        return self.ship is other.ship

    def __call__(self):
        global target
        target = self.ship

認真細緻地定義 __eq__ 函數很重要,確保這個函數可以比較行為的所有欄位(field),並可以合適地使用相等符號(==)和標識(is)比較。

常表達式和純函數 link

Ren’Py可以檢索常變數和純函數的特性(property),並提升界面賦值式的運行速度,且避免界面某些部分的賦值運算。

定義 link

如果一個表達式計算結果總是同樣的值,那麼這個表達式就是 const 的(constant常量的縮寫)。出於Ren’Py的需要,只有在表達式計算結果為常量或者表達式被定義為常量時,表達式才是常表達式。

  • 將任何一元,二元或三元運算符應用於表達式,前提是其他操作數也是常量。

  • 訪問表達式上的欄位。

  • 使用數字或對象為表達式建立索引。

Python的數值和字串都是常量,由常量數值和字串組成的列表、元組、集和字典類型數據也都是常量。Ren’Py中使用 define 語句將變數定義為常量。 renpy.const()renpy.not_const() 函數可以用於在Ren’Py判斷是否為常量的問題上進行更細緻的操作。在下面的 常量名稱 中會列出預設的常量名。

如果你使用的某個變數從來不會改變,使用 define 將其定義和聲明為常量是合理的。例如:

define GRID_WIDTH = 20
define GRID_HEIGHT = 10

當某個可調用的函數、類(class)或者行為(action)的入參都是常量,返回值也始終是同一個常量,那麼這個函數、類或者行為就是 pure (純粹的)。除此之外,一個使用常表達式調用純函數的表達式也總是一個常表達式。

大量的默認函數、類和行為都已被標記為“pure”。這些函數會在後面的 純函數名 章節中列出。

函數可以使用 renpy.pure() 定義為純函數,並在默認儲存區中用作函數修飾器(decorator)。

常表達式和純函數在下列事件消息中不需要可以維護同一個值:

  • 初始化段落的結尾。

  • 語言的改變。

  • 重建樣式。

如何使用常量最佳化界面語言 link

確保界面語言入參和特性(property)是常量,會帶來三點優勢。

第一點優勢是,常量入參和特性(property)在界面準備階段就可以計算和簡化,比如在初始化階段結束時、語言改變時、重建樣式時。在那之後,就不需要再浪費時間計算常量入參和特性了。

第二點優勢是,常量與可視組件復用功能的相性很好。如果某個可視組件的入參和特性都是常量,那麼這個可視組件就總是可以被復用的,獲得了可視組件復用的最大收益。

最後一點優勢是,當Ren’Py遇到可視組件的樹(tree),整個樹的所有入參、特性和作用主控流程的表達式都是常量的話,Ren’Py就不在重新計算表達式或創建新的可視組件,而會重用整個樹。這種規格的復用會帶來明顯的性能飛升。

例如,下列的界面不執行任何Python語句,僅在首次預載入或者顯示時創建可視組件:

screen mood_picker():
    hbox:
        xalign 1.0
        yalign 0.0

        textbutton "Happy" action SetVariable("mood", "happy")
        textbutton "Sad" action SetVariable("mood", "sad")
        textbutton "Angry" action SetVariable("mood", "angry")

常量文本 link

定義文本時請注意,包含新樣式文本替代的字串都是常量:

$ t = "Hello, world."
text "[t]"

直接包含某個文本變數的字串,一般不是常量:

$ t = "Hello, world."
text t

使用百分號替代格式的字串,也不是常量:

$ t = "Hello, world."
text "%s" % t

最後需要注意的是,文本多語言支持函數下劃線(_)是純函數,所以如果這個函數包含一個字串,整個表達式是常表達式:

text _("Your score is: [score]")

如果變數中包含文本替換,就需要使用 !i 轉義符:

$ who = "Jane"
$ t = "Hello, [who]!"
text 'Then I told her, "[t!i]"'

常函數 link

renpy.const(name) link

將某個儲存區的變數聲明為常量。

如果沒有什麼可以改變一個變數的值,或者無法建立索引抵達變數,或者不能存取變數的各種屬性(attribute),那這個變數就是常量。變數必須在定義、初始化和多語言支持Python語句塊(block)之外保持一個常值。

name

一個字串,表示聲明為常量的變數名。

renpy.not_const(name) link

將某個儲存區的變數聲明不是常量。

這個函數會取消 renpy.const()renpy.pure() 的效果。

name

聲明不為常量的變數名。

renpy.pure(fn) link

聲明某個函數為純函數。純函數必須在定義、初始化和多語言支持Python語句塊(block)之外總是使用同樣的入參並返回同樣的值。

fn

聲明為純函數的函數名。可以是包含函數名的字串,或者函數本身。

返回 fn ,允許函數用作修飾器(decorator)。

性能分析 link

Ren’Py支持使用 renpy.profile_screen 函數對界面執行進行性能分析。

class renpy.profile_screen(name, predict=False, show=False, update=False, request=False, time=False, debug=False, const=False) link

請求對名為 name 的界面進行界面分析, name 必須是個字串。

除了 name ,所有入參都必須以關鍵字入參形式提供。該函數使用三組入參。

predict

若為真(True),在界面預載入時進行性能分析。

show

若為真(True),在界面第一次顯示時進行性能分析。

update

若為真(True),在界面更新時進行性能分析。

request

若為真(True),在按下F8時進行性能分析。

第二組入參控制性能分析的輸出結果。

time

若為真(True),Ren’Py會記錄界面運算消耗的時間。

debug

若為真(True),Ren’Py會記錄界面運算的相關資訊,包括:

  • 哪些可視組件被Ren’Py處理為常量。

  • 哪些入參需要被運算。

  • 哪些可視組件被復用。

產生和記錄這些除錯資訊會消耗可觀的時間。所以當 debug 為真(True)時,輸出的 time 時間應該就不是完全可信的。

最後一組入參控制每次Ren’Py運行時的輸出結果。

const

顯示在界面中標記為常量和非常量的變數。

所有性能分析輸出都會記錄在遊戲目錄的profile_screen.txt文件中。

常量名 link

以下是預設的常量名:

  • False

  • None

  • True

  • config

  • style

純函數名 link

以下是預設為純函數或常量名。

  • ADVCharacter

  • ADVSpeaker

  • AddToSet()

  • Alpha

  • AlphaBlend()

  • AlphaDissolve()

  • AlphaMask()

  • AnimatedValue()

  • Animation

  • At()

  • AudioPositionValue()

  • Call()

  • Character()

  • Color

  • ComposeTransition()

  • ConditionSwitch()

  • Confirm()

  • CropMove()

  • DictInputValue()

  • DictValue()

  • DisableAllInputValues()

  • Dissolve()

  • Drag

  • DynamicCharacter

  • DynamicDisplayable()

  • EndReplay()

  • FactorZoom

  • Fade()

  • FieldInputValue()

  • FieldValue()

  • FileDelete()

  • FilePage()

  • FilePageNameInputValue()

  • FileTakeScreenshot()

  • Fixed()

  • Flatten()

  • FontGroup()

  • Frame()

  • Grid()

  • HBox()

  • Help()

  • Hide()

  • HideInterface()

  • If()

  • Image()

  • ImageDissolve()

  • ImageReference

  • InputValue

  • InvertSelected()

  • Jump()

  • Language()

  • LiveComposite()

  • LiveCrop()

  • LiveTile()

  • MainMenu()

  • MixerValue()

  • Motion

  • MouseMove()

  • Move

  • MoveFactory

  • MoveIn

  • MoveOut

  • MoveTransition()

  • Movie()

  • MultipleTransition()

  • NVLCharacter

  • Notify()

  • Null()

  • NullAction()

  • OldMoveTransition

  • OpenURL()

  • Pan

  • ParameterizedText()

  • Particles

  • Pause()

  • PauseAudio()

  • Pixellate()

  • Play

  • PlayCharacterVoice()

  • Position

  • Preference()

  • PushMove()

  • Queue()

  • QueueEvent()

  • QuickLoad()

  • QuickSave()

  • Quit()

  • RemoveFromSet()

  • Replay()

  • RestartStatement()

  • Return()

  • Revolve

  • RevolveInOut

  • RollForward()

  • Rollback()

  • RotoZoom

  • ScreenVariableValue()

  • Screenshot()

  • SelectedIf()

  • SensitiveIf()

  • SetCharacterVolume()

  • SetDict()

  • SetField()

  • SetMixer()

  • SetMute()

  • SetScreenVariable()

  • SetVariable()

  • SetVoiceMute()

  • Show()

  • ShowMenu()

  • ShowTransient()

  • ShowingSwitch()

  • SizeZoom

  • Skip()

  • SnowBlossom()

  • Solid()

  • Speaker

  • Start()

  • StaticValue()

  • Stop

  • StylePreference()

  • SubTransition

  • Text()

  • ToggleDict()

  • ToggleField()

  • ToggleMute()

  • ToggleScreen()

  • ToggleScreenVariable()

  • ToggleSetMembership()

  • ToggleVariable()

  • ToggleVoiceMute()

  • Transform

  • Update

  • VBox()

  • VariableInputValue()

  • VariableValue()

  • Viewport

  • VoiceReplay()

  • Window

  • Zoom

  • ZoomInOut

  • _()

  • _DisplayReset

  • _InputValueAction

  • _m1_00gallery__GalleryAction

  • _m1_00gallery__GalleryToggleSlideshow

  • _m1_00musicroom__MusicRoomPlay

  • _m1_00musicroom__MusicRoomRandomPlay

  • _m1_00musicroom__MusicRoomStop

  • _m1_00musicroom__MusicRoomTogglePlay

  • _m1_00preferences__DisplayAction

  • _movebottom

  • _moveleft

  • _moveright

  • _movetop

  • _narrator

  • _notify_transform

  • _p()

  • abs

  • all

  • any

  • apply

  • bin

  • blinds

  • bool

  • bytes

  • callable

  • centered

  • chr

  • cmp

  • color

  • dict

  • dissolve

  • divmod

  • fade

  • filter

  • float

  • frozenset

  • getattr

  • globals

  • gui.SetPreference()

  • gui.TogglePreference()

  • gui.preference()

  • hasattr

  • hash

  • hex

  • hpunch

  • int

  • irisin

  • irisout

  • isinstance

  • len

  • list

  • long

  • map

  • max

  • min

  • name_only

  • narrator

  • oct

  • ord

  • pixellate

  • pow

  • pushdown

  • pushleft

  • pushright

  • pushup

  • range

  • reduce

  • renpy.Keymap

  • renpy.ParameterizedText()

  • renpy.check_text_tags()

  • renpy.curried_call_in_new_context

  • renpy.curried_invoke_in_new_context

  • renpy.curry

  • renpy.easy_displayable

  • renpy.exists()

  • renpy.filter_text_tags()

  • renpy.fsdecode()

  • renpy.fsencode()

  • renpy.get_all_labels()

  • renpy.has_label()

  • renpy.has_screen

  • renpy.image_exists

  • renpy.image_size()

  • renpy.known_languages()

  • renpy.license

  • renpy.list_files()

  • renpy.loadable()

  • renpy.munged_filename

  • renpy.partial

  • renpy.unelide_filename

  • renpy.variant()

  • renpy.version()

  • renpy.version_name

  • renpy.version_only

  • renpy.version_string

  • renpy.version_tuple

  • repr

  • round

  • set

  • slideawaydown

  • slideawayleft

  • slideawayright

  • slideawayup

  • slidedown

  • slideleft

  • slideright

  • slideup

  • sorted

  • squares

  • str

  • sum

  • tuple

  • ui.callsinnewcontext

  • ui.gamemenus

  • ui.invokesinnewcontext

  • ui.jumps

  • ui.jumpsoutofcontext

  • ui.returns

  • unichr

  • unicode

  • vars

  • vcentered

  • vpunch

  • wipedown

  • wipeleft

  • wiperight

  • wipeup

  • zip

  • zoomin

  • zoominout

  • zoomout