news 2026/10/14 1:42:07

氛围音乐与音景设计实战:从程序化生成到游戏接入

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
氛围音乐与音景设计实战:从程序化生成到游戏接入

最近在搭建一个名为“至主脚下的大世界·氛围音乐·【大天堂·超神堂的一角卌】”的沉浸式音景示例项目。看到这个标题时,很多人第一反应是“这更像世界观设定”,而不是“一个技术项目”。但实际上,从音频工程、游戏资源打包、程序化生成再到混音导出,它几乎覆盖了一个完整的多媒体技术闭环。

这篇文章会把这套标题拆成可以落地的技术方案:如何设计“神圣感”的氛围音乐,如何生成和加工环境音色,如何把音频资产接入交互场景,以及如何排查最常见的音频项目问题。无论你是想学习音频编程、做游戏世界搭建,还是单纯好奇“氛围音乐项目怎么做”,都可以跟着操作一遍。


1. 背景与核心概念

1.1 “大天堂·超神堂的一角”在音频叙事中的定位

先解释一下标题里的关键词。

“大天堂”“超神堂”可以理解为一个宏大的虚构场景,强调的是空间尺度与神圣感;“一角”则暗示这次并不需要制作完整的世界配乐,只需要选取其中一个局部空间进行氛围构建;“卌”在这里是视觉符号,代表“四十”或“多次重复”,放在工程里可以理解为“多层级、多循环的声音层次”。

从音频角度拆解,这个项目要做的事情是:

  1. 为一个局部空间设计环境底噪与背景音乐;
  2. 让音乐听起来有“宏大建筑内部”的混响与余韵;
  3. 使用循环音频、随机触发和程序化合成,让声音不会显得机械重复;
  4. 按可交付标准导出文件,方便接入游戏引擎或虚拟场景。

所以这不是传统意义上的“写一首歌”,而是设计一套“空间声音系统”。声音在这里承担了两个任务:一是让听众感知空间大小,二是传递场景情绪。

1.2 氛围音乐与音景设计的基础概念

氛围音乐(Ambient Music)最早可以追溯到 20 世纪 70 年代,Brian Eno 提出它应该“像色彩和温度一样存在于环境中”。它通常没有强烈的主旋律,而是以长音、循环纹理、残余混响和缓慢变化为主要特征。

音景(Soundscape)则更强调“环境声音的组合”,比如风声、脚步声、远处钟声、人群低语、水滴声。氛围音乐可以看作音景的一个特殊分支:它既有环境声音,又带有音乐化的组织和情绪导向。

回到项目里:

  • 底噪层:教堂或殿堂内低频空气声、极轻的空调/共鸣声;
  • 空间层:空旷中庭的脚步声回声、远处低频钟声;
  • 音乐层:长音和弦 Pad、轻微吟唱采样、钢琴高音区残响;
  • 点缀层:单次出现的鸟鸣、铃铛、金属摩擦或“未知声响”。

这种分层设计是氛围音景的核心。普通音乐讲究节奏对齐与旋律线条,而氛围音景讲究“层次不冲突”和“空间可信”。

1.3 为什么开发者也适合研究这个主题

很多后端或游戏开发同学会觉得“音频项目太主观,不如接口规范好写”。但现实中,游戏项目、虚拟展厅、低代码交互页面都需要音频落地。音频技术涉及文件格式、编码参数、触发逻辑、性能优化和数据管理,这些都很“工程化”。

学习一个虚构标题下的氛围音景项目,你能掌握:

  • 能自己写脚本批量生成和转换音频;
  • 能理解 WAV、OGG、MP3、AAC 的适用场景;
  • 能在 Minecraft 资源包或 Unity 场景中配置可循环播放的声音事件;
  • 能掌握混音中最关键的频段和音量关系;
  • 能建立一套适合个人项目的音频资产命名与版本管理方式。

所以这篇文章不是“艺术鉴赏”,而是一篇工程味更浓的实战拆解。


2. 整体方案设计

2.1 核心体验目标

