<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>幻之空 · 阿幻的个人空间</title><description>游戏策划 · 二次元 · 独立游戏开发者 — 阿幻的博客 RSS 订阅</description><link>https://huannan.top/</link><language>zh-CN</language><item><title>从零到一设计一套肉鸽地图系统：谁来管节奏？</title><link>https://huannan.top/blog/zhihu-map-system-rhythm/</link><guid isPermaLink="true">https://huannan.top/blog/zhihu-map-system-rhythm/</guid><description>这篇文章讲的是《中国式求职》地图系统的设计过程。为什么地图要管节奏、结构和规则如何分工，以及单系统的设计边界在哪里。不涉及代码实现和数值细节。</description><pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate><content:encoded>这篇文章讲的是《中国式求职》地图系统的设计过程。

**为什么地图要管节奏、结构和规则如何分工，以及单系统的设计边界在哪里。** 不涉及代码实现和数值细节，这些可以在策划案完整版中查看。

地图系统也是这个项目迭代最多的模块，前后大约优化了十几次。

---

## 为什么地图系统要管节奏

肉鸽的单局体验质量，很大程度上由什么时候遇到什么来决定的。

战斗的密度分配，资源获取的时机，强度校验点的出现位置——这些直接决定了玩家这一局是有决策空间还是一直搏打，是节奏流畅还是中途卡死。

在这个项目里，承担什么时候遇到什么的系统也就是地图。

卡牌系统更多管的是&quot;遇到之后怎么打&quot;的问题，经济系统管的是&quot;资源如何分配&quot;的问题，而&quot;这一层是战斗，是商店，还是精英&quot;——这件事情只有地图在管。所以地图不去管节奏，也就没有其它系统来管了。

**这个判断不是所有项目都成立的。方向定型之前，先看了一个走不同方向的案例。**

尸姬之梦——它使用了单线地图的设计，把地图的设计深度压制到了最低，设计资源全部投入到角色池和装备系统上。

这并非是偷懒——它的角色池与装备系统之间的耦合已经提供了足够的构筑决策深度，地图不需要再承担节奏控制的职责。它不是不需要节奏控制，而是这个职责被其他系统承接了。

我的项目里其他系统承接不了这个职责——卡池规模有限，角色和遗物的耦合深度也远远到不了尸姬之梦的水平。所以地图必须自己管节奏，但不需要管所有事情。

---

## 节点图而不是单线

确定了地图要管节奏之后，下一个问题是用什么形态来管。

单线地图首先被排除。单线意味着每局体验高度相似，需要大量的角色和道具内容来支撑重玩性——尸姬之梦能用单线跑通，就是因为它的角色池和装备池体量足够大。

排除单线之后，要提供局间差异就需要分叉路径。分叉路径在肉鸽里最成熟的形式就是节点图。

所以方向也就确定了：做节点图，地图必须管节奏，但不需要管所有事情。

---

## 纯随机为什么行不通

做地图系统的时候，第一版想做全随机——希望玩家每次进入地图玩的都不一样。结果问题非常多：路径重叠、节点生成不合理，比如前三层就出现了休息和商店。说实话这很不合理，所以改成了固定层＋中间随机的结构。

中间随机的部分也踩了不少坑。没加约束规则之前，某些种子生成了连续五层战斗，自己测试打到第三层就烦了——跟玩杀戮尖塔那种游戏的感觉一模一样，我做的游戏起码不要让自己都玩不下去，后续就加了很多约束规则。

关键在于我怎么试图解决它。尝试了三个方向，都没有走通。

第一反应就是去加规则。

商店不可以出现在前三层，战斗不可以连续出现在三层以上，精英不可以出现在前五层……每发现一个不合理的结果就补一条规则。

规则是越写越多，写到后面就开始交叉冲突了。&quot;非战斗节点每三层至少一次&quot;和&quot;商店不能出现在前三层&quot;同时生效，第三层也就被锁住了——只能放事件或休息。

但是休息在前三层也是无意义的，因为玩家血量几乎是满的，最终只能放事件。改了一条又和另一条冲突，规则之间相互纠缠，越补越乱。

到这里就应该停下来了。我写了很多条规则，每条单独看都有道理，但它们合在一起产生了一个我没有设计过的硬性约束。

**这说明规则之间已经不是独立运作的了——它们在互相叠加中产生了我预期之外的耦合效应。**

回过头看这些规则，每条规则的背后都有一个关于节奏的设计意图：

&quot;商店不能出现在前三层&quot;——目的是让玩家开局先通过战斗环节来补充经济和补充卡组。

&quot;战斗不能连续超过三层&quot;——目的是保证玩家有非战斗节点可以调整卡组或者补充状态。

问题出在这里：这些意图描述的是&quot;开局阶段应该是什么体验&quot;、&quot;中期应该有什么节奏&quot;——这是每一局都一样的结构性需求。我一直在用规则间接表达这些节奏意图，而不是直接用结构来实现。**规则的本职是在结构框架内部控制随机细节，不是替代结构去定义节奏骨架。**

分工应该是这样的：结构固定住关键的节奏点——开局如何进入状态、中期强度校验、Boss前调整窗口。这些每一局都是一样的节奏骨架，所以适合用结构。

固定层之间的随机区域——具体生成什么、用什么比例、同类节点连续出现如何处理——这些细节层面的控制，才是规则该管的事情。

**规则失控的根源在于我把本应该由结构处理的问题交给了规则——也就是让规则去干了结构的活。**

---

## 结构和规则的分工

固定层锁定了每局都是一样的节奏骨架：前两层战斗让玩家进入状态，第三层的事件为玩家提供了第一个非战斗决策点，60%位置的精英是BOSS前的强度校验，BOSS前的休息/商店是最后的调整窗口。每个固定层的设计意图都是在回答同一个问题——这个阶段玩家应该经历什么体验，而这个答案每一局都是一样的，所以用结构来保证。

这个方向和杀戮尖塔是一致的——尖塔也是用固定的层结构来兜底节奏，随机性在结构框架内部运行。

区别在于细节。尖塔的固定层是区间约束——精英出现在某一段层数的范围内。我的固定层是精确到具体层——第 6 层就是精英。粒度必须更细的原因就是前面说的：池子小。尖塔的卡牌和遗物池规模足够大，即使随机区域生成了不理想的节点分布，玩家也大概率能从奖励中拼凑出一条可用的构筑路径——大池子保底了随机区域的体验下限。所以结构约束可以粗一些，给随机性留更大空间。

我的项目没有这个保底。一次不好的随机地图生成没办法靠拿到一个强力遗物来补偿，单局 30-50 分钟重开成本也大。所以结构约束必须更紧，精确到层才能把节奏锁住。

**固定层之间的随机区域，规则来管。最终保留四条规则：**

1. 同类节点连续出现两层后第三层禁止
2. 生成过程中实时追踪战斗与非战斗比例，偏离区间时动态调整权重
3. 同层三个以上节点时战斗类和非战斗类各至少一个
4. 精英和商店每章有数量上限

这些规则与之前失控的那十几条有本质区别：它们不再试图去表达节奏的意图，而是在结构已经搭好的骨架内控制随机区域的生成质量。

规则虽然少了很多，但不是因为砍了规则，是因为大部分是在干结构的工作，结构到位了也就失去了意义。

---

## 精英位置参数：60%

精英的设计目的是 BOSS 前的强度校验——让玩家用自己下组出的牌面对高强度对手，校验结果反映的是构筑缺陷：什么牌卡手，什么牌无用。它的位置参数同时被前后两端约束。

他的位置参数也很关键，同时被前后两端约束：前端是意味着卡组的成熟度，后端是恢复和调整空间。

- **50%（第一章第 5 层）：前端空间不足。** 前两层固定战斗，第三层固定事件，真正能自由构筑的空间只有第 3-5 层之间。两三层的构筑窗口期，卡组成熟度不稳定：废牌没机会剔，辅助牌不够支撑核心卡牌运转。精英打出来的结果更多反映的是&quot;牌运好不好&quot;而不是&quot;构筑思路对不对&quot;，校验的信息质量低。
- **70%（第一章第 7 层）：后端空间不足。** 精英和 Boss 之间只剩三层。半血以下的状态，三层里面如果没有恰好生成休息或商店，玩家到 Boss 门口基本是半放弃心态——一旦出现这种心态，后续的游玩体验也就可有可无了。
- **60%（第一章第 6 层）：前后约束的交叉点。** 多出的一层不管生成什么节点，都是一次卡组走向成熟的机会。后端缓冲空间也够用——经过精英校验后玩家对卡组的认知更清晰，有足够空间做针对性调整，后续的非战斗环节才真正有意义。

**参数不是拍脑袋填的，是从前后约束的交叉点里找到的。**

---

## 单系统的边界在哪里

节奏稳定后就要开始做路线的差异化。

四条预置路线：精英路线、战斗路线、事件路线、均衡路线。路线之间设了汇合节点方便中途切换。测试发现路线体感趋同——选哪条好像都差不多。

第一反应就是去调整参数。尝试了三个方向，都没有走通。

调整精英的数量——所有路线都经过精英固定层，精英路线放两个已经是第一章卡组的承受上限，再多打不过，或者打过了收益碾压其他路线。

给精英路线加附加奖励——精英掉落本身就高，附加奖励一加其他路线没了存在意义。调低又退化成装饰，有吸引力但不碾压的参数区间几乎不存在。

调整精英的难度——精英是跨路线的共享参数，动了难度四条路线全部受影响。

**三次调整做的是同一件事：在现有节点结构不变的前提下，试图通过参数拉开差异。**

回头看精英这个节点类型的参数空间——奖励高不能降、难度是共享参数不能单独调、数量受卡组承受力约束。三个维度全部锁死。

问题不在于参数没调好，而是在于精英作为一个节点类型，留给参数调整的空间天然就是狭窄的。

**参数和规则都调不动，不一定是问题无解，可能是承载参数的结构本身需要重新设计。**

换个思路：如果精英不是只有一种呢？

引入&quot;小精英&quot;加入这个结构——难度和掉落都低于精英，但高于普通战斗。

这个时候就不是在调整参数，也不是增加规则，而是新增一个节点类型，改变的是结构本身。

小精英奖励中等，这个时候附加奖励的可调整空间也就被撑开了——原本的窄缝，现在就变成一个可操作的区间了。精英路线可以放更多的小精英来调整与其他路线的体感差异，也不会因为单次掉落太高而被碾压。

这个方向在原型中还没有做过验证，但结构重设计的逻辑我认为是成立的：精英作为节点类型，奖励不能降，难度是共享参数，数量受卡组承受力约束，三个维度全部锁死，参数空间天然就窄。引入新的节点类型是撑开参数空间的方式——而不是在窄缝里继续找精确值。

但结构设计也有边界。小精英能撑开精英路线的差异化空间，其他路线呢？事件路线要做出差异化体感，取决于事件池的设计深度；战斗路线要有独特的成长曲线，取决于卡牌掉落和经济系统的收益结构。这些不是地图系统自己能解决的。

**当参数在一个系统内怎么调都找不到合理区间时，应该检查的是承载参数的结构是否需要重新设计。**

单系统内部能通过结构重设计解决一部分问题，但路线差异化的完整解法，最终涉及的是系统之间的耦合设计，可以参考我的另一篇文章《独立开发一款类杀戮尖塔游戏，我是如何设计16个系统之间的耦合的》。

---

## 回顾

三条方法论，以及它们在其他设计场景里的映射：

### 1. 了解结构和规则的分工

在每局都一样的节奏骨架中——每局一致的节奏骨架——开局进入状态、中期强度校验、Boss前调整窗口——用结构固定。固定层之间的生成细节——节点比例、连续出现限制、数量上限——用规则控制。规则失控时，先检查是不是在让规则做结构的活。

这个判断在实际项目里会反复出现。策划案里很多&quot;加个限制条件&quot;的本能反应，本质上是在回避结构层面的重新设计。

同样的逻辑适用于开放世界的区域解锁：主线推进节奏和区域开放顺序是结构，每个区域内的任务密度和奖励分配是规则。

如果玩家反馈&quot;新区域一开就被支线淹没、不知道该干什么&quot;，第一反应往往是加规则——限制当前地区可接取的任务数量。但如果真正的问题是区域开放时机和主线叙事节奏不匹配，限制任务数只是在用规则干结构的活。

### 2. 参数从约束的交叉点里找

精英层定在 60% 不是直觉，是确定前端约束和后端约束之后逐位置验证，找到两端都能接受的位置。

MMO 的每日任务奖励衰减曲线是同一个框架。

前端约束是活跃度——衰减太快，玩家每天能获取的有效资源过少，日常体验变成上线打卡然后无事可做，中低活跃玩家直接流失。

后端约束是经济膨胀——衰减太慢，高活跃玩家每天能刷出的资源远超消耗设计，几个版本之后通货溢出、交易行崩盘。衰减曲线的拐点不是拍脑袋定的，是从这两端约束的交叉点里找到的。

### 3. 参数调不动时看结构，结构调不动时看系统间耦合

MOBA 的英雄平衡是同一个框架。

某个英雄数值怎么调都不对，砍伤害没人玩，加伤害就无解，参数反复调但合理区间始终找不到。

问题往往不在数值本身，而在于这个英雄的技能机制只支撑一种有效打法——参数空间天然就是窄的，不管怎么左右微调都跳不出&quot;要么太强要么太弱&quot;的状态。

常见的解法是重做技能机制，让同一个英雄能支撑多种构筑路径，参数空间随之被撑开。不是继续调参数，是新增结构元素把参数空间本身撑开。

但单系统内部的结构重设计也有边界。

游戏王的禁限卡表就是一个结构层面解决不了、最终需要改变系统间耦合关系的案例。

一张禁限表同时服务竞技玩家和收藏向玩家，限制太严休闲玩家的本命卡组直接被砍没了，限制太松环境卡组收敛到两三套。

参数空间窄的原因不是规则没调好，而是两类需求耦合在同一个系统里。

游戏王后来推出不同赛制——段位赛、杯赛——每个赛制有独立的卡池约束，竞技平衡和收藏体验各自对各自的用户负责。

