找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 48|回复: 0

[原创] 将外部目录添加给Renpy加载

[复制链接]
发表于 前天 23:11 | 显示全部楼层 |阅读模式

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

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

×
此内容也发在了我的仓库里, 点个 Star 吧: https://github.com/Koji-Huang/KojiRenpyBox




使用方法:
在打包好游戏内 renpy 文件夹下的 loader.py 里添加如下代码 ( 也就是修改 SDK )
[RenPy] 纯文本查看 复制代码
def scan_outer_resource(add, seen):

    files = game_files
​
    outer = "xxx"
​
    for each in walkdir(outer):
        add(outer, each, files, seen)
​
scandirfiles_callbacks.append(scan_outer_resource)

这部分代码应该添加在 scandirfiles_callbacks 被定义后( VS Code 随手一搜就知道了 ), xxx 为外部目录的路径

例如:

我们在游戏外有一个 D:\\resource\\ 文件夹, 这个文件夹里有如下文件
[RenPy] 纯文本查看 复制代码
| resource
|--- image_aaa.png
|--- clone_script.rpy

在游戏启动后
[RenPy] 纯文本查看 复制代码
| resource
|--- image_aaa.png
|--- clone_script.rpy
|--- clone_script.rpyc

此时该文件夹下的所有内容都被 Renpy 扫描并存入了索引, clone_script.rpy 也生成了 rpyc 缓存
此时 resource 文件夹中 image 的引用为 image aaa = "image_aaa.png" 而不是 image aaa = "resource\image_aaa.png"




原理:
我们来逐个解读代码中的各个参数

game_files
所有的游戏文件的索引, 保存在 game_files 里的索引等效于该文件存放在 game 文件夹下, 所以我们将外部资源载入到此文件里就可以保证资源被正常加载


一个新项目的 game_files 参数:
[RenPy] 纯文本查看 复制代码
(
  ("xxx\NewProject\game", "gui/bar/bottom.png")
  ("xxx\NewProject\game", "gui/bar/left.png")
  ("xxx\NewProject\game", "gui/bar/right.png")
  ("xxx\NewProject\game", "gui/bar/top.png")
  ("xxx\NewProject\game", "gui/bubble.png")
  ...
  ("xxx\NewProject\game", "screens.rpy")
  ("xxx\NewProject\game", "screens.rpyc")
  ("xxx\NewProject\game", "script.rpy")
  ("xxx\NewProject\game", "script.rpyc")
  ("xxx\NewProject\game", "SourceHanSansLite.ttf")
  ("xxx\NewProject\game", "tl/None/common.rpym")
  ("xxx\NewProject\game", "tl/None/common.rpymc")
)


与之相对的是 common_files, 这里面的索引为 Renpy  引擎内 common 文件的索引, 启动优先级会更高
一开始我定义了一个 outer_files, 但是发现定义在 outer_files 里的文件即使使用了自己写的 file_open_callbacks 也无法被正常加载, 然后联想到了 Renpy 里各个模块那极高的耦合性后我试着改成 game_files, 嗯, 果然成了 ( )

walkdir
walkdir 函数为定义在 loader.py 的一个函数, 具体作用就是遍历文件夹获取相对于传入目录的所有子文件的路径, 比如下面有这么个文件夹:
[RenPy] 纯文本查看 复制代码
D:
|--- image
|----|--- a.png
|----|--- gui
|----|----|--- b.png

如果我们使用 walkdir("D:\image") 那么该函数传回的参数就为
[RenPy] 纯文本查看 复制代码
(
("D:\image", "a.png")
("D:\image", "gui\b.png")
)

这些参数会存到对应的 files 里

add 和 seen

这两个参数都被定义在 loader.py 的 scandirfiles 函数里, 这个函数是扫描所有文件的函数, 在调用时, 他会调用各个回调函数来扫描文件

scandirfiles 源码:
[RenPy] 纯文本查看 复制代码