在动手制作素材前,先写清楚体验目标,否则很容易做出“虽然每个音色都很好听,但拼起来很怪”的结果。

我给这个示例定义的核心体验目标是:

听众进入“超神堂一角”时,先听到空旷的低频空气声,随后远处的钟声唤醒对巨大空间的想象,长音和弦缓慢铺开,偶尔有一些不确定的细碎声响从角落传来。整体音量不大,但有很强的“纵向高度感”。

围绕这个目标,不做快节奏强旋律的音乐,不添加节奏鼓组,不使用过于明亮的音色,避免有大动态的突然爆音。

建议项目一开始就建立一个简单的“情绪关键词表”,例如:

关键词体现方式
神圣大混响、长尾音、慢启音 Pad
巨大低频延伸、延迟回声、弱回声尾部
安静整体音量压低、动态范围控制在 20dB 内
未知随机出现的高频单音、金属泛音
循环所有背景层做无缝循环

这份关键词表在后期的混音和音色选择中会非常有用。

2.2 声音分区与空间结构:点、床、角

为了便于工程管理,可以按“点、床、角”三个概念划分音频内容:

  • 点(Spot):有明显位置感的一次性声音,比如铃铛、水滴、脚步、远方门扉开合。
  • 床(Bed):持续存在的底层声音,比如混响空气声、低频 Pad、循环风噪。
  • 角(Corner):偶发且不可预测的声音,用来制造“角落里有什么在动”的暗示。

这种划分有两个好处。第一,在配置声音触发逻辑时思路更清晰;第二,在调整混音时可以直接按类别处理,避免同类型素材互相打架。

2.3 音频资产的清单与命名规范

音频项目最怕文件命名混乱。这个项目建议使用以下命名方式:

音节名_层级_类型_情绪序号.wav 示例: heaven_bed_air_03.wav heaven_spot_bell_01.wav heaven_corner_metal_07.wav

命名中的“层级”对应点、床、角,这样扫描目录时能快速判断文件职责。同时,所有源工程文件(Audacity 工程或 Reaper 工程)与导出文件分开存放。

推荐目录结构:

audio-project/ ├── assets/ │ ├── bed/ │ ├── spot/ │ └── corner/ ├── export/ │ ├── ogg/ │ └── wav/ ├── scripts/ ├── mcpack/ # 资源包目录 └── unity/ # Unity 工程脚本目录

当文件数量超过二十个时,这样的结构能节省大量整理时间。


3. 环境准备与工具链

3.1 工具与版本说明

本文示例没有绑定特定操作系统,以下工具都是跨平台主流选择:

  • Python 3.8+:用于程序化生成基础音色;
  • numpy:生成音频波形;如果环境允许,可以安装 scipy 用于写 WAV 文件;
  • Audacity:免费音频编辑软件,负责手工剪辑与加工;
  • ffmpeg:批量转码,输出 OGG/MP3 格式;
  • 文本编辑器:用于修改资源包 JSON 配置。

如果你不想安装 Python 环境,也可以直接用 Audacity 的生成功能完成本文大部分实验。Python 脚本更多是展示“可重复生成”的思路,并非唯一方案。

版本需要根据你的项目实际情况调整,本文示例以常见环境为例,重点演示配置思路。不要盲目追求最新版本,稳定能跑通即可。

3.2 建立项目目录

在命令行中执行:

mkdir -p audio-project/{assets/bed,assets/spot,assets/corner,export/wav,export/ogg,scripts,mcpack,unity}

执行后确认目录存在:

find audio-project -type d | sort

预期输出:

audio-project audio-project/assets audio-project/assets/bed audio-project/assets/corner audio-project/assets/spot audio-project/export audio-project/export/ogg audio-project/export/wav audio-project/mcpack audio-project/scripts audio-project/unity

为了便于后续自动打包,建议把每个声音素材的“干声”(未加混响)和“湿声”(已加混响)分开。本文会在 assets 下再细分一层dry与wet,但基础目录不强制要求。

3.3 音频规格基线

在生成和处理素材前,先统一技术参数,这样可以避免最终混音时出现音量不一致、格式不兼容等问题。

