Theseus 模组文档
资源

资源制作指南

音符皮肤等自定义资源的制作方法:图片皮肤 / 字符皮肤 / 模组图片加载


本页汇总游戏所有可以从外部导入的自定义资源,以及每种资源的制作 / 使用方式。大多数资源不需要编程:把文件放到正确的位置,游戏就会识别。

可导入资源总览

资源存放目录制作难度说明
音符皮肤data/respack/低用图片或字符配置替换音符外观
谱面data/charts/中游戏内编辑器制作,或导入他人谱面
剧情文件data/sp_lang/story/低导入他人写的剧情文本
收藏品data/collect/低解锁图鉴条目
音效data/hitsound/ data/sound_effects/低替换判定 / 界面音效
音乐data/audio/ data/bgm/低谱面音乐与菜单 BGM
界面语言data/lang/低文本翻译
窗口图标data/ths.ico低替换游戏窗口图标
模组mods/(工作目录根)高需要代码 / 脚本,见模组文档

所有 JSON 文件均使用 UTF-8 编码,并建议保持多行缩进格式。放入文件前建议先备份原文件。

音符皮肤

游戏启动时会扫描工作目录下的 data/respack/ 文件夹,把其中每个子文件夹识别为一个皮肤。

text
data/
└── respack/
    ├── RE-OST1.2/     ← 图片皮肤示例
    └── neon/          ← 字符皮肤示例(含完整字段说明的 note.json)

游戏内自带 Default(内置默认字符皮肤)始终置顶。手动把新文件夹放进 data/respack/ 后重启游戏(或下次进入皮肤列表时重新扫描)即可看到。

皮肤按内容分为两种类型:

两种皮肤都支持为自定义音符类型补充专属外观条目(见下文「自定义音符类型的皮肤」)。

文件夹内同时存在 4 张图片与 note.json 时,优先按图片皮肤识别。既无图片也无 note.json 的文件夹会被标记为加载错误,在列表中显示,但不会生效。

图片皮肤

图片皮肤是最直观的自定义方式。用绘图软件画 4 张 PNG 即可。

文件清单

文件推荐尺寸作用
normal.png1000 × 100普通音符(Tap)
hold.png1000 × 1100长按音符(Hold),三段式结构见下
exploit.png1000 × 100双向音符(Exploit)
payload.png1000 × 100特殊音符(Payload)

4 张图片缺一不可——缺少任意一张,整个文件夹无法按图片皮肤加载。

Hold 图片的三段式结构

长按音符的 hold.png 高度是 1100px,自上而下分为三段:

text
┌────────────────────────────┐
│  0 ~ 100px   终点(判定线)  │  ← 音符落下的判定线处显示这一段
├────────────────────────────┤
│  100 ~ 1000px  主体          │  ← 起点到终点之间的柱体,按长度伸缩
├────────────────────────────┤
│  1000 ~ 1100px  起点(按下点)│  ← 玩家按下音符的位置显示这一段
└────────────────────────────┘

注意方向:图片顶部是判定线终点,底部是玩家按下的起点。绘制时游戏按轨道宽度等比缩放整张图。

尺寸不达标会怎样

图片尺寸只要可读取就会被加载;若与推荐尺寸不符,皮肤仍能使用,但会在皮肤列表中标记为「不规范」。宽度影响缩放比例,建议保持 1000px 宽以获得最佳显示效果。

制作建议

字符皮肤

字符皮肤用文本字符拼出音符,占用的资源极小,且能与游戏的「增强音符渲染」发光效果配合。你只需要在文件夹里写一份 note.json。

note.json 结构

json
{
  "name": "MySkin",
  "normal":   { "prefix": "|", "fill": "-", "core": "+", "suffix": "|", "color": "#00FFFF" },
  "hold":     { "prefix": "[", "fill": "=", "core": "*", "suffix": "]", "bodyChar": "|",
                "endPrefix": "[", "endFill": "=", "endCore": "o", "endSuffix": "]",
                "color": "#FF66CC", "enhancedColor": "#FF66CC", "enhancedBodyColor": "#FF66CC80" },
  "exploit":  { "prefix": "|", "fill": "-", "core": "x", "suffix": "|", "color": "#FFFF66" },
  "payload":  { "fill": ".", "core": "@", "color": "#66FF66" }
}

样式字段表

字段适用样式说明
prefix全部音符最左侧的前缀字符(如 `\、[`)
fill全部填充字符,按轨道宽度自适应重复填满中间区域
core全部中心核心字符(如 +、*、@),支持多字符
suffix全部音符最右侧的后缀字符(如 `\、]`)
bodyCharhold主体竖线字符,从起点到终点在两侧重复绘制
endPrefixhold终点段的前缀字符(缺省复用 prefix)
endFillhold终点段的填充字符(缺省复用 fill)
endCorehold终点段的中心字符
endSuffixhold终点段的后缀字符(缺省复用 suffix)
color全部默认颜色,格式 #RRGGBB 或 #RRGGBBAA(带透明度)
enhancedColor全部开启「增强音符渲染」时的替代颜色
enhancedBodyColorhold增强渲染时主体竖线的颜色(建议带透明度)