这不是在一个系统内部加结构元素，是把原本耦合在一起的需求拆到不同系统里，改变的是系统间的耦合关系本身。

**好的设计方法论不只在同品类里成立。**

**结构和规则的分工、参数从约束交叉点里找、调不动时退一步看结构，这些思路在肉鸽、开放世界、MMO、卡牌对战里反复出现，只是表现形式不同。**

**系统策划需要跨品类地看设计，因为玩家对节奏、反馈和决策空间的需求是共通的。**</content:encoded></item><item><title>独立开发一款类杀戮尖塔游戏，我是如何设计16个系统之间的耦合的</title><link>https://huannan.top/blog/zhihu-16-systems-coupling/</link><guid isPermaLink="true">https://huannan.top/blog/zhihu-16-systems-coupling/</guid><description>设计出16个系统不是重点，每个人都可以像搭积木一样不断叠加系统，真正定义一款游戏品质的，不是&quot;有多少系统&quot;，而是系统之间如何对话。</description><pubDate>Tue, 07 Apr 2026 00:00:00 GMT</pubDate><content:encoded>我独立开发了一款以中国校招为主题的 Roguelike 卡牌爬塔游戏。设计了90张卡牌，47枚遗物，17个敌人，4种职业，3章爬塔，一共做了16个子系统。

设计出16个系统不是重点。每个人都可以像搭积木一样不断叠加系统，真正定义一款游戏品质的，不是&quot;有多少系统&quot;，而是**系统之间如何对话**。

---

## 系统全景

16个系统分三层，通过统一消息机制通信，互不直接依赖：

**元游戏 / 表现层（6个系统）**
- 地图生成、存档、渐进难度、成就、音频、视觉反馈

**经济 / 内容域（5个系统，5种资源）**
- 卡组、遗物、资源职业、事件、商店

**战斗域（5个系统）**
- 战斗流程、卡牌效果、手牌管理、敌人行为、Boss行为

240个可能耦合对，实际建立41条，耦合密度17.1%。

举个例子就明白了：

**商店 × 敌人行为：** 商店不知道你下一场要不要打谁，敌人也不知道你买了什么。这个耦合，技术上很容易做，但这会让玩家产生&quot;被针对&quot;的体验，进一步削弱构筑的自由度。肉鸽的核心乐趣就是用自己选的牌面对未知局面，而不是跟一个可以读你购物车的 AI 打架。

---

## 案例一：手牌保留制——一条规则如何串起战斗、敌人、Boss三个系统

### 系统设计

杀戮尖塔每回合弃光手牌。我改成了回合结束手牌保留，第一回合抽5张，之后每回合补抽3张，上限10张。

手牌保留在游戏王中是常态，在肉鸽卡牌品类中罕见。补抽3张，第3回合手牌开始溢出，刚好和Boss节奏对上。

### 这条规则如何与其他系统耦合

手牌保留制本身只是战斗规则的改动。但它改变了&quot;手牌空间&quot;的稀缺性，这个变化向其他系统传导：

**→ 与卡牌设计的耦合：** &quot;群发消息：伤害＝手牌数×2&quot;，在弃光制下是弱牌，而在保留制下后期能打18+。同一张卡的强弱，取决于它处在哪套战斗规则下。反过来，虚无卡（回合末自动消耗）从负面标签变成了空间管理工具。手牌保留制重新定义了整个卡牌池的评估体系。

**→ 与敌人设计的耦合：** 弃光制下封印一张牌只影响当回合，保留制下被封的牌一直占手牌空间，两回合不处理手牌就满了。封印从&quot;减少一回合手牌&quot;变成了&quot;持续压缩手牌空间&quot;。13种敌人行为中有6种因此改变了威胁等级。

**→ 与Boss设计的耦合：** Boss2是HR总监，核心机制是封印手牌＋塞诅咒入牌库。这个信息从第一章第一次选牌就开始生效：你要不要拿这张高费combo卡？它单卡强度很高，但如果Boss2封住了你的combo起手件，整条链断掉，这张卡就是死牌占格子。

Boss2还没出现，但它对构筑的约束已经在运作了。

随机Boss制下，玩家的构筑逻辑是&quot;拿当前最强的牌，打到Boss再看怎么应对&quot;。

固定序列下，构筑逻辑变成&quot;每张牌不只要现在能用，还要在三场Boss面前都不成为负担&quot;。选牌从单点最优变成全局博弈。这条耦合不在战斗层，在经济层——Boss通过信息公开，直接介入了商店、事件、卡牌奖励的每一次选择。

**这个Boss的威胁感不是来自它自己的数值设计，而是来自手牌保留制和封印机制的交叉——两个系统链在一起，产生了杀戮尖塔里不存在的&quot;空间窒息感&quot;。**

这是一条精心设计的耦合链：战斗规则（保留制）→ 卡牌评估体系改变 → 敌人威胁类型改变 → Boss设计空间打开。改的是一条规则，影响的是三个系统。

---

## 涌现：耦合不全是好事

测试中出现了两个设计时没预见的策略：

大专玩家用自伤卡积累怒气，配合&quot;越挫越勇&quot;遗物的加成，一回合秒杀了Boss1。

链条是：自伤卡→遗物加怒气→怒气跨战斗保留→怒气转化伤害→Boss1的120HP不够扛。五个系统各自的数值都在合理范围内，但串联后突破了Boss的生存阈值。

类似的情况也出现在本科职业——手牌保留积累低费牌＋连锁遗物＋费用减免，绕过了我预设的核心卡触发路径。

事后我对四个职业做了极限输出估算，发现问题的根源是怒气可以无代价地跨战斗无限积累。解决思路不是砍怒气机制本身——那会破坏大专的核心体验——而是调高精英怪和基础怪物的血量上限，迫使大专玩家在精英关就必须释放怒气来生存，没法把所有怒气囤到Boss战一波带走。

但精英怪血量不是大专的私有参数，四个职业打的是同一批怪。调它，必须沿着每条职业的耦合链追踪连锁影响：

本科是引擎型，回合越多连锁转得越充分，精英战变长对它反而是利好——前提是战斗不拖到手牌空间溢出。保留制下上限10张，本科出牌速度快、抽牌多，大约第5回合开始面临手牌淤积。精英血量的安全区间是&quot;本科引擎能在4-5回合内解决&quot;。

双一流是续航流，造成伤害回血，打得越久回得越多。这条链受影响最小，甚至略微受益。

985是压力最大的一条。985靠高费卡集中爆发，6点精力上限意味着一轮完整输出最多打出两张3费核心卡。精英血量一旦超过985爆发的击杀线，它就必须撑过第二轮——但第二轮精力恢复后手里不一定有足够的高费牌跟上。985对精英血量的容忍上限最低。

最终的血量区间要同时满足两个约束：高到大专必须在精英关释放怒气，低到985不会在精英关系统性卡死。实际调整后这个窗口存在，但不宽裕——这本身就说明耦合链越多，可调参数的安全空间越窄。

调整后大专的&quot;赌狗&quot;体验没变，但决策点从&quot;囤到最后一把梭哈&quot;变成了&quot;每场精英关都要判断这一波该释放多少、事后如何调整&quot;。

---

## 案例二：初始遗物——触发条件画多大的圈

### 系统设计

| 职业 | 初始遗物 | 效果 | 卡牌池协同 |
|------|----------|------|----------|
| 本科·连锁 | 勤能补拙 | 出≥4张牌→抽1＋1精力，可连锁 | 12张低费卡（6张0费），核心卡「超频运转」精力翻倍＋每出1卡抽1 |
| 双一流·续航 | 生生不息 | 造成伤害→恢复20%信心 | 「绝地翻盘」伤害＝已损信心，输出＝回血 |
| 985·爆发 | 名校光环 | 每场+5印象，≥3费卡+3印象 | 14张高费卡，6精力上限，印象≥20/30触发额外奖励 |
| 大专·赌狗 | 越挫越勇 | 受伤→+50%怒气 | 「一战成名」消耗全部怒气×1.5伤害无视甲，受伤从代价变资源 |

**初始遗物的设计核心不是&quot;给什么效果&quot;，而是触发条件画多大的圈、画在哪。圈的大小和位置，决定了这个遗物会和多少个系统产生耦合。**

四个职业的初始遗物对应两种画圈策略：

### 宽条件：制造构筑张力

勤能补拙的触发条件是&quot;出≥4张牌&quot;，不是&quot;出≥4张本科专属卡&quot;。

这个区别直接决定了本科的构筑弹性。如果条件限定本科卡，选牌答案永远确定——本科卡无条件优先，构筑退化成线性堆叠，遗物只是一个自动生效的被动。

条件放宽到&quot;任意牌&quot;之后，通用低费卡也能触发，构筑空间打开了，但选择压力也来了：路上遇到一张强力3费通用卡，拿不拿？拿了单回合出牌量可能凑不到4张，遗物不触发；不拿，放弃了一张绝对强度高的牌。遗物不是自动运转的引擎，是每次选牌都要掂量的方向。

### 窄条件：翻转资源极性

大专的&quot;越挫越勇&quot;条件是&quot;受到伤害&quot;。这个条件很窄——只有一种行为能触发，但窄在一个反直觉的位置上：它把所有职业都想回避的事变成了大专独有的资源入口。

其他职业看到敌人攻击想的是&quot;怎么挡&quot;，大专想的是&quot;这一下能吃多少怒气，够不够在最终BOSS战斗放&quot;。

触发条件的窄本身不重要，窄在&quot;全局负面行为&quot;上才重要——它把受伤从代价翻转成投资，直接改写了这个职业面对同一个战斗局面时的整套决策逻辑。

**但宽窄只是遗物设计的方法。真正体现横向耦合的，是一个遗物的触发条件同时改写了多少个系统的运行逻辑。拿越挫越勇展开：**

**→ 改写战斗系统的决策逻辑：** 其他职业看到敌人蓄力攻击，第一反应是出防御牌挡住。大专看到同样的蓄力，算的是&quot;这一下能吃多少怒气&quot;。同一个敌人行为，在大专的视角下从&quot;需要应对的威胁&quot;变成了&quot;需要计算收益的投资&quot;。遗物没有改动敌人的任何数值，但改写了玩家面对敌人系统时的整套评估。

**→ 改写经济层的选牌标准：** 商店里出现一张自伤卡，对其他职业是垃圾，对大专是核心拼图。同一张卡在不同职业的经济决策中价值完全相反——这个差异不来自卡牌本身的数值，来自遗物的触发条件。遗物跨过了战斗层，直接介入了经济层的每一次选择。

**→ 与手牌保留制交叉产生二阶效应：** 案例一讲过手牌保留制让手牌空间变成稀缺资源。大专要囤怒气就要多吃伤害，多吃伤害意味着要留出手牌位给防御调整——但保留制下手牌空间有限。怒气积累和手牌空间管理互相挤压，这个张力不在越挫越勇的设计里，也不在手牌保留制的设计里，是两个系统碰在一起才产生的。

一个遗物，三条横向耦合链，分别改写了战斗决策、经济选择、和手牌空间管理。

它没有改动任何一张卡的数值，没有改动任何一个敌人的行为，但通过一个触发条件，同时重新定义了三个系统的运行方式——和案例一里手牌保留制做的事情一样，只是案例一是纵向传导，这里是横向辐射。

---

## 案例三：Boss克制矩阵——Boss设计如何与职业系统双向耦合

### 系统设计

| Boss | 核心威胁 | 克制 | 被克 |
|------|----------|------|------|
| CEO · 5回合DDL爆40伤 | 时间压力＋精力递减 | 续航流 | 引擎流/爆发流 |
| HR总监 · 封印+诅咒污染 | 手牌控制＋卡池膨胀 | combo卡组 | 精简卡组 |
| 终面官 · 施压-施压-评估窗口 | 每3回合仅1回合可输出 | 匀速输出 | 爆发/怒气 |

杀戮尖塔每章Boss随机，玩家在构筑阶段无法针对特定Boss。

我做了相反的选择：Boss序列固定，且玩家提前知道。

**固定序列把Boss从&quot;终点检验&quot;变成了&quot;全程约束&quot;：**

玩家知道Boss2是HR总监，核心机制是封印手牌＋塞诅咒入牌库。这个信息从第一章第一次选牌就开始生效：你要不要拿这张高费combo卡？它单卡强度很高，但如果Boss2封住了你的combo起手件，整条链断掉，这张卡就是死牌占格子。

Boss2还没出现，但它对构筑的约束已经在运作了。

随机Boss制下，玩家的构筑逻辑是&quot;拿当前最强的牌，打到Boss再看怎么应对&quot;。

固定序列下，构筑逻辑变成&quot;每张牌不只要现在能用，还要在三场Boss面前都不成为负担&quot;。选牌从单点最优变成全局博弈。这条耦合不在战斗层，在经济层——Boss通过信息公开，直接介入了商店、事件、卡牌奖励的每一次选择。

### 代价是什么？

固定序列的代价很明确——可解性变高，理论最优路径存在。我用职业遗物差异和地图/事件随机来对冲——四个职业面对同一个Boss序列的最优构筑方向完全不同，地图分支保证每局可选牌池不重复。

这是一个有意识的优先级判断：在当前4职业、3Boss的体量下，信息公开带来的全程构筑博弈，比Boss随机带来的重玩不确定性对核心体验的贡献更大。玩家在第一章就开始为第三章的Boss做准备——这条从经济层反向约束战斗层的耦合链，只有在信息公开的前提下才成立。

如果Boss池扩展到每章2-3个候选随机抽取，可以同时保留信息公开的构筑深度和随机性带来的重玩价值。这是内容量上去之后的自然迭代方向，不是当前设计的修补。

同时Boss2的实际难度是多个系统的函数——封印机制、手牌保留制、卡组构成共同决定（案例一已经展开过），单独调任何一个参数都可能连锁失衡。

**这就是耦合的双刃剑。**

---

## 结语

