这篇文章上次修改于 794 天前,可能其部分内容已经发生变化,如有疑问可询问作者。

本文记录一些在使用 Ren'Py 时发现的一些增强表现力的小技巧的实现方法。


行内字体描边

在同一行内设置不同的字体描边。

因为文本样式标签不继承 outlines 属性,所以需要特殊的方法实现。

style border_ is text:
    color "#fff"
    outlines [(5, "#000", 0, 0)]
    ypos -0.2 

style borderbox_:
    xmaximum 24 # 此处适用于字号为22的情况

init python:
    def border_tag(tag, argument, contents):        
        return sum([
            [(
                renpy.TEXT_DISPLAYABLE, 
                Fixed(Text(c, style="border_"), style="borderbox_")
            ) for c in text]
            if kind == renpy.TEXT_TEXT
            else [(kind, text)]
            for kind, text in contents
        ], [])

    config.custom_text_tags["border"] = border_tag

label start:
    "Yiri" "有时候需要一点{border}{cps=20}恐怖的文字{/cps}{/border}。"

原理是把需要改变描边放到 Text 中,这样就能设置单独的 style。为了实现慢速显示,这里为每一个字都包装了单独的 Text,并且外包 Fixed 来控制字间距。

效果图:

效果图
感谢 @被诅咒的章鱼 的技术支持


等待一定时间后触发特殊剧情

某些隐藏剧情的进入条件是在某一处对话处等待一定时间,可以通过这样的方式实现。

define y = Character("Yiri")
label start:
    $ before_time = renpy.get_game_runtime()
    y "你需要在这里等待5秒钟。{w=5}{nw}"
    $ diff_time = renpy.get_game_runtime() - before_time
    if diff_time < 5: # 如果等待时间不足,进入正常剧情
        y "时间还不到哦。"
        y "你只等了[diff_time]秒,你有点缺少耐心了。"
    else: # 如果等待时间充足,进入隐藏剧情
        y "不错,你等了5秒钟。"

原理是设置一个超时自动跳过的对话,并记录对话前后的时间,对比时间差。

使用renpy.get_game_runtime,不会计算在菜单界面或存档期间的时间。


主菜单淡入

从启动界面切换到主菜单时,使用转场。

首先在screens.rpy中,找到main_menu界面的的定义,改成别的名字,比如main_menu_origin

screen main_menu_origin(): # 修改这里
    ...

然后在你的脚本中编写label main_menu,在这里call screen main_menu_origin ,并使用转场。

 label main_menu:
    call screen main_menu_origin with dissolve
    return

这是使用call screen而不是show screen,后者会导致无限循环。

注意with dissolve要和call写在同一行,以表明这是进入界面时的转场。


主菜单二级菜单

对于多故事组成的游戏,在开始游戏时不是进入某一段剧情,而是打开选择剧本的二级菜单。

首先编写二级菜单的screen。这里我为了省事,直接套用了main_menu的样式。

screen 故事选择():
    tag menu
    add gui.main_menu_background

    frame:
        style "main_menu_frame"

    vbox:
        style_prefix "navigation"

        xpos gui.navigation_xpos
        yalign 0.5

        spacing gui.navigation_spacing

        textbutton _("Story1") action Start("start1")
        textbutton _("Story2") action Start("start2")
        textbutton _("Story3") action Start("start3")
        textbutton _("返回") action Return() 

start1 start2 start3 分别是三个故事的起始label

使用action Return()返回主菜单。

然后在screens.rpy中修改navigation界面,把其中的Start()改为ShowMenu('故事选择')

screen navigation():
    ...
    if main_menu:
        textbutton _("开始游戏") action ShowMenu("故事选择")

存档钩子

有的时候我们需要在玩家存档、读档时进行一些额外的操作,这就需要拦截FileAction这一行为。

init -5 python:
    FileActionOld = FileAction # 保存原本的 FileAction
    def FileActionNew(name, page=None, **kwargs):
        if forbid_saving and renpy.current_screen().screen_name[0] == "save":
            return Notify("不,你现在不可以存档。") # 返回不同的 action,可以拦截存档读档行为
        elif forbid_loading and renpy.current_screen().screen_name[0] == "load":
            return Notify("不,你现在不可以读档。")
        else:
            return FileActionOld(name, page=page, **kwargs) # 调用原本的 FileAction 来完成存档读档操作
    FileAction = FileActionNew # 替换为自定义的 FileAction

define y = Character("Yiri")
default forbid_saving = False
default forbid_loading = False
label start:
    y "接下来是存档测试。"
    $ forbid_saving = True
    y "现在,存档被禁止了。"
    y "这很有趣,不是吗?"
    $ forbid_saving = False
    y "好了,你现在可以存档了。"
    $ forbid_loading = True
    y "不过,不能读档。"
    $ forbid_saving = True
    y "现在,试试一起被禁用的感觉。"
    $ forbid_saving = False
    $ forbid_loading = False
    y "很好,玩笑开够了。你可以继续了。"
    return

理论上用这种方法,可以拦截绝大多数的内置 action。


(持续更新中……)