颜色只支持 #RRGGBB(不透明)与 #RRGGBBAA(前两位为透明度,如 #FF66CC80 即 80% 不透明)两种格式。

默认皮肤字符参考

内置默认皮肤使用的字符样式,可作为设计起点:

音符外观默认颜色增强颜色
Normal┼──◆──┼白—
Hold 起点╠══+══╣白蓝 (80,140,255)
Hold 主体║白半透明蓝
Hold 终点╠══#══╣白蓝
Exploit┼──▶◀─┼白—
Payload···●···白橙 (255,140,20)

字符建议使用 ASCII 或常见线框字符(| - + * x @ o [ ] = .)。避免使用 emoji 或生僻符号(如 、、◉)——它们在部分字体下会显示为方框。

自定义音符类型的皮肤

自定义音符类型(由模组注册,例如 laser、slide)默认复用其行为分类对应的外观槽位。想让它们拥有专属外观,只需在皮肤中按「类型 key」补充条目——类型 key 与谱面 JSON 中 note.type 字符串完全一致(区分大小写)。

图片皮肤:在皮肤文件夹内额外放一张 <类型key>.png。例如自定义类型 laser 就放 laser.png,原有的 4 张标准图保持不变:

text
data/respack/MyImageSkin/
├── normal.png      ← 4 张标准图(缺一不可)
├── hold.png
├── exploit.png
├── payload.png
└── laser.png       ← 自定义类型 "laser" 的专属图片(任意尺寸,按轨道宽度等比缩放)

字符皮肤:在 note.json 中增加 custom 对象,每个 key 对应一个样式对象(字段与 normal / hold 等一致):

json
{
  "name": "MySkin",
  "normal":  { "prefix": "|", "fill": "-", "core": "+", "suffix": "|", "color": "#00FFFF" },
  "hold":    { "prefix": "[", "fill": "=", "core": "*", "suffix": "]", "color": "#FF66CC" },
  "exploit": { "prefix": "|", "fill": "-", "core": "x", "suffix": "|", "color": "#FFFF66" },
  "payload": { "fill": ".", "core": "@", "color": "#66FF66" },
  "custom": {
    "laser": { "prefix": "<", "fill": "=", "core": "*", "suffix": ">", "color": "#00FF88" },
    "slide": { "prefix": "(", "fill": "-", "core": "~", "suffix": ")", "color": "#88CCFF" }
  }
}

规则:

在游戏中应用皮肤

  1. 把皮肤文件夹放入 data/respack/。
  2. 启动游戏,进入 主菜单 → MAINTENANCE → visual_res.pkg(VISUAL RESOURCE PACK)。
  3. 列表中会显示所有皮肤:DEFAULT 置顶,其余按名称排序;每项展示名称、类型(图片/字符)与状态(已应用 / 正常 / 不规范 / 加载错误)。
  4. 选中皮肤按 ENTER 预览,确认效果后再次按 ENTER 应用。
  5. 选择会持久化到 userdata/settings.json 的 skin 字段,下次启动保持生效。

皮肤限制

谱面

谱面是完整的可玩关卡文件,包含音乐引用、节拍与全部音符。一个 JSON 文件对应一首谱面。

制作谱面

游戏内置谱面编辑器:进入 主菜单 → [04] EDITOR,新建谱面并放置音符、设置 BPM 与音乐偏移。编辑器保存的谱面直接输出到 data/charts/ 目录。

导入谱面

别人制作好的谱面是一个 JSON 文件。导入步骤:

  1. 把 .json 谱面文件放入 data/charts/ 目录。
  2. 把谱面引用的音乐文件放入 data/audio/ 目录(谱面 audio.file 字段写文件名)。
  3. 进入游戏的文件夹谱面选择界面,谱面会按 metadata.folder 字段分组显示。

注意 folder_index.json 是章节索引文件,不要改名或删除;游戏会把以 folder_index 开头的文件排除出自由游玩列表。

谱面 JSON 结构

json
{
  "version": 1,
  "metadata": {
    "title": "太陽風。",
    "artist": "Unknown",
    "charter": "Editor",
    "illustrator": "Unknown",
    "difficulty": "Easy",
    "level": "1",
    "folder": "uncategorized"
  },
  "audio": {
    "file": "太陽風。.mp3",
    "offset": 0,
    "previewTime": 30000
  },
  "lanes": 4,
  "scrollSpeed": 600.0,
  "timingPoints": [ { "time": 0, "bpm": 155.0 } ],
  "notes": [],
  "markers": [],
  "disorderMarkers": [],
  "extraContent": null
}
字段说明
version谱面格式版本号
metadata曲名、作者、谱师、难度、等级,以及分组用的 folder
audio.file音乐文件名,相对于 data/audio/
audio.offset音乐偏移(毫秒)
audio.previewTime选曲试听起点(毫秒)
lanes轨道数
scrollSpeed下落速度
timingPoints节拍点(时间 + BPM)
notes音符数组
markers / disorderMarkers演出标记 / 乱序标记
extraContent谱面扩展数据(供模组的谱面扩展段消费)