这个项目让我走完了系统设计的全链路——从初始规则到耦合链追踪到涌现后的跨职业调整。下一步的方向很具体：专注打磨一到两个核心系统，把同样的耦合追踪和跨系统调参能力用在更大规模的项目里。</content:encoded></item><item><title>斗魂竞技场海克斯系统耦合拆解——从概率游戏到败者组方案设计</title><link>https://huannan.top/blog/zhihu-arena-hex-coupling/</link><guid isPermaLink="true">https://huannan.top/blog/zhihu-arena-hex-coupling/</guid><description>我是斗魂竞技场之神——60个以上英雄拿过第一名，酷爱勇气的选择。什么英雄都打，什么构筑都试过。这篇从系统耦合角度拆解海克斯系统，并提出败者组方案。</description><pubDate>Mon, 06 Apr 2026 00:00:00 GMT</pubDate><content:encoded>我是斗魂竞技场之神——60个以上英雄拿过第一名，酷爱勇气的选择。什么英雄都打，什么构筑都试过。

正是因为什么都打，所以我比大部分都更清楚一件事情：不同英雄之间的海克斯池差异有多大。有些英雄海克斯池天生友好，不同海克斯之间更容易成型；有些英雄能用的海克斯就没几个，池子还比别人大，构筑成型的难度会高出一截。但这个差异不是个别现象，而是整个海克斯系统结构本身决定的。当构筑无法成型的时候，后期的操作其实也就没了意义——这也会连锁出高段位竞技场的另一个现象。

高段位竞技场一直有个持续存在的体验问题：挂机。——原因之一就是工作室以及陪玩号用脚本刷分，这属于外部因素，抵御的难度比较大，现在脚本进化越来越快，不限于模拟走位，模拟购买装备，模拟战斗，识别难度越来越高。所以比起在现有系统上不断做攻防战，我更多是想增加一个新的系统，去保证遭受这种体验的玩家，感兴趣的可以直接跳到后面，有我的败者组方案探索。

前半篇，我更多还是从另一个角度出发——系统本身的耦合关系，有没有在无意识制造「继续打下去收益不大」的判断条件？

---

## 第一部分：海克斯系统拆解

### 一、海克斯的强度不看品质，看不同海克斯系统之间耦合的网状体系

竞技场的海克斯通常分为银色、金色、彩色三个品质。从直觉上来说，彩色海克斯应该强度远超银色，但实际对局中经常不是这样。

银色海克斯有一类机制性海克斯——比如物理转魔法（物理转魔法，把所有额外攻击力按比例转为法强），同理还有魔法转物理。这类海克斯可以直接改变你的构筑方向和装备的适配范围。

例如一个物理射手拿到了物理转魔法，那么对于他来说法师攻速类型的装备就可以使用了，装备池直接打开了，特别是一些棱彩装备，加成就更大了。反而很多彩色海克斯反而因为过于泛用，丧失了被选择的可能性。毕竟玩家只会想要选择最适配这个英雄的海克斯，而不是去选一个适配大多数射手的海克斯，特别是放在特定的构筑中，他的收益在一定程度上可能还不如一个银色的机制海克斯。

这就意味着海克斯系统的评估维度并不是「这个海克斯是什么品质」，而是「这个海克斯在我当前的海克斯网络里能不能跑起来」。品质只能保证下限，耦合位置决定实际收益。

特别是在前期，其实区别不是很大。第一回合大家只有一个海克斯，没有构筑可言，打的更多是英雄的基础强度和操作水平。但是每多选一个海克斯，「需要什么」就变得更具体——不再是「来个射手海克斯就行」，而是「渴望一个能跟之前海克斯形成联动的特定选项」。到了中后期，耦合成型的队伍和未成型的队伍之间，差距就如天壤。

以 U.GG 的数据为例，卡特琳娜这个英雄在竞技场可以见到大约 129 个海克斯，但选取率前七的海克斯——神射法师、杀戮时间到了、穿针引线、狂徒豪气、珠光护手、涌血、魔法飞弹——这些海克斯占了总选取量的约 70%，其他英雄角色也是类似的比例。

在 190 个海克斯的总池里，对于每个英雄来说「真正想要的」可能就 7-10 个。

同时因为不同英雄的有效海克斯数量非常大。一些机制明确的英雄（例如火男的灼烧，亚索的暴击），包括通吃类型的英雄（例如龙女，马迪尔），有效海克斯集中且容易识别。另外一些英雄机制不那么聚集，池子里能用的海克斯本来就少，构筑成型的难度比别人高出一截。

那么问题来了：7-10 个有效海克斯在一个 46-60 个同品质池子内，三选一能碰到的概率又是多少？

### 二、构建越深，命中率越低——肉鸽三选一的内生属性

海克斯池经过系统过滤后，每个英雄在各品质内看到的子池大约为：银色 49-60 个、黄金 46-57 个、彩色 34-42 个。过滤逻辑做了英雄基础适配——无蓝耗英雄不推蓝耗海克斯、无位移英雄不推位移海克斯——但不追踪玩家的构筑方向。这是一个设计的取舍：如果系统帮你过滤掉所有非当前方向的海克斯，玩家就永远无法去被迫适应意外的选项——「而被迫面对意外」恰恰是肉鸽体验的核心乐趣之一。

海克斯的抽取是超几何分布。设某品质中英雄可见海克斯总数为 Y，其中「对当前构筑有效的」为 X 个，每轮从 Y 中不放回抽取 3 个。命中至少一个有效海克斯的概率是：

P(≥1) = 1 − C(Y−X, 3) / C(Y, 3)

**银色层（Y≈49），假设初始有效 X＝8：**

| 选择轮次 | 剩余有效增幅 | 三选一命中率 |
|----------|----------|----------|
| 第1次 | 7个 | 39.8% |
| 第2次 | 6个 | 35.6% |
| 第3次 | 5个 | 31.0% |
| 第4次 | 4个 | 25.9% |

**棱彩层（Y≈34），假设初始有效 X＝5：**

| 选择轮次 | 剩余有效增幅 | 三选一命中率 |
|----------|----------|----------|
| 第1次 | 5个 | 38.9% |
| 第2次 | 4个 | 33.0% |
| 第3次 | 3个 | 26.3% |
| 第4次 | 2个 | 18.7% |

从 39% 降到 19%，命中率腰斩。这就导致在第 11 回合的最后一次海克斯选择经常觉得「三个选项都不太匹配」——这不是运气差，是构筑越精准，有效的选项越少的数学必然。

再说一层：每个海克斯在单局中最多 4 名玩家能拿。16 人局内热门海克斯被别人先选走了，你的有效池进一步缩小。假设 7 个有效黄金海克斯中有 2 个被「售空」，实际有效数降到 5 个，首轮命中率就从 39.8% 掉到 29.8%。

这个递减结构是肉鸽式随机三选一的内生属性——杀戮尖塔、云顶之弈的海克斯系统都面对同样的数学结构。竞技场的特殊性在于它是 PvP：你的命中率低不只是&quot;这把有挑战&quot;，而是&quot;对面可能命中率高，你在系统层面就落后了&quot;。同一个概率结构，PvE 和 PvP 的体验效应完全不同。

### 三、筛子和经济：纠错资源的分配逻辑

面对命中率递减，系统提供了两层纠错工具。

第一层是筛子（重掷）。V25.05 大清算版本把筛子从「一次刷新全部三个」改成了「单格重掷」（一次只换一个格子），每人开局 4 个，海克斯选择和铁砧共享。4 个筛子要分给 4 次海克斯选择加中间所有铁砧选项，平均下来每次海克斯选择最多用 1 个筛子纠错。每次重掷大约提升 10 个百分点命中率——从约 40% 到 50%。有一定帮助，但不改变递减趋势。属性锻造器的三选一不能用筛子重掷，这部分随机性则完全不可控。

第二层是版本迭代中的资源普惠。大清算版本做了一个很有意义的调整：删掉了旧版的高回报回合（之前第 5、9、13 回合赢了给筛子给回血，输了什么都没有），换成了第 7 和第 10 回合给全体玩家发幸运骰子＋金币＋免费棱彩属性锻造器。从&quot;赢家通吃&quot;到&quot;普惠分配&quot;——这个设计方向说明团队已经意识到资源分配不均的问题并在主动修正。

经济层面，胜利方每回合多拿 250 的击杀金币，8-10 回合后多出 2000-2500 金币，差不多一件传说装备的差价。这个差距对不同英雄的影响不同：例如妮蔻必须买暴击剑和大享，瑞兹、卡萨丁必须买大天使之类的装备，少 2000 金币意味着晚一轮成型。大眼、蚂蚱可以买随机装备（比直接购买指定装备便宜 500 金币），经济压力相对小一些。击杀金币的累积效应是目前正常游玩路径中，胜利方和失败方之间最主要的资源差距来源。

### 四、锻宗：海克斯系统和经济系统交叉产生的涌现玩法

有些玩家的选择更极端：完全不买装备，只买属性锻造器。社区叫&quot;锻宗&quot;。最开始是只有奥恩的彩蛋，后续对所有英雄开放。

锻宗不是纯赌博。碎片分三个品质：银色是基础数值（比如 AP 碎片 15-30 点），金色数值更高（固定 50 点左右），彩色是质变效果——百分比护甲穿透、百分比生命值、每回合 500 金币。前期拿到彩色碎片特别是每回合 500 金币，直接超模。累计 10 个碎片后开始有概率触发碎片增幅（提升所有碎片数值 20-50%），25 个之后保证每次必出。策划也在引导这条路径——勇气凭证送免费属性锻造器，第 7 和第 10 回合送免费棱彩属性锻造器。

锻宗的流行本身是一个值得分析的信号。它是海克斯系统和经济系统之间缝隙产出的涌现玩法——玩家发现属性锻造器的长期叠加收益在特定条件下可以超过装备的即时收益。策划后续通过碎片品质和普惠锻造器回合来引导和平衡这条路径，说明团队把它视为一种可接受的玩法多样性，而不是需要消灭的 Bug。

但是锻宗有一个结构外的外部性问题值得关注：你前期不买装备，战斗力极低，队友在替你抗压力。对于队友来说，你银体的前 8-10 回合和 1V2 的体感非常接近。区别是锻宗是有意为之的构筑决策，挂机是放弃。但对于承受后果的队友来说，这个区别在体验层面不明显。

一个人的构筑决策是由另一个承担代价——这个外部性结构问题和挂机问题在底层逻辑上有相似之处，都涉及 2v2 队伍规模下个体决策对队友体验的放大效应。败者组更多解决的是后者的后果分配；前者需要另一套思路，不在本文讨论范围内。

### 五、操作权重的阶段性转移

回到主线，其实不管走正常路径还是锻宗，海克斯网络的成型过程中，操作和构筑的权重关系正在发生变化。

前期英雄等级低，海克斯少，操作差异是决定性的。走位好，英雄熟练度高就能赢。中期海克斯网络开始分化，彩色海克斯此时效果最大化，特别是耦合好的队伍开始拉开差距。到了后期，网络成型完毕，构建耦合的权重超过操作的权重——同样操作水平的两个玩家，海克斯网络的成型的那个有明显优势。

这种权重转移本身是有设计意义的：它给了竞技场一条完整的节奏曲线——前期靠手法，中期靠决策，后期靠构筑。问题在于，竞技场的操作手感完全继承召唤师峡谷——技能组、伤害公式、走位手感一模一样。这给玩家建立了一个预期：&quot;操作好就能赢。&quot;前期确实如此，但到了中后期产生了「继续打下去收益不大」的判断，不是心态问题，是系统结构的数学必然。这个判断本身不是 bug——它是肉鸽三选一的内生属性，也是局间差异性的来源。但在 2v2 里，当一个人到达这个判断并选择放弃（或者被脚本替代），承受后果的是两个人。败者组不改变这个判断产生的条件，而是给承受后果的那个人一条不依赖队友的出路。

比起动已经跑通的成熟系统，还是外挂一个子系统影响会更小。

---

## 第二部分：败者组方案

### 一、思路

现有的挂机惩罚机制——举报、扣分、封号——但更多作用于对局结束之后，对于你当局的体验没有帮助。

败者组做的是事中补偿。你这局队友出了问题，这一局就提供给你生路。他不阻止挂机的出现，而是让挂机的后果不再由无辜队友全额承担。

### 二、为什么是莫德凯撒

莫德凯撒的故事背景本来就是两度被杀、三度重生，天然契合这个机制。

败者组不是一个冰冷冷的系统弹窗。你在正常比赛中被淘汰了，莫德凯撒会把你拉进他的领域，给你一次从死亡领域杀回来的机会。回到主赛道的那一刻是「我从地狱回来了」。

叙事包装不是装饰——它决定了玩家对这个机制的心理接受程度。「复活赛」听起来像补偿弱者，&quot;从地狱回来&quot;听起来像英雄归来。

### 三、两条进入路径

**路径一：被莫德凯撒选中。** 开局从 16 人中随机选 8 人标记。被标记的玩家如果在正常比赛中被淘汰，会弹出&quot;我不甘心&quot;的选项。再选择进入败者组后，保留完整的构建自由度——海克斯正常三选一、筛子保留、装备自由买。

**路径二：主动臣服。** 没被选中的玩家也可以进入败者组——选择&quot;臣服于莫德凯撒&quot;。代价：丧失所有筛子，装备只能随机买。

不选就正常退出，没有人被强制。

为什么是选择两条路径？被选中是开局随机变量，和海克斯随机、地图随机一样。增加局间差异性——这局你可能被选中，下局可能不是。而臣服是屈底——你是自己低头、接受限制条件换一次机会，而不是系统来安排你。但是在 1V1 战斗结束后，你受到了莫德凯撒的认可，一切恢复正常，这样就可以保证惩罚性体验最小化。

### 四、败者组赛制

1V1 的随机配对淘汰，与主赛制同步滚动进行。被淘汰的玩家陆续加入，仅限后四名，因为胜利规则是前四名才算游戏胜利，所以不用等全部出局。到第五名被淘汰的时候，前面的人已经厮杀结束了。

每轮 1v1 的战斗环境由莫德凯撒的领域决定——地形、buff/debuff、胜利条件可能每轮不同，局间差异性在败者组内部自然产生。养盅结束、两人组队回到主赛道后，一切回归正常 2v2 规则。

最后存活的 2 人组成新队伍，两个不同英雄、不同海克斯、零配合基础——全新的体验。

### 五、回归主赛道

最终的两人以第五名、一滴血回到主赛道，继续正常 2V2。