def scandirfiles():
  """
  Scans directories, archives, and apks and fills out game_files and
  common_files.
  """
​
  seen = set()
​
  def add(dn, fn, files, seen):
​
      fn = unicode(fn)
​
      if fn in seen:
          return
​
      if fn.startswith("cache/"):
          return
​
      if fn.startswith("saves/"):
          return
​
      files.append((dn, fn))
      seen.add(fn)
      loadable_cache[unicodedata.normalize('NFC', fn.lower())] = True
​
  for i in scandirfiles_callbacks:
      i(add, seen)

理解他们并不难, 总之 add 就是用于给搜索 callback 一个接口将 dn(父路径), fn(子路径) 添加到 files(比如game_files) 里的接口, 而 seen 是用于查重的一个集合

scandirfiles_callbacks
这个参数在上面客串过了, 也就是用于系统检索所有文件的函数的列表, 查看 loader.py 文件里可以发现存在四个 callback 被添加到了这个列表里
  • scandirfiles_from_apk: 在安卓下检索 APK 包内
  • scandirfiles_from_remote_file: 如果提供的话, 检索可下载的文件
  • scandirfiles_from_filesystem: 检索文件系统下的文件
  • scandirfiles_from_archives: 检索归档文件 (rpa等) 的索引


此外还有一个 file_open_callbacks, 这个 callback 是用于读取文件并返回读取后文件信息的回调函数, 也就是对各个文件读取的 I 接口, 返回的是字节流对象, 它也有对应的四个回调函数, 但因为上面的代码兼容其中的 load_from_filesystem, 所以不需要编写一个额外的 callback 了


其实我一开始就是编写了一个 callback 来支持读取的, 但是在我调试半天一个 I\O 错误后发现问题不出在这里并且根本不用写这个回调的时候, 气笑了


重新看源码

[RenPy] 纯文本查看 复制代码

# 定义函数
def scan_outer_resource(add, seen):
​
  # 定义 files 为 game_files 的引用
  # 我们需要将外部资源载入到游戏资源索引里
  files = game_files
​
  # 外部资源路径
  outer = "xxx"
​
  # 获取外部资源下的所有子文件的路径
  for each in walkdir(outer):
​
      # 将外部资源文件添加到 game_file 里
      add(outer, each, files, seen)
​
​
# 向检索文件回调函数队列中添加我们定义的函数
scandirfiles_callbacks.append(scan_outer_resource)



最后:

本周的 Renpy 大学习已完成 ( )

在安卓下该方法也可用, 也就是说, 对于安卓的 Renpy 应用, 我们可以把拓展的包体放置在游戏包体外的地方引用, 一开始就是有人找我想办法在安卓实现这个功能, 好在找出路子了 ( )

虽说我这里说是固定, 但我们可以编写一个 config 什么的在 loader.py 里面读取来实现动态获取, 但需要注意的是, 载入包体的时间发生在引擎启动阶段, 进入游戏后不可修改 ( 不过 Shift+R 刷新缓存倒是可以, 总之就是让 loader.py 重新跑一遍的就行 ).

另外, 打包好的包体里的文件是拷贝自 renpy-xxx-sdk\renpy 里的, 如果你改动 SDK 里 loader.py 的源码的话, 可以省去每次打包后重新修改 loader.py 的时间, 但同时你使用此 SDK 运行, 生成的所有代码都会带有这个语句, 我自己为了测试这个代码新下载了一个 Renpy 和一整套的安卓套件 ( 心疼我的机场流量 )

loader.py 这个文件非常有趣, 通过修改他就可以轻松达到加密游戏文件, 支持新的存档格式, 以及这次添加外部资源路径的效果, 但很多人都不喜欢看源码....包括这次写的加载也是不喜欢看源码导致的后果, 这就是所谓的"脏活累活"吗. 唉, 真麻烦呐---


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

本版积分规则

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

GMT+8, 2025-6-8 13:31 , Processed in 0.068401 second(s), 25 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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