最简单的导入方式是先用编辑器做一首空谱面,再参照它的 JSON 结构修改。

官方章节谱面

data/charts/offical_charts/ 存放章节模式使用的内置谱面,通过 folder_index.json 建立索引。普通自定义谱面不需要放在这里。

谱面热重载

data/resources.json 中声明了资源目录:charts 与 audio 目录默认开启运行期检测(watch: true),放入新谱面或音乐文件后会触发重新加载;其余资源替换后需重启游戏。

剧情文件

剧情由两部分组成:故事登记(data/story.json,决定故事何时触发、解锁什么)与文本内容(data/sp_lang/story/,决定显示什么文字)。

导入别人做的剧情

若对方提供的是剧情文本文件(.json),把它复制到 data/sp_lang/story/ 目录即可:

text
data/sp_lang/story/
├── awakening_story_zh.json
├── daily_001_first_step_zh.json
└── ...

文件命名规则:<故事ID>_<语言>.json,语言为 zh 或 en。

文本文件格式

剧情文本是一个字符串键值对文件,每个键的格式为 story.<故事ID>.t<序号>:

json
{
  "story.daily_001_first_step.t0": "> 日志缓存 // 第一步",
  "story.daily_001_first_step.t2": "Aria走出飞船。靴子踩在碎石和野草上,发出细碎的声响。",
  "story.daily_001_first_step.t9": "「这真的是地球吗?」"
}

新建一个故事

仅放文本文件不会被触发——故事的触发条件登记在 data/story.json 的 stories 数组中,主要字段:

字段说明
id故事 ID,必须与文本文件名一致
type类型(如 locked、daily)
sequence排序
showAfterUnlock章节进度达到多少后出现
showAfterStory前置故事 ID
unlockSector / unlockCollect通关后解锁的扇区 / 收藏品
nextScene结束后进入的场景
bgText标题背景文字

界面文本

data/lang/zh.json 与 data/lang/en.json 存放全部界面文字(按钮、菜单、提示),同样为键值对。data/sp_lang/tips/<语言>_menu_tips.json 存放加载界面的提示语。

收藏品

data/collect/ 下每个 JSON 文件对应一个收藏品(图鉴条目):

json
{
  "id": "collect_welcome",
  "name": "WELCOME_LOGO",
  "source": "System Default",
  "brief": "终端启动时显示的标志。",
  "unlockedByDefault": true,
  "rewardLogs": 0,
  "detail": "Theseus TERMINAL .1.0\n\n启动标志。"
}
字段说明
id唯一标识(建议以 collect_ 开头)
name显示名称
source来源说明
brief列表中的简述
unlockedByDefault是否默认解锁
rewardLogs解锁奖励的日志数量
detail详情长文本(\n 换行)

音效与音乐

替换已有音频

直接替换同名文件即可,无需改配置:

支持 wav / ogg / mp3 格式。

新增音频

需要编辑注册配置 data/audio_config.json,在 sounds 或 bgm 数组中追加条目:

json
{
  "id": "my_sound",
  "path": "data/sound_effects/my_sound.wav",
  "type": "sound",
  "usage": "ui_effect",
  "format": "wav",
  "preload": true,
  "volume": 0.7
}
字段说明
id音频 ID(模组 / 场景按此引用)
path文件路径
typesound(音效)或 music(音乐)
usage用途(hit_effect 判定音效 / ui_effect 界面音效 / background 背景乐)
format文件格式
preload是否在加载动画阶段预载
volume音量 0.0 ~ 1.0

谱面音乐的基础路径在 audio_config.json 的 chart_music_base_path(默认 data/audio/)。

曲名显示

data/bgm_metadata.json 为 BGM 提供显示名,格式为 "曲名": "作者说明"。

窗口图标

data/ths.ico 是游戏窗口图标。把同名的 .ico 文件替换掉即可。

模组

mods/(工作目录根,与 data/ 同级)存放可加载的模组(Java jar 或 THS 脚本)。模组还可拥有自己的运行时数据目录 data/mods/<模组ID>/,用 ModContext 读写——两者用途不同:mods/ 存放可执行的模组本体,data/mods/<模组ID>/ 存放该模组的配置与数据。新放入的模组默认禁用,需在模组列表中手动启用。这属于进阶内容,见 快速上手 与 THS 简易语言。

打击特效图片(模组)

判定命中时的视觉反馈(打击特效)图片由模组提供,经 ResourceManager.loadImage 加载,无需配置 JSON:

项目规格
存放位置模组数据目录 data/mods/<模组ID>/ 或 jar 内 resources(随模组分发)
格式PNG,保留透明通道
尺寸无固定分辨率限制;绘制大小 = 图片分辨率 × scale(模组代码指定)
锚点以判定点为中心:水平居中于轨道中心、垂直居中于判定线
播放非循环播完停最后一帧;循环则重复;整体透明度随判定强度(0~1)淡出
精灵图一张图按网格切分(每格 cellWidth × cellHeight),从左到右、从上到下拆帧;不满一格的多余像素被忽略

制作方法与可复制示例见 API 参考 · HitEffectStyle 与 HitEffectStyles。

通用注意事项