找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 676|回复: 2

[原创] 她的心里话!隐藏文本!

[复制链接]
发表于 2025-7-8 22:46:05 | 显示全部楼层 |阅读模式

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

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

×
本帖最后由 Maz马 于 2025-10-13 18:08 编辑

一如既往叠甲
首先,我的编程基础为0.1(嗯,也玩了不少时间了),以下为纯摸索经验,如果有不对的地方望轻喷
基于个人需求写的东西
拉都拉了,就把这盘九转大肠捧出来给大家品鉴一下

事情的起因是这样的,我一直很喜欢在各种软件,广告,小程序里剽窃有意思的功能
最近玩了一下AI聊天软件,其中有一个“窥探心事”功能
大概就是,正常玩,就是和ai聊天,“窥探心事”则会触发AI深度思考并且给你“表面”之下的心里话
这个功能非常的有意思,所以我决定实现一下

思路是在对话框上面加一个按钮,点击这个按钮则会对当前对话进行变更

比如:

我好 爱 你啊” 变成 “我好 恨 你啊”(直接变更文本)
我好爱你啊” 加一行变成 “我好爱你啊\n我磨好刀了桀桀桀桀桀”(在原有文本下增加一行文字)
注意,为什么这个功能有意思呢?
因为他是不影响你剧本的,只要你不点这个按钮,你就看不到,而且

它是在原对白上进行变更!不是分支!是确确实实的隐藏文本!

预览:

                               
登录/注册后可看大图



25.10.13 完整代码(已经完善,可以即插即用了):
[RenPy] 纯文本查看 复制代码
default persistent.alpha_dict = {}#一个存储哈希值键值对的字典
default alpha_trans_id = None#新的哈希值
default alpha_last_id = None#旧的哈希值

init python:
    # 启动函数
    def change_alpha(alpha_trans_id):
        # 给予一个启动值,使用Function()的原因是可以自动调用一次刷新交互界面
        # 也就是renpy.restart_interaction()
        persistent.alpha_dict[alpha_trans_id] += 0.05
    # 自增函数
    def auto_add(alpha_trans_id):
        persistent.alpha_dict[alpha_trans_id] += 0.05
        # 超过则回归数值
        if persistent.alpha_dict[alpha_trans_id] >= 0.8:
            persistent.alpha_dict[alpha_trans_id] = 0.8
        # 数值对齐后的行为
        if persistent.alpha_dict[alpha_trans_id] == 0.8:
            pass
        else:
            # 进行回滚
            renpy.run(RestartStatement())
            # RestartStatement()的源码是renpy.rollback(force=True,checkpoints=0,defer=True),即只有主流程的上下文可以回滚
            
            # renpy.rollback(force=True,checkpoints=0,defer=False)
            # force=True 在主流程上下文,强制回滚,
            # checkpoints=0 回滚检查点为0,即回滚一步,
            # defer=False 无视上下文,无论在主菜单,主流程,回放,任何上下文都可以回滚
        return
    # 自动维护字典
    def clean_dict():
        # 保留值为
        # 进行了数值变更的键值对的上一条键值对
        # 进行了数值变更的键值对
        # 进行了数值变更的键值对的下一条键值对
        # 其余清理。(原因需要看思路过程,和say的加载机制有关)

        keys = list(persistent.alpha_dict.keys())
        keep_keys = []
        print(persistent.alpha_dict)
        for i, key in enumerate(keys):
            if persistent.alpha_dict[key] != 0.0:
                if i > 0 and keys[i-1] not in keep_keys:
                    keep_keys.append(keys[i-1])
                if key not in keep_keys:
                    keep_keys.append(key)
                if i < len(keys) - 1 and keys[i+1] not in keep_keys:
                    keep_keys.append(keys[i+1])
        if None in keep_keys:
            keep_keys.remove(None)
    
        new_dict = {}
        for key in keep_keys:
            new_dict[key] = persistent.alpha_dict[key]
        persistent.alpha_dict = new_dict
    # 重置 所有隐藏对白
    def reset_alpha():
        for key in persistent.alpha_dict:
            persistent.alpha_dict[key] = 0.0
    # 重置 整个哈希字典
    def reset_dict():
        persistent.alpha_dict = {}

