Ren'Py模组开发:setattr设置多级属性无持久化效果问题
核心问题拆解
简单变量修改完全无效
你代码里len(_list) == 1的分支逻辑完全错误:o = new_val只是把局部变量o指向新值,根本没改变全局/存档里的目标变量。比如要修改simple_variable,这种写法等于白做。未对接Ren'Py的存档存储系统
Ren'Py的游戏变量都存在store模块中,直接用globals()获取变量,在模组的独立上下文里可能拿到的不是被存档追踪的实例——看起来当前会话里改了,但实际上没写到存档的核心存储里,退出模组就打回原形。自定义类未做持久化追踪
如果你的Avatar、Skill这类自定义类没继承renpy.object.Object,Ren'Py不会追踪它们的属性变化。哪怕你当前会话里改了属性值,存档时不会把这些变化写入文件,重启游戏自然就复原了。
具体修复步骤
1. 修复简单变量的修改逻辑
把len(_list) == 1分支的代码改成:
if len(_list) == 1: import store setattr(store, _list[0], new_val)
或者直接修改全局变量:
if len(_list) == 1: globals()[_list[0]] = new_val
2. 统一从store获取根变量
把获取根对象的代码从o = globals()[_list[0]]改成:
import store o = getattr(store, _list[0])
确保拿到的是被存档系统追踪的那个实例。
3. 让自定义类支持持久化
所有需要保存属性变化的自定义类,必须继承renpy.object.Object:
class Avatar(renpy.object.Object): def __init__(self): self.skills = {"combat": Skill()} # 其他属性定义 class Skill(renpy.object.Object): def __init__(self): self.level = 1
4. 优化复杂变量路径的处理(可选)
你当前的代码只能处理三层以内、特定格式的变量路径,比如obj.attr["key"].prop,完全没法处理obj.attr_a("some_id").attr_b这种带函数调用的路径。可以用eval简化逻辑(作弊模组自己用,安全风险可控):
def icmod_set_var_val(var_name, new_val): import store try: if '.' not in var_name: # 处理简单变量 setattr(store, var_name, new_val) return # 拆分出最后一个属性名和父对象路径 parent_path, attr_name = var_name.rsplit('.', 1) # 在store的上下文里执行父路径,拿到目标对象 parent_obj = eval(parent_path, store.__dict__) # 设置属性值 setattr(parent_obj, attr_name, new_val) except Exception as e: msgs.show(f'修改失败: {str(e)}')
这个版本能支持所有合法的Ren'Py变量路径,包括带函数调用、多层索引的情况。
5. 正确使用exec修改变量(可选)
如果想用exec,必须指定store的命名空间,确保修改的是存档追踪的变量:
import store exec(f"{var_name} = {repr(new_val)}", store.__dict__)
验证方法
修改后保存游戏,完全退出再重启,加载刚才的存档,检查变量是否保持修改后的值——如果生效,说明持久化逻辑没问题了。
内容的提问来源于stack exchange,提问作者Margo