本项目选择以下基线:

  • 采样率:44100 Hz;
  • 位深:16 bit 或 24 bit;
  • 声道:导出主文件使用双声道;纯环境底层可以使用单声道;
  • 响度目标:平均响度控制在 -23 LUFS 到 -18 LUFS 之间;
  • 单段音频时长:循环底噪建议 30 秒到 60 秒,一次性声音控制在 10 秒内。

统一规格后,在不同软件之间交接文件会顺畅很多。


4. 氛围音乐素材的生成与制作

4.1 用 Python 生成基础 Pad 音色

先从最可控的部分开始:用 Python 合成一个长音 Pad。

Pad 音色的特征是“多个频率同时发声、音量缓慢上升和下降”,适合做“超神堂”的底层音床。这里用 numpy 生成两个正弦波,再加上缓慢的包络。

# 文件路径:audio-project/scripts/gen_pad.py import numpy as np import wave sample_rate = 44100 duration = 30.0 t = np.linspace(0, duration, int(sample_rate * duration), endpoint=False) # 两个基础频率:低频与中低频 freq_a = 82.41 # E2 混合 freq_b = 164.81 # E3 泛音 wave_a = np.sin(2 * np.pi * freq_a * t) wave_b = 0.4 * np.sin(2 * np.pi * freq_b * t) # 淡入淡出包络:避免首尾突然出现/消失 fade_in = int(sample_rate * 5) fade_out = int(sample_rate * 8) envelope = np.ones(duration) envelope[:fade_in] = np.linspace(0, 1, fade_in) envelope[-fade_out:] = np.linspace(1, 0, fade_out) # 归一化到 0.7 峰值,防止削波 signal = (wave_a + wave_b) * envelope signal = signal / np.max(np.abs(signal)) * 0.7 data = (signal * 32767).astype(np.int16) with wave.open("audio-project/assets/bed/dry_pad_e2_e3.wav", "wb") as wf: wf.setnchannels(1) wf.setsampwidth(2) wf.setframerate(sample_rate) wf.writeframes(data.tobytes())

运行脚本:

cd audio-project python scripts/gen_pad.py

运行后会生成一个 30 秒的长音文件。这个文件的听感会比较单调,但它可以作为“基底”放进 Audacity 添加混响与延迟。

4.2 用 Audacity 手工制作“神圣感”转场

单纯的正弦波听起来像测试信号,不像建筑内部声音。我们需要在 Audacity 中进一步加工。

操作步骤:

  1. 导入dry_pad_e2_e3.wav;
  2. 使用“混响”效果,房间大小调到 80% 以上,湿声比例适当提高;
  3. 使用“低通滤波”,滤除 8kHz 以上的高频,营造暗调空间感;
  4. 复制一层,将第二层延迟 0.3 秒并把音量降低 6dB,产生轻微空间回声;
  5. 最后用“包络工具”把整体音量重心往后偏移,让声音像“从远处展开”。

在 Audacity 中做完后导出为audio-project/assets/bed/wet_pad_e2_e3.wav。

为什么这样做?因为“神圣感”通常依赖长混响和缓慢的起音。如果直接使用一个瞬间完整的 Pad,听众会感觉声音贴在耳边。通过减弱起音并强化尾音,可以让声音产生“来自大堂中央”的距离感。

4.3 把“一角”做成动态层次

“一角”这个字眼提示我们不应只做单纯的底层铺底。整个声音场景需要动态变化,否则循环 5 分钟后就容易疲劳。

可以制作以下层次:

  • 背景床:湿 Pad 音量保持 -24dB 左右;
  • 中景层:加入缓慢变化的噪声纹理,比如经过滤波的白噪声;
  • 前景点:加入钟声和铃铛,间隔 20 秒到 40 秒随机出现;
  • 角声:不定期加入金属摩擦声或高音泛音。

在 Python 中进一步生成一个“钟声”素材,思路是“衰减很快的长音”,类似敲击金属后的余韵。