#覆写对话窗口
screen say(who, what):
#通过检测文本标签来判断这句对白是否含有隐藏文本
#用调试文本标签{#ss}来特定{alpha标签,避免正常的{alpha被检测
    $ has_alpha_tag = "{#ss}{alpha=" in what
    python:
        global alpha_trans_id,alpha_last_id
        clean_dict()
        # 如果当前没有ID,获取当前ID
        if not alpha_trans_id:
            alpha_trans_id = renpy.get_translation_identifier()
        # 获取比对用的ID
        check_alpha_id = renpy.get_translation_identifier()
        # 如果当前ID不在字典中,初始化透明度
        if alpha_trans_id not in persistent.alpha_dict:
            persistent.alpha_dict[alpha_trans_id] = 0.0
        # 如果对比ID不在字典中,初始化透明度
        if alpha_last_id not in persistent.alpha_dict:
            persistent.alpha_dict[alpha_last_id] = 0.0
        # 如果当前ID和比对ID不一致,记录ID,并把比对ID赋值给当前ID
        if alpha_trans_id != check_alpha_id:
            alpha_last_id = alpha_trans_id
            alpha_trans_id = check_alpha_id
    # 当字典中的哈希对值大于启动值,小于终止值时,每0.05秒调用一次自增函数
    if persistent.alpha_dict[alpha_last_id] > 0.04 and persistent.alpha_dict[alpha_last_id] < 0.8:
        timer 0.05 action Function(auto_add,alpha_last_id) repeat True
    vbox:
        # 按钮触发透明度变化
        if persistent.alpha_dict[alpha_last_id] < 0.5:
            textbutton "显示隐藏内容" action Function(change_alpha,alpha_last_id)
        textbutton "清理缓存" action Confirm(_("锁上这条隐藏对白吗?"),[SetDict(persistent.alpha_dict,alpha_last_id,0.0),RestartStatement()])
    #原生的screen say内容
    style_prefix "say"
    window:
        id "window"
        if who is not None:
            window:
                id "namebox"
                style "namebox"
                text who id "who"
        text what id "what"
# 使用方式:
# 上面的照着抄之后,使用{#ss}{alpha=[persistent.alpha_dict[alpha_trans_id]]}___________ {/alpha}包裹隐藏文本就可以了
label start:
    show bg
    "这句话的隐藏内容会独立控制。\n{#ss}{alpha=[persistent.alpha_dict[alpha_trans_id]]}这是隐藏内容{/alpha}"
    "测试"
    "这是第二句话,透明度也是独立的。\n{#ss}{alpha=[persistent.alpha_dict[alpha_trans_id]]}另一段隐藏内容{/alpha}"



以下全部内容是思路过程(仅作为我自己的记录,可以完全不看):

这个功能用到renpy一个十分冷门的东西
RestartStatement()文档链接:界面行为(action)、值(value)和函数 — Ren'Py 中文文档
查看源码之后发现其实就是封装了一条特定参数的回滚
[RenPy] 纯文本查看 复制代码
renpy.rollback(force=True,checkpoints=0,defer=False)


这个行为,背后的逻辑实际涉及到“文本是否被阅读过的功能”。
文本如果已经被阅读过,那么对应的is_seen就会变成True,statement则是记录了文本信息
因此,即便你变更了某项数值,他也不会显现在当前的对白上


[RenPy] 纯文本查看 复制代码
#首先定义持久化变量,必须要是持久化的
default persistent.test = 0.0

#创建界面和按钮
screen abc():
    #按钮执行赋值persistent.test为0.5,也就是从透明变成半透明,然后执行RestartStatement(),回滚到当前语句
    textbutton "test" action SetVariable("persistent.test",0.5),RestartStatement()

label start:
    show bg
    #显示这个窗口
    show screen abc
    #为你要隐藏的内容打上texttag,并且插值,现在,只要你点击这个按钮,隐藏内容就会出现
    "龙叔不爱KFC\n{alpha=[persistent.test]}才怪{/alpha}"
    #如果你要重复使用这个值,那么重置它,因为它是持久化的
    $ persistent.test = 0.0
    return

好的,现在我们有了一条被隐藏的对白,接下来我们进行拓展,给他加上动画
这里我使用的是Function(),将我们的刚才的screen abc迁移到原生的screen say
[RenPy] 纯文本查看 复制代码
#这里是把按钮移到了screen say里,我们想要动画,需要让这个持久化的变量持续变动
init python:

    #定义增值函数
    def change_alpha():
        persistent.test += 0.05
        renpy.run(RestartStatement())
    # 将关键回滚行为封装进增值函数,因为动画实际上是由多次回滚组成的

    #定义自动函数
    def auto_increase():
        if persistent.test >= 0.5:
            return
        change_alpha()
        return
    #将自增函数拆分成两部分,是因为我们需要一个启动器,和一个过程器
            

