找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 6244|回复: 2

[经验] 行为钩子——改变内置行为的动作的一种实现

[复制链接]
发表于 2021-6-22 18:18:32 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
本帖最后由 忘忧北萱草 于 2021-6-24 13:17 编辑

有的时候,我们需要改变内置行为的动作,比如在保存之前进行一些判断与操作。
行为钩子是一种实现方式,它通过替换内置行为的定义,拦截行为的执行。

这是我写的行为钩子的实现,为了便于使用,我使用了装饰器的写法。以下是装饰器的定义:
[RenPy] 纯文本查看 复制代码
init -10 python:

    class ActionHook(Action):
        alt = None
        def __init__(self, action, func):
            self.action = action
            self.func = func
            self.alt = action.alt

        def get_sensitive(self):
            return self.action.get_sensitive()

        def get_selected(self):
            return self.action.get_selected()

        def get_tooltip(self):
            return self.action.get_tooltip()

        def periodic(self, st):
            return self.action.periodic(st)

        def predict(self):
            return self.action.predict()

        def __call__(self):
            return self.func(self.action)

        def __eq__(self, other):
            if isinstance(other, ActionHook):
                return self.action == other.action
            elif isinstance(other, Action):
                return self.action == other
            return False


    def action_hook(action_name):
        '''行为钩子装饰器,用于拦截并改变行为的执行。

        用法举例:
        ```
        @action_hook('FileSave')
        def file_save_hook(do_action, *args, **kwargs):
            if renpy.random.random() > 0.5:
                return do_action() # 执行正常的保存
            else:
                renpy.notify('A mysterious power has stopped you from saving...')
                return
        ```

        参数:
            action_name: 所拦截的行为的名称

        被装饰函数需要有一个固定参数`do_action`,这是一个无参数的`callable`,表示执行原本的`action`。
        被装饰函数其余的参数与所拦截行为的参数一致。
        '''
        def wrapper(func):
            variables = globals()
            action_old = variables[action_name]
            if not action_old:
                raise NameError('{} is not an action!'.format(action_name))

            def action_new(*args, **kwargs):
                action_instance = action_old(*args, **kwargs)
                def exec_action(action):
                    return func(action, *args, **kwargs)
                return ActionHook(action_instance, exec_action)

            print(action_name)
            variables[action_name] = action_new
            return func
        return wrapper
            
用法可以看 action_hook 的 docstring。



以下是一个应用实例:
[RenPy] 纯文本查看 复制代码
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


init -5 python:
    @action_hook('FileAction')
    def file_action_hook(action, *args, **kwargs):
        if forbid_saving and renpy.current_screen().screen_name[0] == "save":
            renpy.notify("不,你现在不可以存档。")
        elif forbid_loading and renpy.current_screen().screen_name[0] == "load":
            renpy.notify("不,你现在不可以读档。")
        else:
            return action() 


注意:由于行为钩子采用了是覆盖全局变量中原本的行为的方法,所以可能存在一些尚不确定的问题,请谨慎使用。

评分

参与人数 1干货 +3 收起 理由
被诅咒的章鱼 + 3 A mysterious power has forced me to supp

查看全部评分

发表于 2021-6-23 09:16:38 | 显示全部楼层
请教个问题:
warpper中的globals是什么?
回复 支持 抱歉

使用道具 举报

 楼主| 发表于 2021-6-23 17:04:33 | 显示全部楼层
被诅咒的章鱼 发表于 2021-6-23 09:16
请教个问题:
warpper中的globals是什么?

python的内置函数,返回一个包含全局变量的字典。
这里用来根据变量名获取和修改变量值
回复 支持 抱歉

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|RenPy中文空间 ( 苏ICP备17067825号|苏公网安备 32092302000068号 )

GMT+8, 2024-3-29 22:02 , Processed in 0.038499 second(s), 14 queries , File On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表