# 文件路径:audio-project/scripts/gen_bell.py import numpy as np import wave sample_rate = 44100 duration = 6.0 t = np.linspace(0, duration, int(sample_rate * duration), endpoint=False) # 钟声不是单一正弦,而是多个泛音 base = np.sin(2 * np.pi * 523.25 * t) # C5 p1 = 0.5 * np.sin(2 * np.pi * 1046.50 * t) # C6 p2 = 0.25 * np.sin(2 * np.pi * 1567.98 * t) # G6 signal = (base + p1 + p2) * np.exp(-2.5 * t) signal = signal / np.max(np.abs(signal)) * 0.6 data = (signal * 32767).astype(np.int16) with wave.open("audio-project/assets/spot/dry_bell_c5.wav", "wb") as wf: wf.setnchannels(1) wf.setsampwidth(2) wf.setframerate(sample_rate) wf.writeframes(data.tobytes())

这个钟声要比普通铃声更暗,因为它本来就应该从远处传来。你可以继续加混响和低通滤波,得到wet_bell_c5.wav。

用同样的思路,可以生成:

  • 鸟鸣声纹理;
  • 低频空气声;
  • 缓慢滑动的音高。

每一次生成都建议保留脚本,方便之后调整参数再生成。


5. 程序化与交互:接入资源包

5.1 Minecraft 资源包的声音配置

氛围音景项目最常见的落地方式是做成 Minecraft 资源包,让声音在游戏场景中自动循环。

在资源包中,声音事件定义在sounds.json中。我们先创建一个简单配置:

{ "heaven.bed": { "sounds": [ { "name": "ambient/heaven/bed/wet_pad_e2_e3", "volume": 0.7, "pitch": 1.0 } ], "event": "ambient", "maxDistance": 128, "minDistance": 1 }, "heaven.spot.bell": { "sounds": [ { "name": "ambient/heaven/spot/wet_bell_c5", "volume": 0.5 } ], "event": "ambient" } }

对应的资源包音频文件路径为:

mcpack/assets/minecraft/sounds/ambient/heaven/bed/wet_pad_e2_e3.ogg mcpack/assets/minecraft/sounds/ambient/heaven/spot/wet_bell_c5.ogg

注意:Minecraft 资源包通常优先使用 OGG 格式。你需要把 WAV 转成 OGG 后再放入资源包。

5.2 用 ffmpeg 批量转换格式

在导出目录中准备好 WAV 文件后,可以使用 ffmpeg 进行一次批量转换:

cd audio-project/export/wav for f in *.wav; do ffmpeg -i "$f" -c:a libvorbis -qscale:a 5 "../ogg/${f%.wav}.ogg" done

这行命令会遍历当前目录所有 WAV 文件,并用 libvorbis 编码器输出为 OGG 文件。qscale:a 5表示中等质量,适合环境音循环使用。

文件量较大时,建议只转换真正需要打包的素材,避免无意义的编码损耗。

5.3 Unity 环境中的播放控制器

如果你不是做 Minecraft 资源包,而是想在 Unity 中搭建“超神堂的一角”互动场景,可以写一个简单的 C# 播放控制器。

思路是:

  • 场景进入时播放背景床;
  • 通过 InvokeRepeating 随机触发点声;
  • 通过 AudioSource 的 3D 定位让“角声”有方位感。

以下是核心脚本示例:

// 文件路径:audio-project/unity/AmbientController.cs using UnityEngine; public class AmbientController : MonoBehaviour { public AudioSource bedSource; public AudioSource pointSource; public AudioClip[] spotClips; public float minInterval = 15f; public float maxInterval = 40f; void Start() { bedSource.loop = true; bedSource.Play(); ScheduleNextSpot(); } void ScheduleNextSpot() { float waitTime = Random.Range(minInterval, maxInterval); Invoke(nameof(PlaySpot), waitTime); } void PlaySpot() { if (spotClips.Length == 0) return; int index = Random.Range(0, spotClips.Length); pointSource.PlayOneShot(spotClips[index], 0.5f); ScheduleNextSpot(); } }