莫德凯撒战斗加成：这个队伍被莫德凯撒认可——打出更多伤害的同时，也会受到更多伤害。加上一滴血，一旦失败就会立马淘汰出局。

体验质量：赢了一滴血就是英雄时刻。输了就是自己选择的再来一次，然后光荣战死。无论是哪种结局，都比「队友出问题之后陪着一起接受」会好很多。

### 六、过滤机制

**入口过滤：** &quot;我不甘心&quot;和&quot;臣服于莫德凯撒&quot;都需要主动点击。工作室的脚本不会点——目的就是输，不会选择继续打。

**赛制过滤：** 就算脚本进了败者组，1v1 里不操作直接第一轮被淘汰。

**脚本号的影响范围：** 不阻止挂机——但把挂机行为的影响从两个人缩小到一个人。脚本号挂机，队友可以进败者组继续打，不用与他一起陪葬。

---

**+：这是一个方向性的探索。以下是我识别到的核心风险**

**缓解方向：** 败者组内可以给角色补偿性被动——辅助/坦克进入后获得额外外伤害加成，刺客/射手获得额外韧性。叙事上和莫德凯撒的领域一致——&quot;进入死亡领域的人会被改变&quot;。不追求完美平衡，只需要缩小到&quot;有一战之力&quot;的程度，具体数值需要测试。

**败者组内部的局间差异性：** 统一入口保证了公平，但 1v1 阶段如果每局完全相同，重复游玩的新鲜感会下降。可以尝试的方向包括随机地图、不同的胜利条件，以及对战中随机增加的增益，让差异性在败者组内自然产生。具体哪种变化对体验和开发成本的平衡最优，需要原型测试。

**陌生人英雄搭配：** 败者组筛的是 1v1 能力，回到 2v2 需要配合。两个 AP 法师组队，没前排没控制，出去可能直接被秒。英雄搭配完全靠运气。

**莫德凯撒选中的随机性：** 竞技场已经有海克斯随机、地图随机、荣誉赛随机。再加一层&quot;开局选中 8 人&quot;会不会让玩家觉得随机性过多。主动臣服路径保证了所有人都有机会，但被选中和臣服的构建自由度差异确实存在。

**具体数值需要测试：** 1滴血是象征性的还是该给更多？战斗加成打出/受到伤害的倍率是多少？1v1 打几轮合适？这些没有数据支撑，需要实际原型测试。

**败者组不减少挂机率，只减少挂机对无辜队友的伤害。** 这只是降低挂机对玩家造成的伤害，从源头减少挂机需要匹配机制、脚本检测、工作室封禁等方向的系统性工作，不在这个方案的范围内。

---

### 结尾

定位：不修改已经跑通的核心系统，在接受海克斯随机性的前提下，给因队友问题体验受损的玩家一条出路。</content:encoded></item><item><title>从杀戮尖塔到《中国式求职》——卡牌Roguelike地图系统设计复盘</title><link>https://huannan.top/blog/zhihu-map-system-review/</link><guid isPermaLink="true">https://huannan.top/blog/zhihu-map-system-review/</guid><description>我用Godot 4独立开发了一款类杀戮尖塔的卡牌Roguelike《中国式求职》，开发周期一周，作为求职作品集。这篇是地图系统的设计复盘。</description><pubDate>Sun, 05 Apr 2026 00:00:00 GMT</pubDate><content:encoded>我用 Godot 4 独立开发了一款类杀戮尖塔的卡牌 Roguelike《中国式求职》，开发周期一周，作为求职作品集。这篇是地图系统的设计复盘。

我想做的是一个品类启蒙作——让大家都能体验到卡牌肉鸽这个品类的乐趣。

聊到卡牌肉鸽的地图设计，最近玩的《尸姬之梦》真的让人难忘。单线程地图，固定地图，每一关可选择的困难模式，加上二十多个随从还有一百多件装备之间的联动，做出来的效果很让人惊艳。每个随从都有独立的天赋，卡牌还有装备联动效果，随从的天赋还可以与其他随从的天赋之间联动，可组合的 combo 非常多，让大部分玩家可以在里面找到自己喜欢的流派。

第二个就是对于数值的思考，它精准抓住了玩家对于数值爆炸以及电表倒转的喜爱，大量随从可以组合出无限出牌还有无限续航等非常离谱的效果，同时对于后期 Boss 的设计也很精准，类似回合内出牌量上限、反甲的各种克制手段，数值也是给的很慷慨。虽然最终 Boss 基本都是几千万的血条，但是每次当你见靠着自己的 combo，用几十血的随从把上千万的 Boss 打败的爽感，是其他品类所没有的。这也证明卡牌构筑的策略深度不一定要靠地图的复杂性和选择性来支撑，当自身构筑维度本身足够大的时候，地图也可以极度精简。

欢迎大家支持《尸姬之梦》，也算打了小小的广告了，制作人很厉害。

包括品类里其他作品也是，大家的尝试真的百花齐放，各有各的路。我这边用的是杀戮尖塔的节点图方案，在这个基础上做了简化。

---

## 一、连续打牌打到退游戏——约束规则

我自己玩杀戮尖塔的时候，很容易心态崩塌退出游戏。大部分时候是心累——又要计算地图路径最优解，又要计算卡牌的最优出牌组合，打完还要根据血量和卡组战斗力决定下一步怎么走。虽然才玩到第二章，就好像已经看到了这局的结局，瞬间索然无味。而且说实话，本来可以玩游戏的时间就不是很多，很多时候并不想挑战自己，也不想浪费自己为数不多的游戏时间去重复去干一件事情。

做地图系统的时候，第一版想做全随机——希望玩家每次进入地图玩的都不一样。结果问题非常多：路径重叠、节点分布像抽风、比如前三层就出现了休息和商店。说实话这很不合理，所以改成了固定层＋中间随机的结构。

中间随机的部分也踩了不少坑。没加约束规则之前，某些种子生成了连续五层战斗，自己测试打到第三层就烦了——跟玩杀戮尖塔那种游戏的感觉一模一样，我做的游戏起码不要让自己都玩不下去，后续就加了很多约束规则。

约束规则：

1. **连续衰减。** 同类节点连续出现两层后，第三层禁止。连续两层非战斗后强制切回战斗。
2. **战斗密度追踪。** 生成过程中实时追踪战斗与非战斗的比例，偏离时动态调整后续权重。
3. **同层均衡。** 一层三个以上节点时，战斗类和非战斗类各至少一个。
4. **配额上限。** 精英和商店设了每章硬性上限，超上限自动替换。

---

## 二、只是想完整体验一遍——章节独立结局

玩杀戮尖塔很退游戏还有一个原因：我只是想完整地打一遍看看后面是什么。但杀戮尖塔只有打穿三章统一15层才有完整结局，自己也做的人平白打了一局什么都没收获。打完任何一章 Boss 都可以签约止步，获得跨局遗物。

杀戮尖塔是三章统一15层，打穿三章才有完整结局。

本项目改为 10 → 13 → 15 递增，每章有独立结局和签约机制，签约止步获得跨局遗物。

- 第一章10层 ≈ 10分钟：新手快速走完完整流程，老手刷遗产遗物，熟悉新职业卡组运转
- 层数映射求职题材：初创面试两三轮就定了，大厂可能要过五六关

### 章节固定结构

- 第0层 · 第1层：连续两场战斗
- 第2层：全事件
- 第3层 ~ 60%位置：随机区域，四条路线分化，汇合切换
- 约60%位置：精英专层，所有路线必经，不可绕过
- 最后一层：Boss → 签约/继续

### 三章定位

| 章节 | 层数 | 时长 | 定位 |
|------|------|------|------|
| 第一章：初创 | 10 | ~10分钟 | 教学章。基础攻击为主，引入封印手牌等机制 |
| 第二章：小情灵 | 13 | ~15分钟 | 机制复杂化。牌库污染、叠甲、多段攻击组合 |
| 第三章：大厂 | 15 | ~20分钟 | 全面考验。每个敌人有独特机制，Boss有施压/缓压周期 |

---

## 三、摸清最优解就无聊了——四条路线＋汇合节点

杀戮尖塔玩到后面，支持我的就是源源不断的 MOD 角色，玩家的创造力真的很厉害，她们总能想出一些很不错的创意以及组合。不过很多时候玩到后面，还是会变成寻求最优解的数学游戏——这几张核心牌最关键，这么走是最优解。一旦解出答案后，就会索然无味，也就退了。

四条路线也是一开始就定的。与其让玩家在几十个节点里自己算最优路径，不如直接给四条风格明确的选择：

| 路线 | 特点 | 适合时机 |
|------|------|----------|
| 精英路线 | 精英节点多，打赢遗物拿到手软 | 卡组已经成型，想赌高回报的时候 |
| 战斗路线 | 普通战斗密度高，打完能练练 | 卡组还在磨练的时候 |
| 均衡路线 | 各类节点标准分布 | 没什么明确需求，想稳稳推进的时候 |
| 事件路线 | 事件节点比例高，战斗少 | 想通过事件调整卡组或获取资源的时候 |

### 切换点：让选路不变成固定公式

一选到底 → 算出最优路线 → 每局都走同一条 → 决策深度消失

解决方案：多个汇合节点，随时可以切换路线

流程：四条路线 → 汇合节点①（根据卡组状态、信心值、人脉 → 重新选择路线）→ 四条路线 → 汇合节点② → ... → Boss

心流设计：每个汇合点玩家自然完成难度校准 — 挑战强度始终与当前能力匹配

---

## 四、连线规则与自动验证

1. 从任何起点出发都能走到Boss。
2. 线和线不交叉。
3. 不存在到不了的孤岛节点。
4. 中间位置比边缘位置选择多。
5. 每个节点最多三条路汇入。

---

## 五、防流失设计

做了五层递进兜底：

- **第一层：短章节。** 第一章10层约10分钟。
- **第二层：「我不甘心！」重试。** 任何战斗失败后，可以返回战斗前重新挑战。
- **第三层：妈妈的电话。** 信心值偏低时触发，恢复信心＋获得临时遗物。与「我不甘心！」联动：信心值越低触发妈妈电话的概率越高。
- **第四层：精英战失败解锁新职业。** 精英战失败后解锁双一流和985。
- **第五层：跨局遗产遗物。** 签约止步获得永久增益。

说一下&quot;妈妈的电话&quot;。加这个机制有两个原因：一个是怪的强度太高了，不加回复手段玩家撑不住；另一个是自身经历——求职的时候父母的关心，把它具象化成了游戏机制。信心值越低，触发妈妈电话的概率越高。加上特殊的 BGM，其实在玩家濒临死亡的时候，突然收到妈妈的电话，相信会有不错的情感共鸣，可以说是比较满意的系统设计。

### 四种学历和三个Boss之间有机制交互

| Boss | 施压方式 |
|------|----------|
| CEO（第一章） | 倒计时机制，到点造成巨额伤害 |
| HR总监（第二章） | 干扰路，打乱节奏 |
| 终面官（第三章） | 长期持续施压 |

| 学历 | 参考 | 运转逻辑 | 第一章 | 第二章 | 第三章 | 体验曲线 |
|------|------|----------|--------|--------|--------|----------|
| 本科 | 河流 | 依赖出牌链连贯，新谜则崩盘 | 中等 | 困难 | 中等偏困难 | V形低谷 |
| 双一流 | 吸血鬼 | 造成伤害回复信心，边打边续航 | 较轻松 | 中等 | 中等 | 平稳安全 |
| 985 | 陨石 | 前期蓄力，后期单回合高费爆发 | 困难 | 中等 | 较轻松 | 先苦后甜 |
| 大专 | 河豚 | 前期承伤积累怒气，Boss关一次 | 中等 | 较轻 | 困难 | 后期难 |

---

## 六、没解决好的问题

四条路线的个性化不足。自己测试时候发现，走精英路线和走战斗路线，实际体感差别没有预期那么大——加上汇合节点和约束规则的设计，路线之间的差异化还是不足。第一章只有 10 层，路线还没展开就到了精英层了。

目前继续优化，会考虑给每个路线增加专属事件，例如精英路线会概率性刷出免试节点（可以直接跳跃汇合点，并获得大额奖励），同时调整汇合点的位置以及数量。

---

## 小结

| 设计方向 | 杀戮尖塔 | 本项目 |
|----------|----------|--------|
| 选路 | 全节点可见，自由规划 | 四条路线＋汇合节点 |
| 章节 | 三章统一15层，一个结局 | 10→13→15，每章独立结局 |
| 失败 | 永久死亡＋有限缓冲 | 五层防流失递进 |
| 职业 | 四角色面对相同难度曲线 | 学历局Boss机制交互 |

地图系统的设计复盘到这里。后续计划做《斗魂竞技场》的海克斯拆解以及优化目前挂机问题的探索系统方案。有时间的话，还会做一个聊聊我游戏不同系统之间的展示案，毕竟是求职系统策划，游戏做了那么多系统，系统之间如何耦合，这些还是要写出来的。

应届生，第一次写策划案，欢迎严苛批评。</content:encoded></item><item><title>帮一个独立站站长防CC攻击，从评论区聊到凌晨三点</title><link>https://huannan.top/blog/help-cc-defense/</link><guid isPermaLink="true">https://huannan.top/blog/help-cc-defense/</guid><description>在抖音评论区看到有人被CC打，顺手帮了一下，结果搞了三天。记录一下过程。</description><pubDate>Sat, 04 Apr 2026 00:00:00 GMT</pubDate><content:encoded>刷抖音的时候刷到一个做海外独立站的姐们发视频，说被同行CC了，服务器CPU拉满，网站打不开。

评论区一堆人推销CDN，报价3800的有，60的也有，她说自己是外行搞不明白。

我点进去看了一眼她贴的攻击日志——同一个UA都是Chrome/120，带随机参数绕缓存，不同IP在同一秒集中打443。很典型的CC。这种Cloudflare免费计划就能挡，没人提大概是因为免费的没人赚钱。

我在评论区回了一句分析，她主动加了微信，一开始就发了句&quot;谢谢你愿意帮忙&quot;。后来聊着聊着开始叫我老师，我说别别别，就是懂一点而已。

