Ren'Py, 小心 Function 的返回值

2026/11/14 | 2分钟阅读 | 更新于 2026/11/14

Ren'Py, 小心 Function 的返回值

最近在用 Ren’Py 做点击解谜游戏,点击,变更状态,播放动画,Ren’Py 的 screen 很好用。

一段最简单的示例代码长这个样子,点击触发 action 会进入到动画状态;动画状态使用 timer 计时,结束后到达下一个状态。

default my_door_a_state = "closed" # "opening" "open" "closing"
default my_door_b_state = "closed" # "opening" "open" "closing"

screen my_doors():
  if my_door_a_state == "closed":
    button:
      background "my_door_image closed"
      action SetVariable("my_door_a_state", "opening")

  elif my_door_a_state == "opening":
    add "my_door_image opening"
    timer .5 action SetVariable("my_door_a_state", "open")

当然,如果这样写的话会有大量重复代码片段。所以,我定义了状态机 RuleSet,以及 Rule 来声明当前状态,何种条件,下一种状态,副作用比如音效等等。然后使用一个 screen 把整个 RuleSet 丢进去,自动渲染成 button 或者 timer + add 并处理 action,一切都非常完美。

然后,中间有过这样一个函数:

init python:
  def apply_rule(rule: Rule, rs: RuleSet) -> bool:
    # ...
    return True

顾名思义,它会检查 Rule 的条件是否满足,不满足返回 False, 满足则执行,最后返回 True.

于是,中间写出了这样的东西:

screen my_screen():
  # ...
  timer rs.duration action Function(apply_rule, rule, rs)

然后,一切看上去都很正常,只要定义好规则,一切都是自动的。

只是,如果有两扇门,在一扇门处在动画中的时候,点击另一扇门,会导致前一扇门的动画重新播放,这很奇怪。

虽然使用自定义的 Action 修复了这个问题,但是原本的问题是如何发生的呢?

为什么手写 if elif 没有问题,为什么自定义的 Action 没有问题,只是 Function 就有问题?这个问题,看上去,就像是,第二次点击改变状态,导致整体重新渲染,所以正在播放中的动画中断重新开始。

但是,手写 if elif 并没有问题,所以我第一时间推测,这是纯渲染的问题,所以尝试更改 screen 的写法。

但是接下来发现自定义 Action 不会出现动画的闪动(甚至需要你手动 renpy.restart_interaction() 不然根本不刷新),所以推测和 Function 的机制有关。

于是,从 Function 的原理,Rule 的一致性和相等判断,查了半天,全都毫无头绪。

但是我真的非常非常非常好奇原因,于是做了大量的实验。

然后!发现!如果我随便定义一个 def my_demo_func() 把 apply_rule 包起来……哎!动画!不闪了!

区别在哪?

返回值!

给 my_demo_func 加上返回值,嗯,果然动画会闪动。

嘛,到头来,问题其实很简单。

因为所有的一切都发生在 call screen 的状态下:

label my_label:
  call screen my_label_detail():
  jump my_label

然后:

If the function returns a non-None value, the interaction stops and returns that value. (When called using the call screen statement, the result is placed in the _return variable.)

Instead of using a Function action, you can define your own subclass of the Action class. This lets you name the action, and determine when it should be selected and sensitive.

Function - Screen Actions, Values, and Functions

因为 apply_rule 会 return bool, 所以 call screen 直接结束了。因为 label 有预防意外跳出的 jump 自己的语句,所以重新进入 label. 然后因为 Ren’Py 会 diff 然后复用元素,所以画面根本并不会中断,只是当两个动画同时播放时才会闪那么一下。

这下先入为主,完全没往这个方向想。

© 2026 香蕉引擎故障报告

🌱 Powered by Hugo with theme Dream.

关于

要怎么介绍自己呢,🤔。

很早以前是作为 Web 前端在学习的,但是工作第一年就成为了全干工程师。喜欢尝试各种东西,什么都会一点。

一直很喜欢 Ebiten 游戏引擎 ,特别简洁,用它做过一些小东西,可以查看这个分类 。另外特别推荐这个木鱼 ,是一个相对完整的小玩意儿,包含手搓的一个简单的 UI 框架;支持鼠标和键盘操作;有多语言和主题切换功能;同时支持 Web 端和客户端。它的源代码在 bin16/wooden-fish

主题

网站基于 Hugo,当前使用的是 hugo-theme-dream 主题的修改版 ,根据我的需要,做了一些对 PaperMod 的兼容。

我自己也写过主题 ,但是没有别人写的好看。

正在从我的笔记中往外搬运内容

等待更新:

  • 从《锈湖》中学了些什么东西
  • 我拿 React 写解谜游戏的经过
  • 基于 Pocketbase 的 Pocket Memos
  • 数独!