screen say(who, what):
    style_prefix "say"

    textbutton "test" action Function(change_alpha)#在按钮处调用启动器
    window:
        id "window"

        if who is not None:

            window:
                id "namebox"
                style "namebox"
                text who id "who" 

        text what id "what"
    
    if persistent.test > 0.04 and persistent.test < 0.5:#当条件满足时,自动执行过程器
        timer 0.05 action Function(auto_increase) repeat True
        
    #最初的想法是仅用自增函数完成,但是由于这是*多个*交互行为,
    #而RestartStatement()这个行为本身会结束交互,因此使用了timer来进行自动化


拓展,这里只是使用了function(),实际上也可以做成text_tag,也可以对任何接受插值的文本标签操作
(例如kinetic_text_tags),甚至你可以做出拖动某个字加入到对白中的效果,本质是一个按钮与原生的screen say联动的接口

25.8.28更新思路记录:
旧的例子中,需要为每一句对白都设置持久化变量,这既不利于逻辑的判断,也不利于管理
我的思路是使用翻译文本的对白哈希值来动态的记录变更的值
@Koji 的帮助下,实现了批量使用并管理隐藏文本
[RenPy] 纯文本查看 复制代码
default persistent.alpha_dict = {}#一个存储哈希值键值对的字典
default alpha_trans_id = None#新的哈希值
default alpha_last_id = None#旧的哈希值
 
init python:
    # 自增动画
    def change_alpha(alpha_trans_id):
        persistent.alpha_dict[alpha_trans_id] += 0.05
        if persistent.alpha_dict[alpha_trans_id] >= 0.8:
            persistent.alpha_dict[alpha_trans_id] = 0.8
        if persistent.alpha_dict[alpha_trans_id] == 0.8:
            renpy.run(SetVariable("gold",gold-10))
            renpy.run(Play("sound","audio/music/sound_money.mp3"))
        else:
            renpy.run(RestartStatement())
    def auto_increase(alpha_trans_id):
        if persistent.alpha_dict[alpha_trans_id] >= 0.8:
            return
        change_alpha(alpha_trans_id)
        return
    ### 需要手动维护字典
    def clean_dict():
        print(persistent.alpha_dict,persistent.alpha_dict_num)
        if len(persistent.alpha_dict)-persistent.alpha_dict_num >= 3:
            keys_to_remove = []
            for key, value in persistent.alpha_dict.items():
                if value == 0.0:
                    keys_to_remove.append(key)
            for key in keys_to_remove:
                del persistent.alpha_dict[key]
            persistent.alpha_dict_num = len(persistent.alpha_dict)
    # 重置 所有隐藏对白
    def reset_alpha():
        for key in persistent.alpha_dict:
            persistent.alpha_dict[key] = 0.0
    # 重置 整个哈希字典
    def reset_dict():
        persistent.alpha_dict = {}
#原生对话窗口
screen say(who, what):
#通过检测文本标签来判断这句对白是否含有隐藏文本
#{#ss}用调试文本标签来特定{alpha标签,避免正常的{alpha被检测
    $ has_alpha_tag = "{#ss}{alpha=" in what
 
    #如果检测到了隐藏文本则显示按钮
    if has_alpha_tag:
        python:
            # 获取当前对话的哈希值
            get_trans_id = renpy.get_translation_identifier()
            # 初始化透明度为0.0,并存储在动态字典里
            if get_trans_id not in persistent.alpha_dict:
                persistent.alpha_dict[get_trans_id] = 0.0
            #锁定对白,并把值拷贝到全局
            if alpha_trans_id != get_trans_id:
                renpy.run((SetVariable("alpha_last_id",alpha_trans_id),SetVariable("alpha_trans_id",get_trans_id)))
        vbox:
            # 按钮触发透明度变化
            if persistent.alpha_dict[alpha_last_id] < 0.5:
                textbutton "显示隐藏内容" action Function(change_alpha,alpha_last_id)
            textbutton "清理缓存" action Confirm(_("锁上这条隐藏对白吗?"),[SetDict(persistent.alpha_dict,alpha_last_id,0.0),RestartStatement()])
 
        # 自动动画
        if persistent.alpha_dict[alpha_last_id] > 0.04 and persistent.alpha_dict[alpha_last_id] < 0.5:
            timer 0.05 action Function(auto_increase,alpha_last_id) repeat True
#原生的screen say内容
    style_prefix "say"
    window:
        id "window"
        if who is not None:
            window:
                id "namebox"
                style "namebox"
                text who id "who"
        text what id "what"
 
label start:
    show bg
    # 测试不同句子的透明度控制,也是使用的方法,直接使用{#ss}{alpha=[persistent.alpha_dict[alpha_trans_id]]}XXXX{/alpha}包裹隐藏文本就可以了
    "这句话的隐藏内容会独立控制。\n{#ss}{alpha=[persistent.alpha_dict[alpha_trans_id]]}这是隐藏内容{/alpha}"
    "测试"
    "这是第二句话,透明度也是独立的。\n{#ss}{alpha=[persistent.alpha_dict[alpha_trans_id]]}另一段隐藏内容{/alpha}"
    # 清理函数
    # $ clean_dict()