这段脚本的核心逻辑是:用一个可循环的背景声源负责“床”,用另一个声源随机播报“点”事件。通过随机间隔避免声音变得机械。

在 Inspector 面板中,需要把背景音频拖到bedSource,把点声音频数组拖到spotClips。


6. 混音、导出与性能

6.1 频段分布与音量平衡

混音阶段要解决的核心问题是:多个声层同时播放时,会不会糊成一团?

建议按以下原则处理:

频段负责内容建议
20Hz - 200Hz空气声、低频 Pad控制音量,避免掩盖对话
200Hz - 2kHz中频钟声、人声吟唱、金属音保留清晰度,注意与背景重叠
2kHz - 8kHz铃铛泛音、环境细碎声少量使用,增强“线索感”
8kHz 以上空气声、噪声纹理适当滤除,避免尖锐感

氛围音景的常见错误是每个素材都把音量开到几乎一样大。一个好的起点是:

  • 背景床峰值控制在 -18dBFS 左右;
  • 中景层比背景层低 3-6dB;
  • 前景点缀层峰值可以到 -12dBFS 左右,但必须是短音。

6.2 导出参数与格式转换

导出时不要只导出 MP3。根据使用场景选择格式:

场景推荐格式说明
本地剪辑与后续加工WAV无损耗,方便编辑
游戏资源包OGG体积小,循环点支持好
网页/轻量展示M4A 或窄带 MP3体积进一步压缩
视频剪辑AAC 或 PCM WAV与视频软件兼容性好

每次导出前记得检查采样率。如果源文件是 44100Hz,不要直接导出 22050Hz 还期望音高不变。格式转换可以改变编码,但不应在无意中改变采样率。

6.3 性能优化与内存限制

如果项目要部署到低配置场景,比如网页端或手机端,音频性能会成为明显瓶颈。

几个优化方向:

  • 循环文件尽量拼接为无缝循环文件,而不是让游戏引擎循环播放 5 分钟长文件;这可以减少加载和解码压力;
  • 使用单声道文件作为背景床,双声道文件用于前景点声;单声道文件体积小,GPU/CPU 开销更低;
  • 避免短时间多次播放同一点声,建议每个声源至少间隔 100 毫秒再复用;
  • 在 Unity 中关闭暂时不使用的 AudioSource 组件,而不是挂载大量空组件。

性能优化的首要原则是:减少同时激活的声音数量。氛围项目很容易叠加十几条音轨,但绝大多数平台并不需要那么多同时发声。


7. 常见问题与排查思路

问题现象常见原因解决思路
播放时首尾有爆音音频素材首尾不是零值在编辑软件中加 10-30ms 淡入淡出
循环点有咔哒声循环不无缝在 Audacity 中对齐波形,重新导出无缝文件
游戏内没有声音OGG 文件命名或路径错误检查资源包路径是否与 sounds.json 对应
声音明显失真导出音量过高查看峰值是否接近 0dBFS,降低音量后重新导出
背景声音太吵床层和中景层音量失衡按 -18dBFS 与 -12dBFS 的基准重新平衡
播放延迟明显一次性加载了大文件把长音频拆段或改用流式加载
手机端掉帧同时播放音频数量过多合并音轨、降低声道数、缩短时长

排查时建议按“文件路径 → 资源加载 → 播放逻辑 → 混音输出”的顺序逐层检查。很多问题并不是音频本身坏了,而是资源没有正确加载。

再看一个典型场景:你配置了sounds.json,但游戏里什么声音都听不到。可以先确认sounds.json中的name是否与 OGG 文件相对路径一致。Minecraft 资源包中,name不填写.ogg后缀,它会自动在assets/minecraft/sounds/目录下寻找。

如果用浏览器测试网页版音景,常见问题则是音频上下文未解锁。绝大多数浏览器要求用户先点击页面,再调用AudioContext.resume(),否则音频默认不会播放。这个行为是安全设计,而不是代码 bug。


8. 工程最佳实践

8.1 版本管理与素材备份