然后就开始了。

&amp;nbsp;

说一下，全程是微信远程指导，我告诉她该操作什么，她自己在服务器上执行。没有远程桌面，没有一对一实操——那种要收费的，我这就是顺手帮忙。

她的情况是WordPress商城站，两台欧洲VPS，宝塔面板管，7个域名。之前没接任何CDN，服务器IP裸奔。

也不是没找过人。淘宝闲鱼花了几百块，人家帮她装了宝塔WAF。但被打的时候CPU半小时降不下来。原因很简单——宝塔WAF是在服务器上拦的，请求到了服务器才开始处理，CPU已经被吃了。相当于人都冲进你家了你才开始查身份证。

&amp;nbsp;

## 第一天

先让她把所有域名接了Cloudflare。改NS、加A记录、开代理，接完之后外面只能看到CF的IP，真实服务器IP藏起来了。SSL证书顺手也解决了，CF源服务器证书有效期15年，不用每三个月续一次Let&apos;s Encrypt。

然后指导她配CF的WAF规则。免费计划有5条自定义规则，够用。把CC攻击常见的特征拦了：查询字符串带 `t=` 的（攻击用的随机参数）、`/xmlrpc.php`（WordPress老漏洞）、`/wp-json/wp/v2/users`（枚举用户名）、`/.env`（扫配置文件），全部Block。速率限制也加了一条，同一IP十秒超100次请求触发质询。

光接CF还不够。她的IP之前暴露过，攻击者可以直接打IP绕过CF。所以让她在服务器上用iptables把80和443端口锁死，只放行CF的IP段，其他全部DROP。

配完本来以为差不多了——CPU从99%降到42%，内存从3.6GB降到420MB。结果第二天她又来消息了。

&amp;nbsp;

## 第二天：换IP翻车

换IP的时候出了问题。

换IP是有操作顺序的：先在新服务器上配好iptables，再去CF改A记录。这样新IP从一开始就是锁死的，DNS传播过程中不会暴露。但她之前不知道这个，直接换了IP就改了A记录，iptables还没配上，新IP就这么裸着挂在DNS上了。对面一直在扫，几乎是立刻就找到了新IP，又开始打。

我后来把完整的操作步骤发给她了，先做什么后做什么写得很清楚。她照着做了一遍，这次没问题。但前面那次等于白换了，多浪费了一天。

不光CC，对面同时还在扫网站源文件里有没有现成的木马可以利用——这是标准的入侵流程，先用CC打瘫你，同时扫后门看能不能进一步渗透拿权限。CF的WAF日志里能看到一堆扫webshell路径的请求，全被拦了。可惜啊，没有。

所以第二天基本都在重新来——指导她重新换IP、重新配iptables、把宝塔面板端口也从默认改成随机5位数防Shodan扫描。

&amp;nbsp;

## 过程中踩的坑

宝塔面板的防火墙白名单，加了CF的IP但没加&quot;其他IP拒绝&quot;，等于白加。最后直接让她用iptables替代了，比在面板里点来点去靠谱。

PHP-FPM的服务名折腾了好一会。宝塔装的PHP服务名是 `php-fpm-74`，不是 `php7.4-fpm`，她那边重启了好几次才对上。远程指导的时候这种小问题最耗时间，我又看不到她的屏幕。

清空iptables规则的时候，默认策略已经是DROP了，`iptables -F` 一执行规则全没了，所有连接直接断开。她跟我说突然连不上了，我当时就知道怎么回事了——先改默认策略再清空，这个我之前说了但她可能漏看了。还好VPS后台有VNC控制台能救回来。

CF的五秒盾也被绕过了。开了Under Attack Mode之后，452万次请求里有442万次穿透到了源站。攻击那边用了无头浏览器能过JS验证。后来换成滑块验证，攻击立刻归零。

换完IP发现新旧IP还在相邻网段，从.166.x变成.167.x，扫两个C段几分钟就找到了。不过iptables配好了，扫到也没用，得不到任何响应。

&amp;nbsp;

## 第三天：稳了

她发消息说网站跑了一整天没掉过一次，CPU稳定在个位数。然后又发了一句&quot;谢谢老师&quot;，我说真别叫老师，我就是一个搞技术的碰巧刷到了你的视频。

三天断断续续搞下来，从评论区随手回了一句，到最后真的把她整套防御体系搭好——CF接入、WAF规则、iptables锁源站、换IP、清木马，全套弄完，看着攻击流量打过来全被挡在外面，成就感是真的有的。中间因为操作顺序的问题多花了一天，但想想人家本来就不是搞技术的，能跟着文字步骤一步步把这些做下来，其实已经很不错了。

&amp;nbsp;

## 几个感受

WAF拦在哪一层差别很大。宝塔WAF在应用层拦，请求已经到了你服务器，CPU已经被吃了。CF在边缘节点拦，请求根本到不了你服务器。

CC和DDoS是两回事。CC是伪装成正常用户的HTTP请求洪水，iptables可以拦。DDoS是网络层流量轰炸，数据包到了网卡内核就要处理，那种只能从上游过滤或者换IP。

源站IP隐藏不是接个CDN就完事的。DNS记录、邮件头、面板端口、SSL证书指纹、DNS切换的窗口期，任何一个环节泄露都白搭。而且对面会一直扫，你换了IP不第一时间锁好，几分钟就被找到。

CF免费计划比大多数人想的强。这次吃了452万次请求和22.78GB攻击流量，一分钱没花。

&amp;nbsp;

技术这个东西本身是免费的，网上资料都有，Cloudflare也不收钱。那些报价几千块的，做的事情其实和我指导她做的一模一样。真正的门槛不是信息差，是遇到问题的时候能不能自己一步一步查、一步一步试。

希望大家保持自学的能力吧。遇到问题先搜，先看文档，实在搞不定再找人。大部分情况比你以为的简单。

有问题可以留言。</content:encoded></item><item><title>二次元入坑记 · 崩坏3篇</title><link>https://huannan.top/blog/honkai3-mobile-game/</link><guid isPermaLink="true">https://huannan.top/blog/honkai3-mobile-game/</guid><description>初中的时候在B站看到说是跨时代的3D二次元游戏，下了之后就再也没放下。琪亚娜、芽衣、姬子小姐……没想到当初那么开心的故事，最后变成那个样子。</description><pubDate>Tue, 10 Mar 2026 00:00:00 GMT</pubDate><content:encoded>这篇聊聊崩坏3。

说起来，这应该算是我的二次元手游入坑作了。

&amp;nbsp;

## B站看到的

初中那会儿刷B站，首页推了个视频，说崩坏3是最好的3D二次元游戏，跨时代的巨作。

我当时想，真有这么夸张吗。

然后就下了。

打开游戏的那一刻，CG直接把我看愣了。那个画面质量，在当时的手游里真没见过。而且不光是画面好，是那种整体的感觉——音乐、镜头、角色的表情，全对上了。崩坏3的CG质感是我后来[做AI美术时的参考之一](/blog/chinese-job-hunting-devlog-6)。

现在回头看，2016年的手游CG做成那样真的离谱。后来学了点影视的东西才反应过来，人家那是按电影的标准在做的。难怪当时觉得跟看动画电影一样。

![崩坏3rd - 震撼的CG画面](/images/content/references/honkai3/cg-01.png)
&lt;!-- VERIFY-IMG --&gt;

&amp;nbsp;

## 天网和大世界

让我印象特别深的是那个开放世界。

应该是天网那块，可以自由探索，到处跑，还能驾驶机甲。初中的时候玩到这个真的觉得很厉害了——一个手机游戏，居然有大世界可以探索？

现在想想，应该算是二次元手游里很早做开放世界的了。虽然后来原神把开放世界做到了完全不同的级别，但崩坏3当时敢在手游上做这个，真的挺猛的。

机甲那段我来来回回玩了好多次，纯粹就是觉得好玩。

![崩坏3rd - 开放世界探索](/images/content/references/honkai3/open-world-01.png)
&lt;!-- VERIFY-IMG --&gt;

&amp;nbsp;

## 活动多到离谱

说崩坏3良心，这个真的不是吹的。

夏日活动、各种节日活动，一年到头基本没停过。而且不是那种敷衍的&quot;打怪攒积分换奖励&quot;，是真的有不同玩法的。推箱子、解谜、跑酷、甚至还有音游之类的。

当时真的觉得离谱，一个手游怎么做得出音游来的。

后来自己做策划了才意识到，这套活动设计真的聪明。打本打腻了去玩个推箱子，推完箱子回来打本又觉得好玩了。

&amp;nbsp;

## 深渊和舰队

每周下深渊那段时间是真的热闹。

我当时加了QQ群，每到深渊最后几个小时，群里就开始疯了。大家互相看排名，讨论配队，还有人专门卡时间点去把别人炸下来。

对，那时候好像有个机制是可以把排名靠前的人打下去的。每次快结算的时候特别刺激，你不知道会不会突然被人挤掉。群里经常有人喊&quot;我又被炸了！&quot;

后来学策划了才明白，这种设计就是故意让你心里没底，你越没底越想盯着看。群里最后半小时集体发疯，大家都被拿捏得死死的，但当时没人觉得有什么，就是好玩。

现在应该改了很多了，但那个时候的氛围真的很好。有种大家一起在玩同一个东西的感觉。

&amp;nbsp;

## 琪亚娜

说到剧情，绕不过去琪亚娜。

一开始觉得她就是个傻乎乎的女孩子，天天嚷嚷着要当英雄，成绩很差，老是闯祸。说是女武神，但看起来更像是在学校混日子。

然后不知道从什么时候开始，故事就变了。

不是说女武神吗。怎么后面变成那样了。

当初那么开心的日常，回头看才发现全是铺垫。

&amp;nbsp;

## 最后一课

姬子小姐那段我不太想细说了。

就是没想到她会就这么离开了。

那个CG叫&quot;最后一课&quot;。我看的时候其实一开始还好，觉得剧情就是剧情嘛。但看到后面，回想起之前那些日常，那些她跟琪亚娜在一起的画面，就突然很难受。

音乐选得也好。不是那种刻意催泪的配乐，就是很安静的曲子，反而更让人受不了。

![崩坏3rd - &quot;最后一课&quot;CG](/images/content/references/honkai3/last-lesson-01.png)
&lt;!-- VERIFY-IMG --&gt;

&amp;nbsp;

## 一路陪伴

从初中玩到现在，好长时间了。平时也就登录做做日常，没觉得自己有多上心。但每次剧情更新完，关掉游戏之后会愣一会儿。

&amp;nbsp;

没想到当初那么开心的故事，最后变成这样。</content:encoded></item><item><title>二次元入坑记 · 轻小说篇</title><link>https://huannan.top/blog/light-novel/</link><guid isPermaLink="true">https://huannan.top/blog/light-novel/</guid><description>高中没有手机的那段日子，三秋缒的三部曲陪我度过。日本轻小说的写法很奇怪，但真的很特别。</description><pubDate>Mon, 09 Mar 2026 00:00:00 GMT</pubDate><content:encoded>高一刚开学那会儿，班里后排有个男生桌子底下永远藏着一本书。封面是那种很典型的日系插画，大眼睛的少女，背景是樱花或者夕阳什么的。我每次路过都会偷瞄一眼，心想这人上课看什么漫画呢。

后来有一天他主动递给我，说你要不要看看。

那是我第一次摸到轻小说。比普通的书轻很多，纸张薄薄的，翻起来哗哗的。封面摸上去滑滑的，有一股新书才有的油墨味。

然后手机就被没收了。

&amp;nbsp;

## 没有手机的日子

高中之后家里不让玩手机了。要考大学嘛，手机是万恶之源，这个道理我懂。

但真到了那一天，还是觉得天塌了。没手机就看不了番，B站也刷不了，跟被切断了和整个二次元世界的连接一样。

头几天确实很难熬。晚自习的时候别人都在刷题，我坐在那发呆，脑子里全是上个月追到一半的番剧情。

后来实在太无聊了，就开始翻班里传来传去的那些书。

![轻小说 - 书店里新书的油墨味](/images/anime/lightnovel.jpg)

&amp;nbsp;

## 东野圭吾

那时候班里流行看推理小说。说是看书，其实就是东野圭吾在全班巡回。「白夜行」「嫌疑人X的献身」「恶意」，一本看完传给下一个人，跟接力赛似的。

我也跟着看了。东野圭吾确实厉害，「嫌疑人X」看到最后那个反转的时候，大晚上的后背直接凉了。合上书愣了好一会儿。

但推理小说看多了之后，总觉得缺点什么。谜底揭开的那一下确实爽，可看完就完了，不会在心里留太久。

我想看点能让我心里发酸的东西。

![东野圭吾代表作 - 推理小说入门](/images/content/references/higashino/cover-01.png)
&lt;!-- VERIFY-IMG --&gt;

&amp;nbsp;

## 偷偷买书

学校旁边有一条老街，拐角有一家很小的书店。门面窄窄的，里面塞得满满当当，书架之间侧着身子才能过去。老板是个不怎么说话的大叔，你进去他也不招呼你，就自己坐在柜台后面看报纸。

我在最里面的角落发现了一排日本轻小说。

三秋缒的三部曲。「恋爱寄生虫」「三日间的幸福」「重启人生」。

站在那翻了几页，心跳就快了。和推理小说完全不一样的东西。那种文字很轻，但是读完心里发酸。

一口气买了三本。出门的时候把书塞在校服里面，走路都不敢走太快，怕掉出来被同学看见。不是怕被笑，就是觉得这个东西是属于自己的秘密，不想跟别人分享。

![三秋缒《三日间的幸福》- 全文情绪高潮的作品](/images/content/references/mikakunin/cover-01.png)
&lt;!-- VERIFY-IMG --&gt;

&amp;nbsp;

## 三日间的幸福

「三日间的幸福」讲的是一个把自己剩余寿命卖掉的人的故事。听起来很中二，但我是在晚自习的时候偷偷看的，看到男主站在天台上那段——他已经把自己的寿命卖得只剩三天了，身边所有人都在正常地活着，只有他知道自己快要消失了。

然后他说了一句大概是：就算是这样，也想和你一起看最后一次日落。