细则:
现在功能是基本完善可用的,但还有一些问题没研究明白,先把明白的写下来。

1.
不知道为何这一句并没有成功拦截哈希值的读取和字典的初始化
[RenPy] 纯文本查看 复制代码
 if has_alpha_tag:

而是仅拦截了显示按钮。也许是因为say的预加载机制。
但我还不确定...
但正因为没有拦截到哈希,才让这串代码跑通了...屎山发力!(后面会说原因)
由于并没有拦截到哈希键值对的初始化,因此每一句对白的哈希对都会被记录在字典里
所以当你的对白相当多时,需要定期清理字典,需要注意的是,不能在变更透明度文本的当前句以及上一句清理字典
因为他会把当前显示的文本的哈希对清理掉,而引发值未定义的报错。
[RenPy] 纯文本查看 复制代码
 $ clean_dict()


2.
最开始我的想法是动态清理字典,但在多次尝试以后,我发现了这个结构很神奇的地方...
[RenPy] 纯文本查看 复制代码
RestartStatement()

这个指令会使这个句子的哈希值变成上一句的哈希值...(只是在显示上)

所以我想通过源码方法renpy.get_translation_info()来用哈希锁定返回的句子也失败了
因为字典中,变更透明度后的值,对的是上一条句子的哈希...

“一条对话在字典中将会对应两条哈希,首先是隐藏时的哈希,由于say需要显示当前对白,所以当前哈希需要在字典中有记录,否则报错。
而由于回滚指令RestartStatement(),所以在透明度变更时,实际变更的是上一条对话的哈希在字典中对应的值。
(上一条哈希代表的是上一条对话,即不是被变更透明度的对话,但在字典中,它对的值代表了透明度变更后的值)”

听起来很绕...我简单打个图表方便理解

[RenPy] 纯文本查看 复制代码
    #假设我有对话
    "aaa"
    "{变更透明度标签=[bbb变更透明度前原始的值]}bbb{/变更透明度标签}"
   #此时,透明度字典应该是:
    {aaa的哈希:bbb变更透明度后的值,bbb的哈希:bbb变更透明度前原始的值}
   # 如果在“aaa”和“bbb”显示时你清理了字典中aaa的键值对
   # 那么 bbb的哈希:bbb变更透明度前原始的值 这个键值对就会被清理掉,而"{变更透明度标签=[bbb变更透明度前原始的值]}bbb{/变更透明度标签}"这一句就会报错



就很抽象...

3.
由于这个功能的字典必须使用持久化变量,因此字典会全局影响
也就是说,假设给这个功能设置一个条件,例如:
消耗“好感度”来解锁“心里话”
那么在多个存档中,以及回放中,回滚中都会有一个像作弊的bug
例如我在回放中解锁,而不是在局内解锁
那么当我回到游戏时,实际并没有支出任何的数值...
这一点需要用下一贴来解决,但如果回放中不允许解锁则无需在意
“在回放中影响当前游戏存档数据的方法”

看起来很简单的小功能,没想到涉及到回滚和持久化之后就会这么麻烦





#查看我写的更多屎

在回放中影响当前游戏存档数据的方法
https://www.renpy.cn/forum.php?mod=viewthread&tid=1739

她的心里话!隐藏文本!
https://www.renpy.cn/forum.php?mod=viewthread&tid=1713

能力雷达图
https://www.renpy.cn/forum.php?mod=viewthread&tid=1719

暴学CDD的十四天/附虚拟摇杆
https://www.renpy.cn/forum.php?mod=viewthread&tid=1675

简单rpg地图移动
https://www.renpy.cn/forum.php?mod=viewthread&tid=1652

摇骰子小游戏
https://www.renpy.cn/forum.php?mod=viewthread&tid=1653

猜球盅小游戏
https://www.renpy.cn/forum.php?mod=viewthread&tid=1574

FontGroup() 游戏里全局使用两种字体的方法
https://www.renpy.cn/forum.php?mod=viewthread&tid=1491

粉身碎骨浑不怕,要留答辩在人间

























发表于 2025-7-9 09:16:02 | 显示全部楼层
赞美大佬
回复

使用道具 举报

发表于 2025-7-9 19:59:54 | 显示全部楼层
好适合白切黑病娇哦
回复 支持 抱歉

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-10-14 23:33 , Processed in 0.045277 second(s), 25 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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