音频项目的文件体积通常比代码大,不适合直接全部塞进 Git。推荐做法是:

  • 代码脚本、配置文件、JSON 配置进入 Git 仓库;
  • 大体积音频源文件放入外部硬盘或网盘同步工具;
  • 使用DVC或 Git LFS 时,只跟踪导出音频,不推荐把所有原始录音都入库。

即使是一个人的个人项目,也要养成“至少两份备份”的习惯。音频工程文件一旦丢失,重建代价非常高。

8.2 自动化检查清单

发布前可以写一个简单的检查脚本,帮助检查文件是否满足格式与响度要求:

cd audio-project/export/ogg for f in *.ogg; do echo "== $f ==" ffprobe -hide_banner "$f" 2>&1 | grep -E "Stream|Duration" done

ffprobe会输出每个文件的编码信息,你可以快速发现采样率不一致或声道数异常的文件。

更严格的团队可以设置响度检查。例如使用 ffmpeg 的loudnorm滤镜查看每个文件的响度值,但要注意:响度归一化不应该在母带导出前直接应用,因为不同层叠声音需要保留动态差异。

8.3 创作与协作建议

如果这不是个人实验,而是团队项目,建议建立“听觉描述文档”。因为音频文件无法通过代码 Review 直观检查,只能靠耳朵判断。

描述文档可以简单记录:

  • 每个声音的意图;
  • 音量参考值;
  • 大致长度;
  • 循环方式;
  • 在不同设备上的听感差异。

这样当美术或策划提出“这个声音不对”时,能快速定位问题出在素材本身、混音,还是播放逻辑。


9. 把项目做完之后

跑完整个流程后,你手上应该有一批可用的氛围音频素材:一个带混响的 Pad 底层、一个钟声点素材,以及一套能随机触发的播放控制脚本。把这些素材放进 Minecraft 资源包或者 Unity 场景,就能得到一个“超神堂一角”的初步声音体验。

后续可以继续尝试的方向包括:

  • 用 Python 生成更多可复用的低频空气纹理;
  • 在 Audacity 中制作真正的无缝循环文件,并对比循环点处理前后的差异;
  • 为自己的资源包写一个自动校验脚本,检查 JSON 路径与 OGG 文件是否匹配;
  • 尝试用 Reaper 或 Ardour 做更复杂的多轨混音。

音频项目最有趣的地方是:同一个素材,换一套混响参数,或者调整 0.5 秒的延迟时间,听感就会完全不同。建议你从现在开始为自己的场景建立一套“可复用的氛围音景生成模板”,之后再做类似项目时,只需要替换素材和空间参数,就能快速产出新内容。

如果这篇文章中的某个步骤对你有帮助,可以把它收藏下来,当作一个音频工程排错清单使用。祝你顺利搭建出自己心里的那座“超神堂”。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/14 1:42:04

Modbus从站站点设计实战:寄存器地图与RTU协议栈要点

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/14 1:41:52

HTML表单与表格实战:从图书登记到成绩统计的完整拆解

聊到HTML入门作业,十个里至少有八个跟表单和表格有关。最近帮人看前端基础作业,收到两份很典型的要求:一份是“图书信息登记页面”,表单录数据、表格列清单;另一份是“学生成绩登记与统计页面”,表单收成绩…

作者头像 李华
网站建设 2026/10/14 1:41:49

零硬件入门STM32:Keil仿真跑通GPIO、定时器与串口

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/14 1:41:48

推荐系统增量重训练新思路:序列元学习如何实现快速适应

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/14 1:41:47

本地优先的AI资讯筛选浏览器插件:从采集到通知的完整开发实战

1. 项目起因与整体思路:被 AI 信息洪水淹没之后的“反叛”我每天打开电脑的第一件事,不再是写代码,而是刷各类 AI 资讯:公众号推送、技术社区热帖、模型发布公告、开源项目的 release note,还有各种榜单和复盘。刷了半…

作者头像 李华
网站建设 2026/10/14 1:41:31

QCC300x左右声道合并输出:单喇叭音频调试全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华