当时我把书合上了。不是因为不好看，是因为太好看了。不是那种嚎啕大哭的感觉，就是心里堵了一下，说不上来哪里难受。

整个晚自习后半段我都没看进去任何东西。

&amp;nbsp;

## 重启人生

「重启人生」更狠。

这本我是周末在家看的。躺在床上翻，说好只看一章就睡觉，结果一直看到凌晨两点多。被窝里开着手电筒——对，没有手机之后我真的开始用手电筒看书了，像小学生一样。

看完之后我盯着天花板想了很久。如果可以重来一次，我会做出不一样的选择吗？

想来想去觉得大概不会。就算重来，我还是那个我，还是会做一样的事，犯一样的错。有点丧，但又觉得安心。

就是这样吧，没有什么如果。

&amp;nbsp;

## 日本轻小说的写法

说实话，日本轻小说的写法真的挺奇怪的。

主语经常省略，一句话能拆成三行写。对话和心理描写混在一起，有时候分不清角色在说话还是在想事情。翻译成中文之后有些句子读起来怪怪的，语序不对劲。

但就是这种奇怪的写法，反而让那些场景特别有画面感。

动漫有配乐有画面帮你烘托气氛，你不用想象，一切都摆在眼前。但轻小说只有文字。你得自己在脑子里把那个场景搭出来——天台的风什么样，夕阳什么颜色，他说那句话的时候声音抖没抖。

全靠你自己补。所以每个人脑子里的画面都不一样。

我到现在还记得「三日间的幸福」里那个天台的画面。风很大，天很红。虽然书里其实没写风大不大，但我脑子里就是这样。

&amp;nbsp;

---

感谢父母愿意给我买实体书。真的。

现在想想还挺神奇的。要是那时候手机没被没收，我大概会继续窝在被子里刷B站追番。那条老街拐角的书店我可能永远不会走进去，三秋缒这个名字我可能永远不会知道。

没有手机的那几年，我反而读了最多的书。晚自习偷偷翻，周末躲被窝里用手电筒照着看，课间趴桌上翻两页。

虽然当时觉得挺惨的。</content:encoded></item><item><title>二次元入坑记 · Galgame篇</title><link>https://huannan.top/blog/anime-highschool/</link><guid isPermaLink="true">https://huannan.top/blog/anime-highschool/</guid><description>从缘之空入坑galgame，到十二神器，到被「你和她和她的恋爱」整破防。</description><pubDate>Sun, 08 Mar 2026 00:00:00 GMT</pubDate><content:encoded>这篇聊聊 galgame。

说起来也是缘之空带我进的这个坑。

&amp;nbsp;

## 缘之空

说实话，最开始去看缘之空，是冲着肉番去的。

小学到初中那段时间嘛，大家懂的。

但没想到的是，真正留住我的是故事本身。

![缘之空 - Galgame入坑作](/images/anime/yosuga-no-sora.jpg)

悠和穹让我羡慕的是，他们知道自己想要什么，然后就去了。不管别人怎么看。现实里面哪有这么容易，我自己就是那种想想就算了的人。

真的好厉害。

&amp;nbsp;

## 入坑 Galgame

后来家里居然愿意让我买缘之空的正版游戏。感谢父母，认真的。

玩到正版 galgame 之后，就打开了另一个世界。

![Galgame - 十二神器推荐帖的回忆](/images/anime/galgame.jpg)

记得当时在网上专门搜了&quot;galgame 十二神器&quot;之类的推荐帖，然后一部一部地玩。那段时间沉迷得不行，每天放学回来就打开电脑继续推剧情。十二神器里面好几部我都是那时候通关的，Fate、沙耶之歌、Clannad……有的玩到半夜不舍得存档退出，第二天上课就开始犯困。

galgame 和看动漫不一样，你得自己点选项。选错了走到 bad end，那是你自己手贱点的，怪不了别人。所以被刀到的时候特别疼。

这种感觉很特别。

&amp;nbsp;

## 沙耶之歌

十二神器里面，沙耶之歌是我玩的比较早的一部。

怎么说呢，就是那种玩完之后要缓好久的作品。

郁纪出了事故之后，整个世界在他眼里都变了。街道、房子、身边的人，全都变成了血肉模糊的东西，到处都是恶心的肉块和黏液。只有沙耶，在他眼里是一个正常的、漂亮的少女。

但问题在于，从外面的人来看，沙耶才是那个怪物。

说白了就是——在一个所有人都无法理解你的世界里，只有一个人愿意陪在你身边。你是选择回到&quot;正常&quot;的世界，还是留在只有她的世界里。

你会怎么选？

这个问题我想了很久。三个结局我都打了，没有一个让人好受的。特别是沙耶开花的那个结局，整个人类都被改变了，但郁纪和沙耶反而得到了某种意义上的圆满。你说这算好结局还是坏结局，我到现在都说不清。

![沙耶之歌 - 独特画风的视觉冲击](/images/content/references/saya-no-uta/cover-01.png)
&lt;!-- VERIFY-IMG --&gt;

&amp;nbsp;

## 你和她和她的恋爱

然后玩到了这部。

对我的冲击比沙耶之歌还大。

![你和她和她的恋爱 - 打破第四面墙的META体验](/images/anime/galgame.jpg)

里面有 META 的剧情，就是角色会突破第四面墙那种。她直接叫出了你存档文件夹里的名字——不是游戏主角的名字，是你电脑用户名。然后她问你，你是真的喜欢她，还是只是因为觉得这条线的剧情比较好才选了她。

当她质问我，玩家本人，你到底了解我多少——我直接不知道该点什么了。

然后就开始想，我之前那些存档读档到底算什么。说难听点就是把人当工具人攻略了。

虽然她是虚拟角色吧，但被这么一问，是真的心虚。

后来我[自己做游戏写剧情](/blog/chinese-job-hunting-design-2)的时候，这段体验影响挺大的。

&amp;nbsp;

---

galgame 就是这样，选项是你自己点的，没法怪别人。

BGM 的话，博客里现在放的就是缘之空的「記憶」。也说不清为什么能单曲循环那么多遍，就是每次听到会愣一下，什么都不想，就那么听着。

头像是穹妹。没什么好说的。</content:encoded></item><item><title>二次元入坑记 · 凉宫春日篇</title><link>https://huannan.top/blog/anime-chusan/</link><guid isPermaLink="true">https://huannan.top/blog/anime-chusan/</guid><description>初三压力大到不行的时候，凉宫春日和GOD KNOWS拉了我一把。这首歌对我来说不只是一首歌。</description><pubDate>Sat, 07 Mar 2026 00:00:00 GMT</pubDate><content:encoded>这篇单独拿出来写，因为凉宫春日对我来说不只是一部番。

&amp;nbsp;

## 初三

初三的时候压力真的好大。

父母天天跟我说，现在是五五分流，以前还能花钱买分上高中，现在不行了。我的成绩又很一般，说实话那段时间我自己也不知道该怎么办。

追的番剧都看完了，在家也没什么事干。

![初三 - 压力最大的那段日子](/images/anime/exam.jpg)

那时候就很好奇，其他学生到底是怎么过日子的。然后不知道搜了什么关键词，搜到了凉宫春日的忧郁。好像有人说这是校园番的开创者之类的，我就去看了。

&amp;nbsp;

## 漫无止境的八月

不过先说一个搞笑的事。

我提前下好了全集准备大晚上熬夜追完，泡面都泡好了。结果我下的是漫无止境的八月。

就是同一集反复播了八遍那个。

![凉宫春日的忧郁 - 漫无止境的八月名场面](/images/anime/haruhi.jpg)

前两集没反应过来，看第三集的时候觉得不对，到第五集我开始翻文件名确认自己没下错，到第八集泡面已经凉透了。

那种感觉怎么形容呢，大概就是你满怀期待打开一盒巧克力，发现里面装了八块一模一样的。而且你还认真吃完了。

后来才知道这是京阿尼的&quot;实验性叙事&quot;。好吧，算你厉害。

不过凉宫春日本身还是好看的。长门看书的样子很治愈。

&amp;nbsp;

## GOD KNOWS

但我真正想说的是这首歌。

文化祭那段。春日上台唱 GOD KNOWS。

自己看吧，放在这里了（2199条真实B站弹幕，可以调大小和密度）：

import DanmakuPlayer from &apos;../../components/DanmakuPlayer.astro&apos;;

&lt;DanmakuPlayer videoSrc=&quot;/video/god-knows.mp4&quot; danmakuSrc=&quot;/video/god-knows-danmaku.json&quot; /&gt;

&amp;nbsp;

这首歌后来我听了很多很多遍。

也不知道怎么形容。就是生活里撑不下去的时候，深夜一个人待着很难受的时候，会打开来听。听完也不是说马上就好了，但就是觉得，嗯，还能再撑一下。

&amp;nbsp;

## 两百多分到五百多分

看完凉宫春日之后，也说不上来怎么回事，就突然想学了。

初三最后一个月，我从两百多分考到了五百多分。

父母都震惊了。老师也是，问我为什么不早点学，说要是早半年开始努力不知道能考多好。

我也不知道该怎么回答。总不能说是因为看了一部动漫里的一首歌吧。

但就是这样的。一首歌，撑过了一个月。

后来碰到另一个过不去的坎的时候也做了类似的事——[把求职焦虑做成了一个游戏](/games)。大概是学会了，难受的时候就找点什么抓着。</content:encoded></item><item><title>二次元入坑记 · 追番篇</title><link>https://huannan.top/blog/my-favorite-anime/</link><guid isPermaLink="true">https://huannan.top/blog/my-favorite-anime/</guid><description>从B站被积分吓退，到天降之物一看就走不掉。追番这件事，一旦开始就停不下来了。</description><pubDate>Fri, 06 Mar 2026 00:00:00 GMT</pubDate><content:encoded>突然想写写自己和二次元的事。也不是什么总结，就是最近翻手机看到一些老番的截图，想到了以前的很多事。

&amp;nbsp;

## 下载B站

那时候身边的人都说看番去B站，我就跑去应用商店下了一个。

结果刚打开，弹出来的是什么&quot;下载APP送积分&quot;，不同APP给的积分还不一样。搞得跟做任务似的。

当时就被吓退了，直接卸载了。

![B站APP - 二次元追番的起点](/images/anime/bilibili.jpg)

过了一段时间才重新下回来，发现好多了。不过第一次下B站被积分劝退这个事，现在想起来还是挺好笑的。

&amp;nbsp;

## 天降之物

入坑番是天降之物。

那时候家里就有手机了，感谢父母的开明。在手机上看番，也不懂什么画质不画质的，能看字幕就行。

一开始是在其他番剧的评论区看到有人推荐的，说很好看。就去搜了一下，点开第一集。

然后就走不掉了。

![天降之物 - 入坑的第一部番](/images/anime/sora-no-otoshimono.jpg)

就是那种一看就停不下来的感觉。前一秒还在笑，后一秒就被戳到了。智树这人平时就是个废柴色胚，满脑子只想着和平与胖次，但伊卡洛斯真出事的时候他是真上啊。这种人最要命了。

后来番剧没出完，动画公司破产了。

当时挺难过的。不过也因为这个，跑去追了漫画。第一次为了一部作品去追原作，就是因为天降之物。那时候真的很喜欢这个故事，也很羡慕里面那些角色的日常。虽然知道是虚构的，但就是会想，要是自己的生活也能那么有趣就好了。

&amp;nbsp;

## 约会大作战

后来有一天刷B站首页，推荐了一部叫「约会大作战」的番。

看了一眼封面，想着这是啥，就点进去了。

结果又陷进去了。

![约会大作战 - 十香登场的那一幕](/images/anime/date-a-live.jpg)

十香是真的猛，被全世界追杀还能笑着吃烤肉。而且她对士道那个信任感，完全不讲道理的那种，看着看着就觉得这人也太好了吧。

后来动画公司也破产了。

我就纳闷了，为什么我看的番剧，制作公司都破产。

再后来，约战要出最终季了。我知道这个消息的时候，反而不敢看了。因为我知道，看了之后，这个故事就真的结束了。以后再也不会有新的剧情了。

![约会大作战最终季 - 不敢看的结局](/images/anime/datealive-final.jpg)

所以就一直拖着没看。就好像只要不看，故事就不会结束一样。

我知道这个想法挺傻的。但就是没办法。大概很多追番的人都有过这种感觉吧。

&amp;nbsp;

## 未闻花名

未闻花名。这个没什么好铺垫的，直接说。

这个真的没办法装了。最后那集看完我直接把脸埋枕头里了。面码她明明什么都没做错。

![未闻花名 - 面码与仁太的夏天](/images/anime/anohana.jpg)

后来我发现自己有个毛病，就是每次看到动人的故事都忍不住流泪。一开始觉得有点不好意思，后来就无所谓了，反正又没人看见。

那首 secret base，建议不要在公共场合听。之前在地铁上随机到了，差点当场社死。

&amp;nbsp;

## 只有神知道的世界

这部番小时候看的时候，有个地方一直没想通。桂马最后为什么选了那个人。

![只有神知道的世界 - 桂马的选择](/images/anime/kaminomi.jpg)

后来自己也碰到过类似的事。不是什么大事，就是在不知道怎么办的时候，发现有些人会留下来，有些人不会。那个时候突然想起桂马的选择，就懂了。

就是会留下来的那个人。想通了之后反而觉得，也没什么好纠结的。

也不是什么顿悟，就是到了那个时候自己就知道了。

&amp;nbsp;

---

写到这里才发现已经写了这么多。手机里那些老番的截图还存着，偶尔翻到会愣一下。</content:encoded></item><item><title>建站日志 #3：暗色模式、导航栏、搜索和那些琐碎的事</title><link>https://huannan.top/blog/site-building-3/</link><guid isPermaLink="true">https://huannan.top/blog/site-building-3/</guid><description>暗色模式在View Transitions下丢失、导航栏透明导致文字重叠、Material Icons字体加载失败显示英文……小功能大坑。</description><pubDate>Thu, 05 Mar 2026 00:00:00 GMT</pubDate><content:encoded>音乐播放器写了1400行之后，我觉得剩下的东西应该都是小活了吧。暗色模式？加个class。导航栏？固定在顶部。搜索？弹个框。结果没有一个真正&quot;随便搞搞就行&quot;。

