马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
本帖最后由 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
粉身碎骨浑不怕,要留答辩在人间
|