&amp;nbsp;

## 暗色模式的闪屏

![暗色模式 - 深色主题首页效果](/images/content/blog-screenshots/dark-mode-homepage.png)

暗色模式做起来不难——Tailwind 加个 `dark:` 前缀就行。配合 `localStorage` 记住用户的选择，配合时间判断自动切换（18:00-6:00 暗色）。

但跟 View Transitions 结合的时候就出问题了。

Astro 的 ClientRouter 在页面导航时会做一件事：把新页面的 `&lt;html&gt;` 属性替换到当前页面。而新页面是服务端渲染的，`&lt;html&gt;` 标签上没有 `class=&quot;dark&quot;`——因为暗色模式是客户端 JavaScript 加上去的。

所以每次导航，暗色模式就丢了。页面会闪一下白色，然后 JavaScript 执行后才恢复暗色。

修复方法是监听 `astro:after-swap` 事件——这个事件在 DOM 替换后、页面渲染前触发。在这个时机从 `localStorage` 读取主题设置，立刻把 `dark` class 加回去。这样就不会有闪屏了。

这个 bug 找了很久才定位到。因为表现是&quot;有时候暗色模式正常，有时候不正常&quot;——取决于你是直接打开页面（正常）还是通过导航跳转（不正常）。这种时灵时不灵的 bug 是最折磨人的。

&amp;nbsp;

## 导航栏透明的问题

一开始导航栏用的是毛玻璃效果——半透明背景 + `backdrop-filter: blur()`。

看起来很好看对吧？问题是往下滚动的时候，文章内容会从导航栏下方透出来。特别是标题之类的大字，跟导航栏的文字叠在一起，根本看不清。

后来改成了实底背景——`bg-white/95`，几乎不透明但还保留一点点通透感。同时加了下滑自动隐藏、上滑显示的逻辑。

自动隐藏的实现也有坑。要检测滚动方向（当前 scrollY 跟上次比较），设阈值防抖（不然轻微抖动就会反复显示隐藏），还要处理顶部区域（滚动到最顶部时必须显示导航栏）。

进度条也要跟着导航栏一起动——导航栏隐藏时进度条也要跟着往上走。两个元素的 `transform: translateY()` 要同步。

导航栏总算消停了。然后打开手机一看——

&amp;nbsp;

## Material Icons 翻车

![移动端 - 底部导航栏和响应式布局](/images/content/blog-screenshots/mobile-homepage.png)

一开始用 Google 的 Material Symbols 图标字体。在 HTML 里写 `&lt;span class=&quot;material-symbols-outlined&quot;&gt;home&lt;/span&gt;`，字体加载后会显示对应的图标。

问题是——字体加载失败的时候，页面上就会显示一堆英文。

手机底部导航变成了：&quot;home&quot; &quot;article&quot; &quot;inventory_2&quot; &quot;sports_esports&quot; &quot;person&quot;。

完全不能接受。

最后全部换成了 SVG 内联图标。虽然 HTML 变长了，但至少不依赖外部字体加载，永远不会显示英文。同时删掉了 Material Symbols 的字体引入，少加载一个外部请求，页面加载也变快了。

&amp;nbsp;

## 搜索

![搜索面板 - Ctrl+K 快捷搜索](/images/content/blog-screenshots/search-panel.png)

搜索功能一开始做成了弹窗（overlay）——点击搜索按钮弹出一个全屏遮罩，中间一个搜索框。

后来被吐槽了：为什么要单独弹一个界面？很打断体验。

改成了内嵌式——搜索面板从导航栏下方展开，不遮挡页面其他内容。搜索数据是构建时生成的 JSON（`/search.json`），客户端加载后用简单的字符串匹配做模糊搜索。

没用 Algolia 或 Fuse.js 之类的库，因为文章才十几篇，简单的 `indexOf` 就够用了。等文章多到几百篇再考虑换。

搜索结果直接显示文章标题、封面缩略图、日期和标签，点击跳转。

搜索就这样了。

&amp;nbsp;

## 弹幕视频播放器

凉宫春日那篇文章里有一个 GOD KNOWS 的弹幕视频播放器。用的是 DPlayer 库 + 2199 条真实的 B 站弹幕数据。

这个功能做的时候也有坑——还是 View Transitions。

DPlayer 用 `DOMContentLoaded` 事件初始化。软导航不会触发这个事件。所以第一次打开文章页视频正常显示，离开后再回来，视频就没了。

修复方法跟其他脚本一样——加 `astro:page-load` 监听。另外还加了跟音乐播放器的联动：视频播放时自动暂停 BGM，视频暂停后恢复。靠 `CustomEvent` 互相喊一嗓子，谁也不用知道对方怎么写的。

到这里网站功能基本成型了。然后某天我把链接发到群里，发现分享出去只有一个干巴巴的 URL，没有标题预览也没有封面图。

哦，对了，SEO。

&amp;nbsp;

## SEO

最开始完全没管这个。

后来花了半天把分享预览、站点地图、RSS 那些东西一口气全加完。发到群里一看——有封面有标题有摘要，终于像个正经网站了。

&amp;nbsp;

## 搭博客的感受

做之前以为都是小活，做完发现每个功能自己能跑，但放在一起就打架。暗色模式跟 View Transitions 打，导航栏跟进度条打，视频播放器跟音乐播放器打。大部分时间不是在写功能，是在当和事佬。

不过现在打开自己的网站看看，还行，没白折腾。</content:encoded></item><item><title>建站日志 #2：音乐播放器，1400行代码的怪物</title><link>https://huannan.top/blog/site-building-2/</link><guid isPermaLink="true">https://huannan.top/blog/site-building-2/</guid><description>从APlayer到自己写，拖动不跟手、歌词不同步、浏览器不让自动播放……一个音乐播放器能踩多少坑？</description><pubDate>Wed, 04 Mar 2026 00:00:00 GMT</pubDate><content:encoded>音乐播放器这个东西，做之前觉得&quot;不就是放首歌吗&quot;，做完之后发现——光 JavaScript 就写了1400多行。

&amp;nbsp;

## 为什么不用现成的

![音乐播放器 - 1400行代码的成果](/images/content/blog-screenshots/music-player-expanded.png)

一开始用的是 APlayer，一个很流行的开源网页播放器。

但后来越用问题越多：样式改不动，想做毛玻璃风格很麻烦；跟 Astro 的 View Transitions 不兼容，页面一切换播放器就没了；想要的功能它也没有，双语歌词同步、封面高斯模糊什么的都得自己想办法。

所以决定自己写。[BGM对我来说太重要了](/blog/my-favorite-anime)，追番篇里写过。

代码生成主要靠 Claude Code，我负责需求设计和代码审阅。但调试和修 bug 的过程真的痛苦。

&amp;nbsp;

## 浏览器不让自动播放

第一个大坑：浏览器的自动播放策略。

我想要的效果是：用户打开网站，滚动一下页面，音乐就自动播放。

结果发现 Chrome、Safari、Firefox 全部不允许。

它们的规则是：`audio.play()` 只能在&quot;用户激活事件&quot;里调用。点击、触摸算，**滚动不算**。

所以如果用户第一次交互是滚动，`audio.play()` 会被浏览器拒绝，而且是静默拒绝——不报错，就是不播放。

最后只能把&quot;显示播放器&quot;和&quot;播放音乐&quot;拆成两步。滚动只负责把播放器显示出来，真正的播放等用户点击或触摸的时候再触发。要是播放被浏览器拒了，就做个标记，下次用户随便点哪里的时候再偷偷重试一次。

这个 bug 前前后后修了好几版才稳定。&quot;为什么音乐没有自动播放&quot;这个问题我反复测了几十次，一度以为是自己代码写出了什么玄学 bug，折腾半天才发现——浏览器压根不让你播。

&amp;nbsp;

## 拖动不跟手

播放器是可以拖动的——按住然后拖到屏幕上任何位置。

第一版实现用的是直接改 `left` 和 `top` CSS 属性。能拖是能拖，但是很卡。特别是在手机上，手指滑动的时候播放器跟不上，有明显的延迟。

后来查了一下，`left/top` 属于&quot;布局属性&quot;，每改一次浏览器都要重新算一遍布局（reflow），所以才卡。

换成 `transform: translate(x, y)` + `requestAnimationFrame` 之后就好了。`transform` 不触发重排，浏览器可以拿 GPU 去算。改完之后丝滑了很多。

不过拖动还有另一个坑——拖动结束后如果手指释放的位置在播放器的按钮上，会触发按钮的点击事件。比如你拖完松手，刚好松在暂停按钮上，音乐就暂停了。

解决办法是在拖动结束后短暂屏蔽点击事件：在 capture 阶段注册一个 `stopPropagation`，然后 `setTimeout(0)` 后移除。

就一个拖动功能，前后翻了三遍。

&amp;nbsp;

## 歌词同步

&lt;!-- TODO-IMG: [截图] 双语歌词面板截图 | 来源: 播放器歌词面板截图 | 优先级: P1 --&gt;

双语歌词同步听起来很简单——按时间戳匹配当前歌词，显示出来就行。

但真做起来细节一堆。

时间戳是最烦的。每首歌的歌词都要手动标注每一句的时间，48首歌里有歌词的24首，每首十几二十行。对不上的时候就一行一行调——播放、暂停、微调0.3秒、再播放、还是不对、再调。纯体力活，干到后面脑子都是麻的。

滚动也有问题。歌词面板里当前行要居中显示，这用 `scrollIntoView` 就能做到。但要是用户自己在翻歌词，自动滚动就会跟手动滚动打架，画面一直在抢。

后来改成了：用户碰了滚轮或触摸之后，自动滚动先暂停 3 秒。3 秒内没有继续操作，再恢复自动滚动。

歌词面板打开/关闭的动画也调了一会儿。直接 `display: none/block` 切换太生硬，加了 `opacity + transform + scale` 的过渡，从下方滑入并放大，关闭时反向收缩淡出。

&amp;nbsp;

## Apple Music 高斯模糊

&lt;!-- 播放器截图已展示了高斯模糊背景效果 --&gt;

后来想做 Apple Music 那种效果——播放器背景是当前歌曲封面的高斯模糊。

做法不算难：在播放器底层放一个跟封面一样的图片，用 CSS `filter: blur(30px)` 模糊掉，然后叠一层半透明的毛玻璃遮罩。

切歌的时候换一下背景图，每首歌的播放器颜色就不一样了。

但性能是个问题。`filter: blur()` 在手机上很吃性能，模糊值越大越卡。30px 的模糊在低端手机上直接掉帧。后来加了 `transform: scale(1.5)` 让模糊图放大超出容器，顺便把边缘的锐利截断也解决了。

歌词面板、字幕条也各自有一层封面模糊背景。三层模糊同时存在的时候，z-index 的层级关系得理清楚，不然会互相遮挡。

&amp;nbsp;

## 音量控制

音量滑块做成了竖向的——鼠标悬停在喇叭图标上方弹出。

但第一版有个 bug：拖动音量滑块的时候，因为鼠标事件冒泡到了播放器的拖动处理器，导致拖音量的时候整个播放器跟着一起跑。

修复方法是在音量滑块的 `mousedown` 和 `touchstart` 里加 `stopPropagation()`，阻止事件冒泡到上层的拖动逻辑。

还有个问题，音量滑块被播放器的 `overflow: hidden` 给裁掉了。滑块是往上弹出的，但播放器为了圆角设了 `overflow: hidden`，直接把滑块截了一半。最后改成 `position: fixed` + `z-index: 9999`，每次弹出的时候动态算位置，让它跟着喇叭按钮走。

这种&quot;修一个小功能结果拔出萝卜带出泥&quot;的事，整个开发过程中碰了无数次。到后面我都麻了，每次修 bug 都先给自己打个预防针：这个 bug 后面八成还藏着两个。

&amp;nbsp;

## 1400行

最后数了一下，音乐播放器相关的 JavaScript 居然有1400多行。

播放列表管理、歌词解析和同步、字幕条动画、完整歌词面板、拖动系统、音量控制、随机/顺序切换、视频联动暂停、页面可见性检测、View Transitions 持久化、自动播放策略兼容……

谁能想到&quot;放首歌&quot;这件事能堆出1400行代码。而且这些全塞在 Base.astro 一个文件里，快 1900 行了，编辑器每次打开都要卡好几秒。

但第一次在自己网站上听到音乐响起来的那一刻，还是挺激动的。坐在椅子上傻笑了好一会儿。</content:encoded></item><item><title>建站日志 #1：为什么选Astro，以及踩过的坑</title><link>https://huannan.top/blog/site-building-1/</link><guid isPermaLink="true">https://huannan.top/blog/site-building-1/</guid><description>Hexo太老、Next.js太重、Hugo模板语法看不懂。最后选了Astro 6，结果发现View Transitions才是真正的坑。</description><pubDate>Tue, 03 Mar 2026 00:00:00 GMT</pubDate><content:encoded>选框架就花了两周。真正写代码反而快，三天搞完——然后 View Transitions 的 bug 调了四天。

对了，如果你也想自己搭博客，我写了个[零成本建站教程](/blog/site-building-guide)，跟着做就行。

&amp;nbsp;

## 选框架

&lt;!-- TODO-IMG: [对比图] 框架Logo对比（Astro/Hexo/Hugo/Next.js）| 来源: 可引用 /images/content/generated/frameworks-compare.png 或各框架官方Logo | 优先级: P1 --&gt;

选框架这件事折腾了挺久。[跟当初选游戏引擎一样纠结](/blog/chinese-job-hunting-design-11)。

最开始试的 Hexo，毕竟教程最多。但一打开就有种回到2015年的感觉，很多插件最后更新还是三四年前。总觉得不太对，pass。

然后看了 Hugo，人人都说它快。确实快，但我一看模板语法—— `{{ .Params.title }}` ——作为一个没编程基础的人，我盯着屏幕看了五分钟完全不知道自己在看什么。pass。

也看了 Next.js。React 全家桶，功能确实强。但我就想写写 Markdown 生成静态页面，它给我整了 SSR、API Routes、状态管理……大哥我搭个博客又不是做 SaaS。pass。

最后刷到了 Astro。默认输出纯静态 HTML，零 JavaScript；支持 Markdown；组件语法跟 HTML 差不多；还内置了 View Transitions 页面过渡动画；文档也写得清楚。

试了一下午就决定用这个了。后面从零到部署上线，整个过程确实比预想的顺——当然，除了 View Transitions 这个大坑。

&amp;nbsp;

## Cloudflare Pages

部署选了 Cloudflare Pages。

选Cloudflare Pages纯粹因为——免费。GitHub推一下代码就自动部署，不用碰服务器。唯一踩到的坑是单文件25MB限制，几个BGM文件超了，ffmpeg压了一轮才传上去。

域名是 `huannan.top`，DNS 也托管在 Cloudflare。一站式搞定，不用到处折腾。

&amp;nbsp;

## View Transitions 的坑

![文章详情页 - View Transitions 的载体](/images/content/blog-screenshots/article-page.png)

Astro 6 内置了 View Transitions（页面过渡动画），用起来很简单——加一行 `&lt;ClientRouter /&gt;` 就行。

但这玩意儿是个大坑。

它干的事情大概是：点击链接时不刷新整个页面，而是用 AJAX 请求新页面的 HTML，然后&quot;软替换&quot;当前页面的内容。听起来挺好的对吧？

问题是——**你的 JavaScript 脚本不会重新执行**。

我的音乐播放器、主题切换、图片灯箱，全部是用 `&lt;script is:inline&gt;` 写的。第一次加载页面的时候一切正常，但是点击导航到另一个页面，这些脚本就不跑了。因为 Astro 检测到新旧页面的脚本内容一样，就跳过了。

结果就是：导航到新页面后，主题切换按钮点了没反应，图片点了不放大，音乐播放器消失了。

修了好久。最后是这么搞的：

- 音乐播放器加 `transition:persist`，让它跨页面存活
- 主题切换用 `astro:after-swap` 事件恢复暗色模式
- 其他脚本用 `astro:page-load` 事件重新绑定

这些 Astro 文档里都有提到，但就写了一两句。根本看不出来会出这么多问题。

&amp;nbsp;

## Tailwind CSS

样式用的 Tailwind CSS v3。

一开始觉得在 HTML 里写一大堆 `class=&quot;flex items-center gap-3 px-4 py-2 bg-white rounded-xl shadow-sm&quot;` 很丑很乱。但用了一段时间之后发现——真香。

不用想 class 名字，不用在 CSS 文件里来回切换，改样式就在标签上改，改完就能看到效果。反正我也不太会写 CSS，Tailwind 这种直接往标签上堆的方式反而更容易上手。

暗色模式也很方便。加个 `dark:` 前缀就行：`text-gray-800 dark:text-gray-200`。

不过有一个坑：Tailwind 的 JIT 模式下，动态拼接的 class 名不会被识别。比如 `` `stagger-${i}` `` 这种写法，Tailwind 不知道你用了 `stagger-1` 到 `stagger-6`，就不会生成对应的 CSS。解决办法是在 global.css 里手动写好这些 class。

&amp;nbsp;

## 开发模式

整个网站的代码生成主要靠 Claude Code，我负责需求设计和代码审阅。

自己从头手写每一行代码太慢了，用 AI 生成效率高很多。但也别想着什么都丢给它——审阅的工作量一点都不小。

AI 写的代码有时候会有一些微妙的 bug，你不仔细看发现不了。比如它把 `bottom: 24px` 写成内联样式，然后在 JS 里又用 Tailwind 的 class 去覆盖，两边打架导致播放器位置不对。

这种问题就得自己盯着。代码可以让 AI 写，但出了问题还是得自己兜底。

&amp;nbsp;

下一篇聊音乐播放器。那个东西是整个网站最复杂的部分。</content:encoded></item><item><title>阿幻教你快速建立一个自己的网站</title><link>https://huannan.top/blog/site-building-guide/</link><guid isPermaLink="true">https://huannan.top/blog/site-building-guide/</guid><description>零成本、不用买服务器。从装环境到上线，手把手教你用 Astro 搭一个自己的博客。</description><pubDate>Mon, 02 Mar 2026 00:00:00 GMT</pubDate><content:encoded>很多人问我博客怎么搭的，这篇就写个完整的教程。跟着做就行。

先说结论：**不花一分钱，不用买服务器。**

&amp;nbsp;

## 最终效果

![幻之空博客 - 最终效果首页全貌](/images/content/blog-screenshots/homepage-full.png)

搭完之后你会有：

- 一个属于自己的网站，有独立域名（比如 `xxx.top`）
- 用 Markdown 写文章，支持图片、代码块、视频嵌入
- 深色/浅色模式自动切换
- 手机电脑都能看
- 写完文章 `git push` 就自动上线，不用管服务器

&amp;nbsp;

## 费用

| 项目 | 费用 |
|------|------|
| Astro 框架 | 免费 |
| GitHub 代码托管 | 免费 |
| Cloudflare Pages 部署 | 免费 |
| `.top` 域名 | 约 9 元/年 |
| `.com` 域名 | 约 55 元/年 |

域名不是必须的。不买域名的话，Cloudflare 会给你一个 `xxx.pages.dev` 的免费域名，也能用。

所以最低成本是 **0 元**。

&amp;nbsp;

## 准备工作

需要装两个东西：**Node.js** 和 **Git**。

### 装 Node.js

Node.js 是运行 Astro 的环境，没它跑不起来。

去 [nodejs.org](https://nodejs.org) 下载 LTS 版本（长期支持版），安装的时候一路下一步就行。别选 Current 版本，坑多。

装完打开终端（Windows 搜索&quot;cmd&quot;或者&quot;PowerShell&quot;），输入：

```bash
node -v
```

显示版本号就说明装好了。比如 `v20.x.x`。

### 装 Git

Git 是把代码推送到 GitHub 的工具。

去 [git-scm.com](https://git-scm.com) 下载安装，一路默认就行。安装选项巨多，别慌，全部默认没问题。

装完输入：

```bash
git -v
```

显示版本号就 OK。

### 注册 GitHub

去 [github.com](https://github.com) 注册一个账号。免费的。

后面代码要放在 GitHub 上，Cloudflare 从 GitHub 自动拉取代码来构建网站。

&amp;nbsp;

## 创建项目

打开终端，找一个你想放项目的位置，运行：

```bash
npm create astro@latest my-blog
```

它会问你几个问题：

- **How would you like to start?** → 选 `Empty`（空项目，模板反而碍事）
- **Do you plan to write TypeScript?** → 选 `Yes`
- **How strict?** → 选 `Strict`（别怕，不会写 TypeScript 也没关系，写 Markdown 用不到）
- **Install dependencies?** → 选 `Yes`
- **Initialize a git repository?** → 选 `Yes`

等它装完，进入项目：

```bash
cd my-blog
```

&amp;nbsp;

## 装 Tailwind CSS

Tailwind 是样式框架，写 CSS 很方便：

```bash
npx astro add tailwind
```

一路选 `Yes`。它会自动帮你配好，省得自己折腾配置文件。

&amp;nbsp;

## 跑起来看看

```bash
npm run dev
```

终端会显示一个地址，一般是 `http://localhost:4321`。浏览器打开就能看到你的网站了。

虽然现在啥都没有，但至少能跑了。

&amp;nbsp;

## 写第一篇文章

在 `src/content/blog/` 文件夹下创建一个文件 `hello-world.md`：

```markdown
---
title: &apos;你好，世界&apos;
date: 2026-03-02
---

这是我的第一篇博客。

写点什么都行。
```

两个 `---` 之间的部分叫 frontmatter，是文章的元数据（标题、日期等）。下面就是正文，用 Markdown 语法写。

保存后刷新浏览器，文章就出来了。

&amp;nbsp;

## 部署上线

### 推到 GitHub

在 GitHub 上新建一个仓库（Repository），名字随便取，比如 `my-blog`。

然后在终端里：

```bash
git add .
git commit -m &quot;第一次提交&quot;
git remote add origin https://github.com/你的用户名/my-blog.git
git push -u origin main
```

代码就上传到 GitHub 了。

### 连接 Cloudflare Pages

&lt;!-- TODO-IMG: [截图] Cloudflare Pages设置截图 | 来源: 自己的后台截图 | 优先级: P0 --&gt;

1. 注册 [Cloudflare](https://dash.cloudflare.com)（免费）
2. 左侧菜单找到 **Workers 和 Pages** → **创建**
3. 选 **连接到 Git** → 授权 GitHub → 选你的仓库
4. 构建设置：
   - 框架预设：`Astro`
   - 构建命令：`npm run build`
   - 输出目录：`dist`
5. 点击部署，然后去倒杯水

等你回来，Cloudflare 就给你分配好一个 `xxx.pages.dev` 的网址了。打开就是你的网站了。

以后每次 `git push`，Cloudflare 会自动重新构建和部署。你只管写文章推代码就行。

&amp;nbsp;

## 绑定自己的域名（可选）

&lt;!-- TODO-IMG: [截图] 域名生效后的截图 | 来源: 域名管理后台 | 优先级: P1 --&gt;

想要 `xxx.top` 这种自己的域名的话：

1. 去域名注册商买一个（推荐腾讯云或阿里云，`.top` 最便宜，9 块一年）
2. 在 Cloudflare Pages 的设置里添加自定义域名
3. 把域名的 DNS 服务器改成 Cloudflare 给你的（注册商后台改）
4. 等几分钟 DNS 生效，搞定

HTTPS 证书 Cloudflare 自动签发，不用管。这一步我当时折腾了一会儿才搞明白 DNS 改在哪里，其实就是去你买域名的那个网站后台改 nameserver。

&amp;nbsp;

## 写文章的工作流

搭好之后，日常写文章就三步：

1. 在 `src/content/blog/` 里新建一个 `.md` 文件，写文章
2. `git add . &amp;&amp; git commit -m &quot;新文章&quot; &amp;&amp; git push`
3. 等一分钟，上线了

就这么简单。

用 VS Code 写 Markdown 很舒服，装一个 Markdown Preview 插件可以实时预览。

&amp;nbsp;

## 进阶：加功能

基础博客搭好之后，可以慢慢加功能：

- **MDX 支持** — `npx astro add mdx`，让文章里可以嵌入自定义组件（比如视频播放器），我博客里的弹幕播放器就是这么做的
- **RSS 订阅** — `npm install @astrojs/rss`，一行命令的事
- **Sitemap** — `npx astro add sitemap`，帮搜索引擎收录你的站
- **评论系统** — Waline（免费，需要另外部署，稍微麻烦一点）
- **音乐播放器** — 这个就是自己写了，参考我的建站日志 #2
- **深色模式** — Tailwind 的 `dark:` 前缀 + JavaScript 切换

我的博客源码在 GitHub 上，想参考的话可以直接看。

&amp;nbsp;

## 常见问题

**Q：不太会编程，能搞吗？**

能。我自己就没什么编程基础，代码主要靠 Claude Code 生成。你真正需要会的就是写 Markdown，语法很简单，几分钟的事。

**Q：Cloudflare Pages 真的免费吗？有什么限制？**

真的免费。每月 500 次构建、无限带宽、无限请求。个人博客完全够用。唯一的限制是单文件不能超过 25MB（视频和大图注意压缩）。

**Q：文章能用中文吗？**

当然。文件名、标题、正文全部可以用中文。

**Q：手机上能写文章吗？**

可以。在 GitHub 网页版直接编辑 Markdown 文件，保存后自动触发部署。不过体验一般，还是电脑上写比较舒服。

**Q：以后想换框架怎么办？**

文章都是 Markdown 格式的，是通用的。换任何框架都可以直接迁移，不会被锁死。

&amp;nbsp;

就这些了。有什么问题可以在评论区问我。</content:encoded></item><item><title>你好，世界！这里是幻之空</title><link>https://huannan.top/blog/hello-world/</link><guid isPermaLink="true">https://huannan.top/blog/hello-world/</guid><description>第一篇，随便聊聊为什么搭了这个博客。</description><pubDate>Sun, 01 Mar 2026 00:00:00 GMT</pubDate><content:encoded>用Godot做了大半年游戏，踩坑无数，回头一看连个记录都没留下。

要求职嘛，总得有个地方展示自己做的东西。简历上写&quot;独立完成了一款Roguelike卡牌游戏&quot;，HR看了估计也就扫一眼。但如果有个网站，点进去就能能直接看到游戏在跑、能翻翻策划案——比简历上一行字有说服力多了。

当然，说实话，不只是为了求职。

之前也不是没有地方写东西，QQ空间、微博、朋友圈，都写过。但总觉得不太对劲。那些地方太吵了，写出来的东西混在各种动态里面，过两天自己都找不到了。而且有些话，发在社交平台上总会顾虑谁会看到、会怎么想。

就想有个自己的地方，安安静静地放东西。写什么都行，不用在意有没有人看。

&amp;nbsp;

## 为什么叫「幻之空」

名字其实很简单。

「幻」是我名字里的字，「空」来自「缘之空」。高中那会儿反复看了好几遍缘之空，是对我影响挺大的一部番。所以就把这两个字拼在一起了。

幻之空。听起来有点中二，但我觉得还行。

![缘之空 - 博客名「幻之空」的灵感来源](/images/content/references/yosuga/key-visual-01.png)
&lt;!-- VERIFY-IMG: 如果此图不存在，需要手动下载缘之空官方宣传图 --&gt;

&amp;nbsp;

## 这里会放什么

我自己也没完全想好。但脑子里已经有一堆想写的了——

我现在在用 Godot 4 做一款叫「中国式求职」的游戏，光是一个卡牌拖拽的交互就重写了三遍，这种东西肯定得记。然后追番、看轻小说、听 Vocaloid，这些占了我生活的大半，不聊不行。还有些乱七八糟的碎碎念吧，比如哪天又熬夜到四点之类的。

反正不给自己设限制。想到什么写什么。

&amp;nbsp;

好了，第一篇就这样。下一篇大概会聊聊建站的过程，光是选框架就选了两周。如果你也想搭博客，可以看看少踩几个坑。</content:encoded></item></channel></rss>