<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
   <channel>
      <title>云风的 BLOG</title>
      <link>https://blog.codingnow.com/</link>
      <description>思绪来得快去得也快，偶尔会在这里停留</description>
      <language>en</language>
      <copyright>Copyright 2026</copyright>
      <lastBuildDate>Sat, 23 May 2026 15:04:32 +0800</lastBuildDate>
      <generator>http://www.sixapart.com/movabletype/?v=3.2b5</generator>
      <docs>http://blogs.law.harvard.edu/tech/rss</docs> 

            <item>
         <title>缺氧和异星工厂的比较</title>
         <description><![CDATA[<p>缺氧（Oxygen Not Included, ONI）和异星工厂（Factorio）都是自动化领域的神作，它们在 Steam 上都有自动化、基地建设、资源管理的标签，可见游戏体验上有相当多的相似之处。玩家群体也高度重合。但是什么造就了它们的独特性？而不会像 Satisfactory 或戴森球计划之于异星工厂那样，有着深深的同类基因。</p>

<p>从游戏核心上看：</p>

<p>缺氧把玩家放在一个有限资源的环境下，一切都是资源转换。玩家玩游戏是一个熵治理过程，把无序变为有序。除了玩家的主动行为，还有丰富的、半随机的、环境自然推演。缺氧的物理系统在很大程度上模拟现实，这减少了玩家的学习成本。但在细节上和现实有所不同：质量和能量都不是守恒的，随着玩家活动，物质会减少，热量可以被主动删除…… 这给了许多玩家对付系统熵增的武器。而一个封闭系统中的热力学熵增，就是玩家需要对抗的系统崩塌。</p>

<p>异星工厂（原版）这提供给玩家一个无限地图。扩张面对的是物流和自动化的指数级复杂度上升。（在原版中）玩家不断追求更大更快的自动化生产线，在这个过程中，需要增进对游戏系统的理解，找到在下一个指数级上的自动化解决方案。玩家很少会面临系统崩溃，虫子的威胁虽然存在，但几乎可以忽略。即使去掉虫灾威胁，游戏也几乎不会损失太多体验。</p>
]]><![CDATA[<hr />

<p>两个游戏都强调物流。玩家都需要通过规划设计，在 2D 平面上把物品的需求和供给连接起来。</p>

<p>异星工厂机械爪和传送带是其经典元素。但为老玩家所熟知的还有液体管道、火车、无人机等都颇具游戏性。这些物流手段的差异在于吞吐量、延迟、控制复杂度、能量开销，在玩家优化物流时，需要在其中权衡。</p>

<p>缺氧很少鼓励玩家优化物流效率。它实际上也有无人机和管道运输两种基础物流，但却和异星工厂是反过来的。异星工厂先引入的是轨道/管道物流，无人机则在科技树后期开放；而缺氧一开局就给了最智能的“无人机”也就是仿生人，只需要指定需求点，仿生人就能自动的搬运物品。轨道运输反而需要仿生人的技能点足够了才能点开建造。清扫器（对应异星工厂的机械爪）也显得智能的多：只要在其作用覆盖范围内，就能自动的满足需求。大多数场景下，甚至不需要铺设轨道（对应异星工厂的传送带）。</p>

<p>缺氧因为单个地图都不太大（DLC 更是减小的母星的规模），所以单个地图上仿生人跑图的成本非常小。物质被存放在三个环境：地图格、散落在环境中的碎片以及工人设施中。物流手段大体上都可以看作是在这三个环境中的转换。例如，有的机器和环境交换流体，有的连接管道。所有的机器的固体产出都以碎片形式掉落在环境中。清扫器（爪子）可以在工人设施间传递物品，也可以将环境碎片放置到人工设施中。仿生人和挖矿机都可以把地图格上的固体转换为碎片，但仿生人只能把少量流体擦拭为碎片，无法直接转换地图格中的气体。液体和气体碎片必须通过设施投放到地图格，固体则需要依赖物理规则和温度变换才能在地图格结块。</p>

<p>而在费人工环境下，物质转移遵循的是系统的物理法则；仅有人工环境才可以通过轨道转移物品。可见，无论是固体轨道还是流体管道，在缺氧中只是大系统中的一个小部分（而不像异星工厂那样是核心系统）。缺氧的轨道更多为了解决封闭环境问题：缺氧需要玩家维持局部生产环境，真空、特定气体、高温、低温。而仿生人虽然是一个相当智能的物流工具，却会因为其活动感染环境。轨道则可以解决环境封闭后的物流。</p>

<p>和异星工厂颇为不同的是，缺氧里用仿生人做物流的效率大大超过轨道运输，速度更快，吞吐量更高。比如你用装罐器在 A 处把液体或气体装罐，再在 B 处卸载。物流效率上甚至超过铺设专门管道。其实，在异星工厂中也有类似的设计：你用背包装满物资，人肉运输和装填，效率或许是最高的。只不过无法自动化而不可能在大规模生产活动中持久使用。</p>

<p>缺氧中的轨道多用于在不适合仿生人活动的环境中使用，或是用于热交换。</p>

<p>热是缺氧中最特别的东西。它无法通过物流手段直接搬运，只能通过同一格或相邻格之间的热传导。将一个格子或一个东西控制在某个温度范围却是游戏后期的普遍挑战，这是造成缺氧和其它游戏差异的重要游戏元素。</p>

<hr />

<p>两个游戏都有采矿，冶炼、制造设施、建设基地的元素。它们都可以用一系列基础设施组合起来构建更复杂的模块。比如在异星工厂中，玩家可以设计搭建不同的核电站、炼钢车间；缺氧中也可以设计不同的动物养殖场等等。高级玩家都是以功能模块为单位设计基地的。</p>

<p>缺氧模块的元素比异星工厂要更细粒度。从游戏设定上来说，缺氧本质上只有四种模块的建设材料：矿石、金属矿石、精炼金属和人工材料。虽然看起来玩家还是要先把这些基础材料建设为人工设施，再用设施搭建模块。但因为建设好的设施还可以无损的分解回材料碎片，所以也可以看作是用这四种基础材料搭建模块的。而异星工厂则是把原材料在生产流水线中经过若干工序的加工为机器，再将机器铺在地板上。机器无法（通过游戏的基础机制）还原为材料。</p>

<p>两者对比，可见缺氧在用更少更基本的元素搭建复杂模块。缺氧很多时候需要用多个设施组合起来才有基本功能。比如用一个管道元素信号器加上一个流体截断器可以拼装出一个流体分流器，它依赖的是更底层的流体在管道中流动的规则。异星工厂中也有类似的东西，例如用多个二分器和传送带一起构建一个更复杂的分流器。但两个游戏的差别在于，异星工厂的原件组合的规则更直白一些，更多的场景是组合的规模；而缺氧往往用几个原件在相对隐晦的规则下拼装在一起，并有着说不清的边界条件。</p>

<p>由于缺氧中搭建模块必须靠仿生人行动，所以建设次序有时也很关键。比如说斜角方块可以在不破环封闭性的前提下挖开或修建，如果破坏了封闭性，可能会破坏环境的封闭性，增加不必要的后续补救操作。不像异星工厂，需要解决指数级增加的生产需求，游戏本身提供了蓝图支持。缺氧中大部分模块在同一局游戏中只用建设一次，熟练的玩家不仅要记住一个模块的样子，还要注意建设次序，或是根据环境以及不小心犯下的小错误而动态调整。缺氧中的模块从尺寸上来说要比异星工厂要小，但它提供了更多的分层：除了功能层外，电线、液体、气体、轨道、信号在不同层上，这让它在更小的网格区域提供了更多的复杂度空间。</p>

<p>异星工厂改建模块的成本是相对较小的。建设、拆建、升级，有了蓝图和无人机的帮助，只是下达一个指令，在宏观上管理即可。这给游戏在规模上递进提供了玩法基础。缺氧的改建成本有时会变得很大。从游戏设定上看，有些设施拆除的时间成本就建设的成本高。环境的破坏成本也远低于建设成本：典型的例子就是抽真空是个及其费时的过程，而挖开一个砖块就可能打破真空环境。这也导致了新手在把基地建得一团糟后，重开一盘往往是更好的选择。虽然异星工厂也有类似体验（没建好就重开），但重建基地要容易的多。</p>

<hr />

<p>异星工厂中，机器只要通电解决好供给和输出，就可以无限运转下去。建设基地可以人工快速铺建，也可以通过建设无人机蓝图铺设。玩家在不断的解决自动化的后勤工作。这样设定的前提是：资源接近无限，需要提升的是生产模块，以并行处理来提升生产效率。</p>

<p>缺氧则通过指派任务来进行游戏中的活动。相当多的机器还需要仿生人操作。运行机器也是任务的一部分，和建设及物流并列。</p>

<p>虽然 Rimworld 在这方面类似，都是间接的像小人下达任务指令，但我认为在内核上缺氧还是更接近异星工厂一点。你甚至可以把仿生人看成是异星工厂中无人机的加强版本，几乎不会有人会对缺氧中的仿生人共情。而 rimworld 中的小人却有更多的“人”性。但和异星工厂相比，缺氧一个显著的不同点：缺氧中的任务是需要分配优先级的。这是因为下任务太容易，很容易堆积下永远无法完成的任务清单。但任务间隐藏的依赖关联，游戏的底层规则未帮你自动理清。</p>

<p>让制定优先级成本核心操作的基础是：资源有限，任务有限，单个任务的步骤繁多并有隐藏的依赖关联。</p>

<hr />

<p>异星工厂是靠研发科技推动游戏进程。从游戏设计上来看，可以明显感受到科技瓶生产本后需要的产能级数上升。玩家可以感受到爬科技树推动着游戏过程。未解开的科技驱动着玩家增加生产率，解开的科技则引导玩家通过新科技升级生产过程。在（原版）通关后，还可以追求发火箭的速度，在更高效率产能的需求推动下，寻找更大模块生产的策略，同时，那些可以无限循环的科技也能进一步的促进生产效率。</p>

<p>缺氧的科技树很早就能完成，玩家从新手成长起来，更多的是不断增进对那些科技解锁的设施的理解。游戏的绝大部分时间，科研都不太阻挡游戏进程。在一盘游戏中，玩家要做的工程总数并不多：建设供氧，解决食物来源，发电，删减累积的热量，获取火箭燃料，取得太空材料，制造终极火箭。这些项目并没有直接写在任务书中，也不存在于科技树上，而是在玩家玩的过程中，通过发现环境的改变，需要面临新的挑战而激发出来。对比异星工厂那个写在科技树顶端的科研项目，缺氧中促使玩家发展的暗线是材料。获取更高熔点的金属，在极低温度下也能保持液态的制冷剂，这些都可以在更宽的温度环境下用老方法获得新东西，直到最终制备出液氢火箭探索时空裂隙。</p>
]]></description>
         <link>https://blog.codingnow.com/2026/05/oni_factorio.html</link>
         <guid>https://blog.codingnow.com/2026/05/oni_factorio.html</guid>
         <category>游戏</category>
         <pubDate>Sat, 23 May 2026 15:04:32 +0800</pubDate>
      </item>
            <item>
         <title>一个简单的缺氧地热模块</title>
         <description><![CDATA[<p>又玩了半个月的缺氧，目前累积游戏时间已达 645 小时，感觉对这个游戏有了更多理解。</p>

<p>我重新开了一盘，尝试用纯仿生人开局，依旧是保持 3 个初始小人开荒。仿生人不用吃食物，三个周期集中做一次呼吸，只要解决了能源问题，玩起来还是很舒服的。但仿生人需要定期上油，基本的方案是用排泄的残渣油做成润滑膏自循环。但我在查配方表的时候发现：润滑膏其实可以裂解成石油，而润滑则可以用菌泥榨成植物油替代。这样，似乎就多了一种方法在游戏初期拿到石油。</p>

<p>在我的理解中，缺氧的石油在开荒期的主要用途是做精炼金属的冷却剂，配合蒸汽机可以做到净增的电能。和现实常识不同，缺氧的世界是不遵守能量守恒的。金属精炼机消耗固定的电能功率，同时把金属熔解需要的热传递到冷却剂中。也就是输入固定的电能，输出不确定的热能。而蒸汽机则可将热转换为电。这导致，精炼某些金属，比如游戏开荒期急缺的钢，不仅不消耗电，还能产电。</p>

<p>但蒸汽机有个使用门槛：它只能将 125~200 度的蒸汽转换为 95 度的水。所以只有当精炼金属的冷却剂的温度提升到 125 度以上时，才可以用来发电。用水做冷却剂显然不行，因为水的沸点只有 100 度，冷却剂会气化，导致冷却管损坏。而石油是游戏初期最为常用的精炼冷却剂：它在 540 度左右才会发生相变。只要用石油做冷却剂炼钢，在炼钢的过程中会获得大量熔解铁的热，温度升到 200 度，然后送去蒸汽机还原为水，就能发电了，发的电将超过运行精炼机的电开销。</p>

<p>对大部分玩家来说，用石油冷却炼钢，通常是在游戏中学会的第一个主动热量控制的玩法。如果没有做足功课（比如看老玩家的攻略）自己研究缺氧的话，在面多了加水，水多了合面的试错过程中，一般会在基地变成蒸笼后第一次思考热控制的问题：看起来游戏中大部分的生产活动，哪怕只是小人闲逛，都是电能或生物能想热的单向转换。而游戏中提供的字面意义上的降温机器，似乎都在转移热量，而不是消除热量。这导致了整个基地的热会缓慢的上升，直到不适合生存。</p>

<p>初看蒸汽机需要在 125 度以上蒸汽的高温环境才能工作，却可以用来消除热，有点反直觉，细想又些合理之处。这就是游戏提供的挑战：先把温度（通过某种热交换）升上去，再用蒸汽机降下来，从中删除热。至于输出的 95 度的水依然温度很高，但绝对热量减少了。接下来，可以再利用冷凝机，消耗电制造温差降温（但不删除热），把温度继续降低到常温。这个过程也和现实不同，不遵守能量守恒：制造温差是需要输入电能的。</p>

<p>缺氧玩下去，直到通关。一直在挑战玩家，怎么获得极低的低温，如何掌控极高的高温。</p>
]]><![CDATA[<hr />

<p>和异星工厂这样的游戏不同。缺氧里没有直接的给出一个机器，造出来使用就可以制造低温材料或高温材料，也不是去到更远的地图上就能获得。这个限制在于大部分的机器都有一个工作温度，通常在常温附近，远低于制造机器材料的熔点。所以根本没法在高温环境使用它们。但仔细推敲游戏中的设施说明，似乎有一些端倪，比如碳炉没有工作温度限制，可以不断的炼碳堆积热量，似乎只要在一个密闭空间往里面塞材料，就能无限升温直到碳炉达到自身的融点。</p>

<p>看起来只要找到极高熔点材料来做碳炉就可以获得超高温环境了（实际操作却没那么简单）。</p>

<p>又比如，金属精炼机会把熔炼金属的热量输出到冷却剂中，而不是堆积在机器上。这使得它可以在室温下熔炼上千度的金属。找到合适的冷却剂就能把冷却剂加热到极高温度……</p>

<p>冷凝机也是如此：它可以无限把输入液体降温，只不过将热输出到自身的环境中。只要能找到凝固点极低的液体材料。事实上，缺氧的通关挑战就是制备出液态氢做燃料驱动终极火箭探索时空裂隙。</p>

<p>其实，不仅仅是高级材料。游戏中海暗藏了诸多并非依赖特定材料的温控挑战。一般来说，更复杂的方法都意味着生产效率。</p>

<p>缺氧的世界里，资源是有限的，玩家能做的大部分活动，都是在转换材料，而不是生成新的。而且在转换过程中，不仅不遵守质量守恒（表面上大体遵守），还伴随着质量损失。冶炼是将矿石变成金属、种植是将泥土变成农作物、烹饪是将有机物变成食物、进食是将食物变成废水……如果游戏世界里不存在火山和流星雨的话，游戏一开始看到的由海量砖块构成的整个世界终将变成虚无。更高级的转换方法意味着更少的损耗，不光是原料，还有生产过程中花掉能量对应的损耗。</p>

<p>以石油为例，如果只是查阅游戏手册的话，会发现最直接的制造方法：从原油精炼。但这个过程只有 50% 质量的原油转换为石油，平排放少量天然气。btw, 这质量减少或许对消除热反而是件好事。虽然原油精炼机本身会产热，但由于质量衰减，一些原油中的热被同时删除了。</p>

<p>但是，如果你能有办法把原油加热到 402 度的话，它们会 1:1 相变为石油。相当于产能提高了一倍。而且没有排放到环境中的天然气这种副产品，不需要做额外的气体管理。石油精炼机需要小人操作，但通常 400 度高温，想用一般人员操作的机器去做也办不到，只要找到方法，还可以节省劳动力。</p>

<p>由于采原油需要消耗水，而石油发电则可以产生废水。当原油和石油的转换比例为 2:1 时，用原油精炼石油发电的过程，水时亏损的；但如果有办法将转换比例提高到 1:1 ，水反而有正收益。这相当于，采集原油不仅不耗电耗水，还可以用来发电产水。</p>

<p>人工产生高热的方法有很多，比如上面提到的烧窑炉，还有熔炼玻璃（玻璃熔炉可以产出高达近 2000 度的熔融玻璃），但想利用它们却不简单。最容易想到的方法其实是利用地图底层的岩浆，它们有 1600 度的高温，只需要把热抽出来即可。这就是所谓的地热裂解石油。</p>

<hr />

<p>我在最近的一盘新游戏中，倒没有在开荒期裂解石油的想法，因为油井要在第二颗星球上才有。我想的是怎样更早获得第一箱石油用于炼钢。仿生人的排泄物残渣油和原油一样，可以在高温下相变为石油，只是问题稍高一点，需要 450 度，但制备方法也是非地热无它。所以，我就尝试了在游戏开荒期开发地热。由于是开荒期，能动用的材料和人力都很有限，玩了两天后，我感觉对游戏的热交换机制有了一些新理解。在推特上和玩友进一步讨论后，索性在沙盒模式下测试了一些新想法。</p>

<p>如果在网上搜攻略，会发现地热开发都需要建设一个不算小规模的模块。通常，用金属砖或砖石砖把岩浆块中的热向上导引到高处，然后用一个金属气闸和模块连接并密封。这个金属气闸就是热桥。当用信号开启时，密封环境下的金属气闸打开会形成真空，而在缺氧的设定中没有热辐射，真空是完全隔热的；当金属气闸关闭时，金属材料会高效的传热。</p>

<p>导入的热可以轻松的把原油加热到 400 度 （或残渣油需要的 450 度），直接无损变为石油。</p>

<p>但上面制备出石油只是最简单的一步，难的是如何将高温石油降温利用。一般攻略里会教你做逆流换热：让低温原油接触高温石油，在原油加热的同时，石油也降温了。这通常做一个置顶向下的之字形通道，让高温石油自行流下，而低温原油在管道中逆流向上，一路和石油换温。等原油抵达加热处（顶上引入的岩浆高温块），预热的差不多了，只差临门一脚；石油流淌到最下方，也降低到可以接受的温度。</p>

<p>这里需要注意的是裂解时的温度，如果超过 540 度会继续相变为高硫天然气。所以一般会额外做一个热容器让裂解时温度变化稳定一点。</p>

<hr />

<p>为什么网上找到的几乎都是逆流换热这个方法？我思考了一下，这是游戏机制决定的策略。</p>

<p>游戏中大部分机器的工作温度上限是 75 度，如果用金汞齐材料可以提高 50 度，用钢可以提高 200 度（后期还有更好的材料，但需要到游戏晚期）；所以，125 /275 度几乎就是用机器操作的温度上限。超过 275 度后，必须利用游戏物理规则，而不是机器来控制世界中的物质。</p>

<p>在缺氧中，物质存在于三种状态，环境中的砖块、环境中的碎片、单位的附属物品。环境中，每个格子都只能存在一种物质砖块，或是固态或是液态或是气态或是真空，砖块会依据游戏的物理法则在环境中运动。固体几乎不能动；液体会流动，并受重力影响下落，气体会扩散，并据摩尔质量而分层。当玩家去影响这些砖块，可以把这个砖块变成碎片掉落在所在格，再可以拾取带在身上或放入机器中变为物品。</p>

<p>一旦砖块变为碎片再转变为物品，玩家就可以利用各种手段在游戏世界中移动它们。但温度就是它们之间的门槛。超过了机器的工作温度，无法用机械手段处理它们：不能把固体放进运输轨道、流体塞入管道。比如，当你想对超过 275 度的环境物体进行降温，不能拿起它们放在一台可以降温的机器中，而是需要把你可以控制的低温物品用管道送去它的同一格，依靠游戏物理法则交换温度：低温物体升温，高温物体降温。</p>

<p>那么，是否可以把原油转变为 400 度的石油就放在那里，静候它降温呢？理论上，它们最终会和环境保持一致的温度。但这个过程时间很长。缺氧中的自然运动都非常缓慢。但这还不是重点，重点是如果你把常温的原油变成了高温石油，等于往你的活动区域注入了相当大的热量。如果你不消除这些热量，最终都会反映为活动环境的温度上升。石油质量不小，300 度的温差意味着大量的热量。1kg 的原油，300 度温差大约是 50 万 DTU 。而一台蒸汽机全速工作，每秒大约能删除 80 万 DTU 。</p>

<p>这就是为什么，必须用升温得到的石油去加热常温的原油。只有这些，带入环境的热才相对更少。假设你引入地热把原油提升 300 度变为石油，再把这额外的 300 度温差转移到下一批同等质量的原油上，那么除了第一批原油，后面你就没有引入额外的热量。但不引入额外的工作，无法做到 100% 的热量转移，所以最终还需要一点点的主动散热才能做到热量平衡。</p>

<hr />

<p>我这几天反复在沙盒中尝试的是：能不能做到一个较小且简单的模块做到把原油升温，再对其降温，做到热平衡。根据计算，光靠蒸汽机肯定是不行的。游戏中不设工作温度的工具实在有限，但因为限制，反而可以在有限的选择中尝试。</p>

<p>典型的是气闸门，可以随便用信号开关。前面已经提到气闸门可以用来做热桥，多个气闸门串联似乎还可以用来推动流体。但我不太想构建过于复杂的自动化机构。</p>

<p>流体容器和泵都有工作温度，这意味着无法把高温流体塞进管道控制流向。即使在低温段进入管道，当温度上升后，也无法做限流等控制，唯一例外的排出口，即从管道系统中排放到环境中是不受温度限制的。</p>

<p>直接在室温和 400 度高温之前控制不太可行，我们需要的是温度的梯度变化控制。275 度到 450 度之间完全不能主动操控，125 到 275 之前可以有限操控，125 度以下可以随意控制。</p>

<p>每次控制的质量多寡是有差别的。一次处理的质量很少时，温度变化很快，需要小心不要越过相变点；一次处理的质量很多时，需要累积很长的时间，长周期往往意味着环境会累积很多不可预期的变化。尤其时流体，对环境中的大质量砖块花很长时间升温或降温，就必须考虑这段时间它的流动行为。</p>

<hr />

<p>如果只是想处理第一点点油，不考虑热平衡的问题，可以把地热引出来，在旁边放个小水库自然降温。虽然热进入了活动环境（水库温度上升了），但量少可以接受。这就好比游戏开荒期，都是抱着一个小水库炼第一批钢的。关键的技术点是怎么引出岩浆中的热。</p>

<p><img alt="ONI_1.png" src="https://blog.codingnow.com/images/ONI_1.png" width="630" height="664" /></p>

<p>这是我的方案，和网上常见的攻略不同，我用的是一些更早期的材料，适合在非常早期快速启动。在图片的红框处用了一点小技巧，那么个被碳掩埋的温度传感器。只需要挖开一个空格，在上面做一个温度传感器，并用煤做一个变温板。因为煤在 200 多度就会相变为精炼碳，所以就坍塌为一个碳方块，但温度传感器被固体掩埋后可以继续工作。用这个方法下面接上气闸门做的热桥和金属方块（图中是金属变温板）就可以非常安全精确的导入地热了。</p>

<p>因为这里我们需要的温度不超过 500 度，它们的材料用铜制就可以。接触岩浆的部分，虽然岩浆有 1600 度（邻接岩浆的深渊晶石 1300 度），但气闸门连续导热时间不会太长，实测最高温度在 1000 度之下，用铁（而不是钢）也是安全的。但铜的熔点太低，不可以使用，这包括连接闸门的信号线。注意：在热传导中，信号线/电线/管道等的导热是不可忽视的，不能光关注砖块。</p>

<p>这个建议地热装置把导热块设定在 400 或 450 度，超过就打开气闸门（关闭热桥），导入的环境的最高温度不会超过设定温度之上 20 度（算上开关热桥本身的时间差），由于自然换热，方块之前会趋向热平衡，方块不可能自然升温超过最高温方块。这个温度传感器得到的温度，是整个系统中的理论最高温度，所以这个依赖这个温度信号的机构是绝对安全的（石油因为超过温度而气化）。</p>

<p>从上面滴入原油，靠环境加热到相变温度后，会变成石油，继续滴入原油，两者需要占据两个格子，所以液面会提升。最终被挤上去，落入右侧的自然降温区，靠右边的水池降温。</p>

<p>除了下面的热桥，需要自动化控制的是上面的滴落口。如果一次加的原油太多，来不及变成石油。这里的方法是在低落点检测元素，发现是石油的话就继续滴。因为原油的密度比石油大，所以滴下来会沉在最下面和导热块直接接触，并把之前裂解的石油挤上去。推友提示说，石油和原油密度不同，单个格子石油质量有个上限，所以这里用液压传感器也可以（原油可以制造的最大液压超过石油）。但我尝试过之后，发现液压控制要求一次处理太多原油（单格装满），这样单个制备周期过长。</p>

<hr />

<p>为了长期运行，我设计了一个带自身热平衡的模块。和网上传统的模块比，显然规模要小得多。我已经在沙盒中稳定的运行了 100 多周期。</p>

<p><img alt="ONI_2.jpg" src="https://blog.codingnow.com/images/ONI_2.jpg" width="528" height="395" /></p>

<p>它本质上也做了一个逆流换热机构，只不过不是用得自然流淌的通道，而是让低温原油在管道中绕圈做自循环。</p>

<p>低温原油先绕进右下的黄色区域，这个区域也是石油的最终出口，保持温度在 120 ~ 170 度左右，原油会对其降温，并将自己预热，部分回流到左上的初始区。长期运行的结果会将左上角的原油初始区温度提升到 120 度左右（所以这里的泵需要至少金汞齐）。</p>

<p>黄色区上面有一个主动降温区（绿色框），和黄色区用金属砖接触，用蒸汽机降温，同时可以稳定输出一定的电力。由于蒸汽室最低温度会停在 125 度（蒸汽机停止工作，也就不会输出 95 度的水继续降温），所以黄色区域在长期运行的温度下限就是 125 度。在初期由于有低温原油降温，温度会更低。如果持续有常温原油输入的话，黄色区会降低到一个更低的温度。但为了安全，黄色区的水泵还是钢制比较妥当（稍微超过了金汞齐的上限）。我在的设定是 130 度以下就把石油抽出。</p>

<p>橙色区是中温区，温度在 170 ~ 250 度左右。和黄色区用气闸门隔开，制造出温差。它的区域不大，只有 2x4 格，里面放了一圈自循环的管道。原油进入后，会不断循环升温，直到红色区域放行进入。</p>

<p>红色区域下面和地热源接触，本身的空间更小。更小的空间保证了一次加热的高温油不会太多。依然是一个 2x4 的管道死循环。这里设定管道温度达到 400 度的相变温度才从上方滴落。当然，在启动阶段，如果红色区域为空（液压检测），也允许滴入。这让红色区域的温度大部分时间都接近 400 度，滴落的液体瞬间就变成石油。当红色区域超压就会开门放入橙色区。同时，从原油从橙色区的自循环管线进入，原来黄色区管道中循环的液体进入橙色区，橙色区管道中循环的液体进入红色区。管道系统的三个循环圈逐级升温。</p>

<p>绿色框的主动降温区用了一个冷凝机额外对蒸汽机降温（使用上面那个独立水箱做冷却液）。它的工作时间不长，所以蒸汽机本身的发电就够用了。蒸气室内的温度几乎不会超过 170 度（因为最高温受红色区的高温限制），其实用金汞齐做冷凝机也可以。但我试运行时还是用了钢。</p>

<p>这个模块中几乎没用什么自动化信号，就是简单的用温控气闸门，以及最终的水泵。因为只靠温度控制，所以可以看到模块中混杂了石油和原油。这是因为没有设计额外的机制保证原油不从 A 区漏出。但这关系不大，在黄色区抽出的时候过滤一下即可（绿色区右侧有一个过滤器）。原油重新回到循环中是无害的。</p>

<p>它只所以不需要复杂的自动化控制，是因为原理很简单：</p>

<p>低温原油先在低温区转圈，把原油加热到低温区的平均温度。如果中温区有空位，就进入中温区，否则一直循环；同理，高温区没有空位，原油就在中温区循环；高温区会在管道中把原油温度加热到和环境一样的温度。环境中是原油和石油的混合液体，由地热砖持续加热。地热砖达到上限温度（我设为 500 度）就断开热桥，严格保证高温区的温度上限。</p>

<p>当（很小的）高温区装满时，打开闸门，将部分液体放入中温区，同时由于有了空位，中温区的原油管道进入高温区。</p>

<p>中温区和低温区的闸门由两个区域的温度同时决定。如果中温区温度不足（高温区的高温石油流入不足），或低温区温度太高（主动降温还不充分），它们是隔开的。这保证了中温区和低温区的温差，降低了用蒸汽机的压力。闸门的控制温度是反复实验的经验数字，衡量标准是让蒸汽机 90% 以上的时间都在持续输出电力，又来得及把最终石油的温度控制在 130 度左右。</p>

<hr />

<p>附：水管接得很混乱，如果设计一下应该可以规划得更好。</p>

<p><img alt="ONI_3.jpg" src="https://blog.codingnow.com/images/ONI_3.jpg" width="802" height="464" /></p>
]]></description>
         <link>https://blog.codingnow.com/2026/05/oni_geotherm.html</link>
         <guid>https://blog.codingnow.com/2026/05/oni_geotherm.html</guid>
         <category>游戏</category>
         <pubDate>Sat, 16 May 2026 23:57:29 +0800</pubDate>
      </item>
            <item>
         <title>我对《缺氧》的游戏理解</title>
         <description><![CDATA[<p>最近一个月，我一直在玩《缺氧》(Oxygen Not Included) 。前几年玩过 100 多小时，算是比较熟悉了。但这个月又高强度的玩了 300 多小时，目前总游戏时长为 485 小时，感觉对这款游戏有了一些新的理解。</p>

<p>最初喜欢上这个游戏，是想找一个类似《异星工厂》的以自动化为核心玩法的基地建设类游戏。Factorio 是我最喜欢的游戏之一，游戏总时长达 2905 小时，是放置类游戏之外我花的时间最多的游戏。我很想看看类似游戏还能向什么不同方向发展。这两个游戏的目标都非常类似：在无人星球上殖民，建设一个基地发射火箭逃出升天。它们的拓展玩法有相似之处：发射第一枚火箭只是游戏的开始，需要继续探索星空和不同的星球，面对更复杂的挑战。所以，我一开始是从 Factorio 的角度去看待 ONI ，随着对游戏的理解，才发现它们其实有不同的内核。</p>

<p>ONI 初看的确像是 Factorio 和 Rimworld 的结合体（btw, Rimworld 我也有 123 小时的游戏时长，对它也有初步了解）。和 Factorio 的传送带特色不同，ONI 是基于类似 Rimworld 的工人驱动基地运作的。但 ONI 里的工人没有 Rimworld 中复杂的社会关系和社会情感联系，更像是一群无情工作的机器人。所以我认为它们像是 Factorio 里的无人机加上了细致编排任务的能力。</p>

<p>但玩了这么长时间后，我认为 ONI 和 Factorio 有着巨大的区别。</p>
]]><![CDATA[<p>Factorio 的运作方式是简单清晰明确的，玩家可以在明确规则下不断扩大生产规模，而不同规模下的自动化需要解决不同的问题。所以，Factorio 玩家常说 The Factory Must Grow 。所以，Factorio 鼓励蓝图的使用、Mod 和游戏本体之间相互促进、不断完善更丰富的自动化手段。游戏除了标志性的机械爪传送带外，还有流体、电力和热量系统，它们都以相当简单的规则运作。其中<a href="https://blog.codingnow.com/2022/01/fluid_system.html">略复杂的流体</a>系统，在 2.0 也被简化为超级水箱，把“流动”去掉了。</p>

<p>ONI 的底层逻辑或许也很简洁。但它模型并非基于确定性规则的物流。相对比 Factorio ，玩家首先理解的是物品怎么在传送带上移动、如何被机械爪抓取；液体如何被传递，这些都和物流有关；但 ONI 首先传达给玩家的是气体的扩散和液体的流动，它们都是在环境中自动进行的：不需要玩家铺设轨道，玩家也难以精确控制它们。稍微深入游戏后，玩家还会发现，贯穿游戏的难题是热量。热同样以某种规则在环境中以单元格为单位交换，但热却无法作为一个实体直接操控。玩家需要去控制某个区域的温度，但却没有直接的手段。游戏后期最大的挑战是制备液氢制造远程火箭，这需要极低的温度；还需要驯服金属火山和岩浆，这又需要处理上千度的高温。</p>

<p>在缺氧中，资源在初期丰富但却有限。从游戏中期开始，玩家就会发现资源越来越紧缺，玩家的绝大部分手段都是在做资源转换：将 A 转换为 B 并可能伴随着质量损失。而绝大部分原始质量就是地图板块上的那些砖块，并不会凭空变多；相比而言 Factorio 的地图趋于无限，只要你肯向远方发展，永远有采不完的矿，解决好物流即可。同时，随着 ONI 中的生产活动，花掉的能量全部转换为热量。大多数游戏手段都是把热从 A 传递给 B ，而让热净减少的手段却极其有限，且藏得很深。</p>

<p>不看攻略的话，从游戏内对各种设施的字面解释很难直接找到减少热的方案。这也是新手通常都会在中期把基地变成 40 度以上的蒸笼而束手无策。初见游戏时，看到游戏界面中的文字大篇幅的罗列每种材料的比容、热传导率、热特性、固态液态气态的转换温度等会觉得离自己很远，但熟悉游戏后会发现，这些才是核心要素。</p>

<p>我最初玩 ONI 完全不得章法，基地盖得奇形怪状。这倒是和最早玩 Factorio 很像。但和 Factorio 不同，我并不完全靠自己摸索理清条理。看了几篇 ONI 的攻略后，我照着攻略指示修建基地，知道每个阶段要解决什么问题，大致怎么做。和 Factorio 明确的科技树驱动不同，ONI 的科技树其实爬得很快。玩家很少被卡在科技上，甚至在游戏中期就能解锁大部分科技，整个游戏过程也不会被科技进度卡住。真正困难的是，大部分科技解锁的物件，从字面理解上都很难想到它能做什么，有什么副作用。我感觉从这点上，ONI 的门槛比 Factorio 要高，很需要攻略引导。</p>

<p>前几年，我最初的 100 小时游戏就是按某篇攻略引导玩进去的，并深得其乐。但最近几百小时，我发现自己琢磨能玩出非常不一样的感受。游戏流程也和之前攻略引导的体验截然不同。最显著的差异就是：我最新的一盘直到在第三星球开荒，一共只养了四个小人。其中三个是开局选的，第四个是在第二星球上系统送的。也就是整个游戏过程，我都没有在传送门要一个新的小人。</p>

<p>绝大部分 ONI 的攻略都不会介绍这样的玩法。玩家或许把不加人手的玩法视为高手的挑战，但我是在理解了这个游戏的内核后，发现这是推进游戏进程的最佳手段之一，而且游戏过程会非常轻松。我来解释一下这种游戏思路的内在逻辑：</p>

<p>前面说到，游戏的大部分资源都是地图上的方块。只有喷泉和流星雨是从外部补充的净增加质量，对眼冒金星 DLC 而言，母星去掉了流星雨就只剩喷泉。游戏过程的生产活动，本质上都是资源转换。例如，你可以把小人看成将氧气加食物转换为二氧化碳和废水的转换器；食物则通常是由动物或植物将泥土转换而来，烹饪过程可能有净水参与。把两者联合起来看，小人把氧气 + 泥土 + 水转换为了二氧化碳和废水。</p>

<p>最大的例外是科研，基础科技是对水和泥土的净消耗。也就是水和泥土消失了，点亮了科技树。</p>

<p>同时，所有的生产活动都需要消耗能量。这是一个能量到热量的转换过程，最终反映为地图温度的升高。这个游戏本质上是在治理混乱，即减少地图的熵。把地图上的不同砖块转换为有序的基地，有效的维持玩家主动导向的转换过程，同时系统以某种内在规则让物质在地图上自然流动：这包括了重力作用下的液体流动、开采的砖块碎片自然掉落、气体分层等。由于一切转换器（工人、动植物、机器）都有适用环境，生物需要对应的气（液）体环境、光照、温度；机器相比生物对环境的要求没那么苛刻，但也是存在的。所以玩家建设基地就是分两个阶段处理问题：一开始的建设阶段把对应的材料搬运到位、随后的维护阶段维持环境的稳定性。</p>

<p>无论玩家养多大的工人规模，科研的总净开销是一样的。游戏的前半段，需要的核心转换是 1200 kg 的钢，用于制造第一台制冷机。因为制冷机+蒸汽机组合是游戏最稳定的将热净减少的方式。铁转换为钢的过程受限于石灰的产能，通常在初期是蛋壳。需求和产能也是恒定的，也和工人规模无关。</p>

<p>而且，游戏里大量的资源转换环境其实起的作用更大，并不需要花特别多的人力，而玩家只要用小人下达指令后，更多的等下去静待花开。</p>

<p>更少的工人意味着在产出第一台制冷机前，更少的生产活动，更少的做资源转换。维持工人的核心在于平衡氧气到二氧化碳的转换过程。这里分两个问题：制备氧气和处理二氧化碳。</p>

<p>制备氧气在前期主要是两个途径：用藻类转换或分解水。</p>

<p>藻类是相对有限的，但养活三个工人和八个工人其实区别不大（通常不会消耗完），细微的差别在于挖空地图导致的空间扩大导致的气体扩散。虽然总量不变，但熵增加了。新手很容易到处开挖，但我的经验是越早把基地封起来有选择的逐步扩展才会减少要处理的问题。</p>

<p>电解水制氧看起来干净的多：不需要挖藻类，而初期基地周围的环境水本身就需要治理（否则无法按规则规划基地）。但游戏隐藏了一个副作用是新手很难注意到：电解水制氧会产生额外的热。前面说到，游戏本质上的核心挑战就是热治理。所以我认为把这个问题推迟（到科技树基本爬完）有极大好处。所以，保持一个极小团队，有利于推迟电解水制氧。事实上我最近一盘游戏直到游戏后期需要氢气之前都没有电解水。</p>

<p>另一个问题是处理二氧化碳。在发射近程二氧化碳火箭之前，二氧化碳几乎没用。有两种手段处理它：用碳素脱离器处理掉，或存起来。因为中后期一定会适用二氧化碳火箭，我认为存起来比较好。但在开发太空前，很难找到低温区液化或固态化二氧化碳，保存气态二氧化碳非常占空间。所以，二氧化碳转换得越少越好。早期在开发太空前一定会用煤炭发电过渡，这是部分二氧化碳源头，另一部分就是工人的日常呼吸了。更少的工人意味着呼出越少的二氧化碳。电力消耗也会因为工人数量减少而略微减少，但少的不多。人数增加而增加的电耗主要是在食物制备。科研、生产石灰、精炼金属这些基本需求倒是和工人规模相关性较少。</p>

<p>工人偏少最明显的劣势是干活的人少了，玩家可能会觉得游戏节奏无意义的变慢，实则不然。在 Factorio 里，新手通常不太愿意扩大生产规模，因为那意味着脱离已经经营好的舒适区。但 ONI 不同，规模化生产在游戏大部分时段几乎难以带来好处。玩家在中前期要解决的问题并不太多，一步步总能做完，它们并不能靠扩大生产规模提升效率。相反，人越少要做的维持生存方面的工作越少，专心做推进科研和基地发展的步骤就可以了。用三人团队发展，从游戏内时间看，迈入游戏中期的总周期数比一个八人团队明显要长，但实际游戏时间却不会增加太多。这是因为，游戏内小人干得慢了，但可以用最高速度推进游戏时间；而大规模团队通常会用最慢速度玩游戏，甚至还要时常暂停。本质上来说，维持最小团队，推进游戏需要（点鼠标）的操作数量变少了。小团队也会大量减少中后期工人闲置的时间。</p>

<p>另一个优势在于：工人干活是会加经验升级的。升级带来了能力的成长，提高了工作效率。因为总的工作量差不多，所以越小的团队，经验越集中，就能更快的得到几个高素质的全能工人。劣势或许是人数太少发展需要的技能不够，在多人团队中，这往往是不同发展方向的人承担的。无论开局怎么刷，三个人都无法全部覆盖需要的专长。但我的经验是：在中期洗点，只要规划好每个阶段需要做什么，完全够用。例如：只有在装修和做化石勘探任务时才需要大师艺术，做完就洗掉即可；同理，铺设传输轨道需要的高级技能，也可以在需要时再点出来，做完项目就洗掉即可。</p>

<p>最近玩 ONI 给我的感受是：玩游戏不能着急，需要规划好，一次做一个工程。这其实是一个慢节奏游戏，让小人生存并不难。下指令容易，但执行需要很长的游戏内时间。相比 Factorio 会发现，修建一个设施需要极长的时间：改造场地环境、远距离搬运材料、建造；改建（拆除）甚至比建新的还久。但 ONI 一盘游戏必须要做的工程并不算太多，几乎都是一次性的。所以，这个游戏不像 Factorio 那样依赖蓝图，反而因地制宜处理问题更多一些。尤其是，环境的自然变化：液体流动、气体扩散都需要很长的时间，把游戏节奏慢下来，利用好环境的自然变化反而要做的总工作量会减少。欲速不达是新手常犯的错误。例如，不把基地封好就出门到处乱挖，导致后期治理要花更多时间。尤其是病毒进入基地、不可呼吸气体混入氧气环境都是一瞬间，但再想处理干净却是及其费事的。</p>

<p>这些小问题（环境的恶化）并非致命，但会潜在削弱长期的工作效率，或增加远期治理的工程量。新手和老手基地往往在视觉上就有极大不同：整齐规划的干干净净。装修房间，清理杂物是看起来短期收益最小的工作，装饰度提高的长期收益很容易被忽略，尤其是人手不足的时候不想先做。但实际上，这种迟早要完成的工程，只要不影响生存，反而应该早点完成。</p>

<hr />

<p>ONI 对我来说，最重要的游戏体验是不断发现小问题并提出解决方法。这得益于游戏内的物理规则制造的环境让同样的问题有不同的解决方案。每种方案都很难做到完美，总有一些副作用，而游戏者对游戏理解越多，就越能清楚如何承担这些副作用。</p>

<p>比方说，制备氧气是游戏的基础，游戏名就叫做 Oxygen Not Included 。但所有的制氧方案都是把氧气排放到环境中的。好在小人生存需要的氧气也是从环境中摄取。但一旦需要提取氧气使用：比如冲入氧气面罩或太空服，就需要把氧气放进管道，从环境中分离氧气就麻烦的多。直接的方法是用抽气机加气体分离器。看起来很彻底，但需要的能耗却不应忽视。不想 Factorio 那样，缺电就想办法扩展电网，ONI 里要考虑烧煤导致的二氧化碳治理问题，能量消耗带来的热量问题，这些都是短期看不到的问题，但长期游戏必将受到影响。</p>

<p>藻类制氧可以制造一个纯氧房间，这样就能节省一个分离器。但人工添加藻类时可能带入的二氧化碳就可能是一个干扰因素。运输轨道和无人机运输都是解决方法。环境气体元素信号器不耗电，可以用信号控制减少制氧室混入的其它气体，也能解决一部分问题，但不彻底。不过，ONI 中其实不需要彻底解决问题。因为和 Factorio 不同，在 Factorio 的传送带上混入杂质会堵塞整条流水线，必须手工清理；而 ONI 偶尔在氧气管道中混入一点杂质气体，只会引起设备的损坏，小人会自动修理。只需要权衡这个维修开销是否能值回票价：剩下的气体分离器的开销。为了让优化掉气体分离器更有价值，ONI 里大部分机器其实是不太耗电的，或是有极短的工作时间，大部分闲置，所以整个机器需要的总电量在优化得当时并不高。而气体分离器这种只要通气就得需要长期工作的机器反而显得功率占比很大。对比 Factorio ，传送带筛选器是不耗电的，除了太空上的空间限制，都是鼓励你使用。这个差异导向了不同的游戏体验。</p>

<p>同理，电解水制氧，你可以在管道中分离氢气和氧气（以及环境中可能存在的杂气），也可以设计好房间利用气体的自然环境分层。但依赖环境一则需要用时间来换，二是气体扩散过程的随机因素导致不能 100% 确定。</p>

<p>凡涉及气体隔离和液体分离都有类似问题。最常见的是制作真空室，它是做氯气消毒室的前置，也是做辐射管道的基础，还可以用于隔热。从多道气闸的信号控制，再或不同水门（用液体隔开不同的空间，同时让人可以穿行）的搭建方法，都伴随着很多隐晦的副作用。例如看似完美解决问题（隔离真空室）的水门可能带来一瞬间让小人湿身的负面 buf ，或是可能让无人机浸水，还可能因为温度变化液体发生相变。ONI 中并没有直接提供一个可以完全隔离两个空间的气闸门，而是设计成开门会有一小段时间漏气或漏水，这留下了很多的操作空间。</p>

<p>ps. 如果你真的想不耗电过滤气体，在充分理解 ONI 的流体系统后，可以用气阀和管桥巧妙的搭建出一个机构解决这个问题。有兴趣可以在 youtube 上找 3 Ways To Filter Gas! Oxygen Not Included Tutorial / Guide 这个视频来看。</p>

<hr />

<p>最后，介绍一下我的游戏开荒流程，可以作为针对网络上其它常见攻略流程的一份补充。开荒指基本开发完母星和第二星，用短程火箭开发第三星，并研究出中程火箭，可以去更远的星球。</p>

<p>2.0 眼冒金星的标准模式中，第二星和母星有传送器互联，可以双向传输人和物资，所以可认为是一体的。如果玩经典模式，即更大的母星则需要做一些调整。</p>

<p>如前文所述，我的游戏流程最大的不同是只用系统给的工人，不招募任何新人。所以初期一直用三个人，在第二星上获取第四个。如果有“神秘隐士”这个故事特质，可以在最后招募一个高属性小人作为补充。但最好不要选“梦境合成器”故事特质，因为需要通过延长睡眠时间（甚至专门的做梦团队）获得全员属性提升很不划算。毕竟全员也没几个人。</p>

<p>可以把游戏开荒过程看成是若干个小的项目，因为人手少，所以大致串行完成这些项目即可。</p>

<p>第一个项目是挖出基本空间，并开发初级科技。</p>

<p>开发初级科技只需要泥巴（一级）和水（二级），这是一切的基础，所以必须最先完成。挖出最小空间额外建两房间，其中一间卧室，一间临时厕所。初始传送门自带光源，所以可以就地改造成科研室。房间全部用 16 * 4 的规整空间，可规划为以中间通道为轴堆成，每层左右两间，纵向发展。我倾向于左侧生活区，即科研室、卧室、卫生间、食堂、温室，后期保持 25 度以下环境温度；右侧偏生产，放置更多热源。左右两侧之间留两格的通道即可，一列纵梯，预留一列滑杆。</p>

<p>由于高压电缆和变压器有极高的负面装饰，所以我倾向于放在工作区的更右侧并用墙隔开，然后每层靠墙设一个变压器，然后是检修用的第二梯子加纵向高压电缆。高压电缆的右侧可以留下未来的无人值守区，用于发电、蒸气室等。进入无人区需要留一个房间放氧气面罩站。</p>

<p>综上，基地横向每层三个 16 * 4 的房间，两个纵向通道。</p>

<p>在这个阶段，厕所是临时的，可以扔在右侧工业区，未来会拆掉。而生活区的卧室是永久的，所以可以建在科研室的正上方（初期氧气充沛）。至于水源，早期基地附近肯定有，可能面临的问题是占据了规划中的房间位置。所以需要留出足够位置，不用破环规划。</p>

<p>在第一个阶段，如果克制的开挖空间，是不需要制氧的。因为不招募新人，所以地图上的氧石挥发氧气就足够用了。食物也不需要补充，开局送的营养棒和挖土翻出来的淤泥根够吃，所以不需要修建食物压缩机。唯一要建的是人力发电机和科研台（唯一耗电设备）。</p>

<p>第二个项目是建造卫生间。</p>

<p>我之前看的攻略大多是快速建立煤炭发电来取代人力发电机以节省人力。但我认为人少的时候初期生存压力也少（因为系统开局送的生存资源是一样多的），人力其实完全够用。三个人大致的分工是一个科研，一个发电，一个建设。相比烧煤发电，通旱厕反而是更浪费人力的工作。如果顺利的话，完全可以在两个旱厕都堵住前，让自动化卫生间投入使用。</p>

<p>卫生间的水是可以自循环的。即冲厕所和吸收用的水远少于小人排除的废水，配合净水器反而有废水的净产出。需要考虑的是如何处理多余的废水不要堵塞管道的问题。一般的解法是让多余的废水送去液培砖种芦苇。之后做太空服正好需要芦苇。</p>

<p>至于地图附近有没有芦苇可以拔来种要看随机刷的运气，通常是有的只是远近问题。采芦苇时应该采取最小空间破坏原则，挖到就把路重新堵上，避免带入过多病毒，以及不必要的氧气扩散。</p>

<p>卫生间和净水房分开，我试过两个方案，其一是和卫生间上下两层，净水房后面兼做农场；后来发现更好的是左右两间，兼做仓库。</p>

<p>注意这里卫生间产生的废水净化后不要引入净水储备，因为其中有食物中毒病毒。让它们自循环和种芦苇即可，和基地其它用水完全隔离。如果节奏安排得当，还可以点出装桶和倒桶科技，同样放在净水房中。这时就可以拆掉一开始的手压水泵，并把拆掉旱厕扔出来的废水投入卫生间的水循环中。这可以省掉基地外额外挖一个坑倒废水的工作。</p>

<p>废水最好能尽快处理，尤其是在它挥发太多的污染氧之前完成。基地中混入一些污染氧虽不致命，但影响工作效率。</p>

<p>这个阶段，工作量其实是不均等的。科研的活最多，但当然不能让小人闲下来。但原则是整理基地，即使是收拾杂物也比向外开挖更重要。</p>

<p>第三个项目是修建米虱壁虎农场和哈奇煤炭生产间。</p>

<p>米虱是重要的食物来源，人少的话可以吃很久，而且腌制米虱由于保质期很长，还可以在其后用于短途太空旅行。不少攻略建议这时开始种蘑菇，我认为在人少够吃的情况下完全不必。倒不是种蘑菇麻烦，是因为处理菌泥带来的病毒需要的步骤较多（需要收集氯气消毒）。如果不处理病毒的话，就涉及后面会面临的病毒治理问题。</p>

<p>普通壁虎很好捉，但养出产塑料的变种比较花时间，所以要尽早养。如果运气好在附近挖出小动物变异器这个故事特质就更省事一些，不然多生几次蛋也能出来。塑料不是很着急，开荒需求也不多，完全可以等养出滑鳞壁虎产出。不需要特地去建石油产线做塑料。</p>

<p>哈奇可以把砂岩转化为煤炭，开荒期电力省点用的话，就不需要出去挖煤了。而且哈奇产蛋量较大，蛋壳是开荒要的那 1200kg 钢的原料，石灰的稳定来源；而且少量的生蛋可以作为食物补充。对于稍微有点规模的基地，比如传统的 8 人基地，这点生蛋肯定不够吃，但超小的 3 人基地，则不容忽视。这也是不需要种蘑菇，后期也不需要种冰霜小麦的原因之一。</p>

<p>如果运气不错在地图中间找到同伴芽的话，可以挖回来种上传播花香。但这属于锦上添花。</p>

<p>这个阶段如果氧气不足，可以随便加两个藻类制氧机。</p>

<p>第四个项目是装修基地，扩建出氧气室和发电房。</p>

<p>随着基地的扩大，为了提高物流效率，早点点出滑杆科技是有价值的。因为煤炭发电出的比较晚，所以二氧化碳问题不会太严重。空出一个房间专门制氧是有必要的。通常放在基地上方右侧的工业区，因为一般而言制氧过程都伴随着热量产出（单纯藻类制氧不严重）。为了减少后面分离出纯氧的难度，早点在上方留出纯氧室比较好。</p>

<p>这时不推荐电解水制氧，原因前文已经阐述了。但养壁虎需要一点氢气，推荐在地图上抽过来，否则电解一点水也也是可以的。</p>

<p>发电房放在基地最右下，后面会和其它部分隔离开，所以要留出一个房间用于内部的氧气检查站柜。</p>

<p>第五个项目是出门前的准备，包括密封基地，氯气室，氧气站、太空服等。</p>

<p>这个项目的目标室把基地和外部完全隔离开，出门带上氧气面罩，最好是太空服。氯气室用来消毒。但不需要一步到位，一开始只需要抽取附近环境中的氯气。扩建基地一定会遇到氯气区，这时需要先在入口先建好气泵，然后密封抽真空。这个过程漏一点氯气无所谓，反正随着时间会自然分层，到时候在基地下方和堆积的二氧化碳一起分离即可。抽出的氯气装箱后，通到基地的出口洗矿。这是很多新手会忽略的开发步骤，因为病毒的危害并不会立刻显现，但是处理病毒的过程会比较漫长。</p>

<p>如果病毒进入基地，处理起来也不算麻烦。如果前面卫生间水循环搭建正确，应该不会有食物中毒的问题，主要会遇到的是粘液肺，多见于挖开菌泥区。如果种蘑菇的话，不洗掉菌泥上的粘液肺，就很容易在基地蔓延开。粘液肺在纯氧环境会慢慢消失，所以除了隔绝病毒外，重要的是净化掉基地内的污染氧。同时，吸入一口污染氧还会给小人一个短期的负面 buf 。所以在基地口的氧气面罩检查站外，需要和出门气闸间留一点空间，避免开着门换衣服。</p>

<p>前面几个项目按部就班的话，因为只有三个小人，所以生存完全不会有压力。操作强度也不大，或许游戏内的周期过了不少，但大部分时间都是在加速运行的，真实游戏时间不需要太长。</p>

<p>接下来要做的事情主要有两个，都是需要出门完成的：为开发第二星做周全的准备以及开发星球表面发展太阳能和火箭基地。</p>

<p>开发二星一般需要挖通三个设施，分别是小人传送站和发送以及接收物资的站点。我觉得把物流提前打通，也就是把管道都修好再去二星会让后面的工作简单很多。这样一到二星，就立刻可以利用母星的资源。</p>

<p>眼冒金星 DLC 的开局母星非常小，所以都不会在很远的位置，应该马上就能看见。挖路要尽量少挖，用最短距离挖过去，然后把管线拉通即可。穿好太空服再做这个工程可以提升不少效率。顺便还可以把附近的故事特质完成了，尤其是小动物变异器对获取滑鳞壁虎很方便。</p>

<p>但是，铺设固体传输管道需要大量金属，所以可能需要专门开采铜矿。采矿机就非常有用了，可以节省大量人力。但如果从机器拉电缆可能比较费事，比较简单的方法是做电池，使用两个袖珍放电器就可以带动一个采矿机。电池还可以用于物流无人机，早点做两三个无人机，完全就不会有物流负担，基地的杂物也会自动被整理的干干净净。</p>

<p>另一方面，直接向上挖通地表即可，也穿上太空服。到了地表后第一件事就是铺太阳能板。早点关掉煤炭发电可以省去好多麻烦。路上如果遇到低温区，可以把玻璃和金属精炼等热量大户先临时塞进去，这样就不会破坏基地内部的温度。后面建好蒸汽房还可以搬回来。</p>

<p>一旦攒出 1200kg 的钢，就可以开始搭建蒸汽房了。蒸汽机加冷凝机是最通用的热量消除机构。因为蒸汽机是唯一一个确定且直接的设备，可以热量转换为能量。它吸入 125 度以上的蒸汽，转换为 95 度的水，同时发电。这里发电是次要的，最重要的用途是这个过程热量消失了。但为了获得 125 度的蒸汽，除了在后期可以利用环境外，稳定的主动手段就是使用冷凝机。它的工作原理是输入高温液体，输出低温液体（可以用于基地其它的降温用途），其中的温差变成热量有机器本身散发到环境种。所以，冷凝机本身不消除热量，它只搬运热量。虽然系统本身热量减少了，但冷凝机的工作过程会产生大量的环境热，它正好用于把水烧为超过 125 度的蒸汽。但这样，冷凝机本身必然处于高温中，所以必须用耐高温的钢来制作。这就是开荒需要 1200kg 钢的原因——制造第一台用于烧开水的钢制冷凝机。</p>

<p>怎么搭蒸气房网上有很多介绍，这里就不细讲了。但我想说的是，可以参考攻略，但完全不需要抄攻略中的图纸。一旦明白原理，自然会有很多想法，肯定会做出不同的蒸汽房设计。ONI 和 Factorio 不同，它更难存在最优解，一切都和游戏过程相关。</p>

<p>如果是三人基地，其实搭蒸汽房降温的需求并不强烈。比如我玩的最新一盘，搭好蒸汽房后，基地平均温度才不到 20 度，要解决的是略微增温而不是降温。但温度调节迟早是需要的，工业化温控这是必须完成的基地设施。当然这不是唯一的路径，有兴趣的话还可以试试用冰霜萝卜控温，或是将高温二氧化碳到地表固体化带走基地的热量。</p>

<p>一般来说，开发第二星的主要目标是建立起石油产线。表面上看起来，石油是工业化生产塑料的基础。但其实游戏的开荒期塑料需求并不大：装修完基地，改造地板和梯子，建立通向地表的载人管道，这些用壁虎产出就足够了，完全不需要通过石油生产。</p>

<p>石油除了中后期做石油引擎的中程火箭外，最重要的用途是用于金属精炼的冷却剂。所以我们只需要做一点点出来就够用。</p>

<p>一开始只能用水做金属精炼冷却剂。如果背靠冷源，比如附近就有低温喷泉，那么这种天然冷却源就可以稳定的工作很久。但如果自己在基地内部做冷却循环，就会发现经常需要修机器。因为金属精炼，尤其是炼钢，会放出大量的热，让冷却液迅速升温。而水超过 100 度就会气化，太低温度会结冰，这些相变都会破坏管道。放置温度巨变要么需要一个相当大的热容器，比如上面提到的大水池，尤其是天然冷源；要么就需要很复杂的自动化控制机构。虽然把玩自动化机构也值得玩很久，但更简单的方法是换成石油做冷凝剂。油的比热容比水小，炼钢时温差更大。但这反而是优势。因为超过 125 度的油就可以用来烧开水，用蒸汽机带走热量，同时还能回收部分电能。</p>

<p>所以，游戏中蒸汽机加炼钢也是一套基础的机构。懂得原理的话，也可以玩出很多很多不同的设计。</p>

<p>第三个星球就需要找出火箭去了。它通常很近，所以用二氧化碳引擎最简单。这时，游戏前期存的那些二氧化碳就用得上了，而且二星上的石油工业副产品也是二氧化碳，可以直接传送回母星，基本是不缺燃料的。</p>

<p>二氧化碳引擎速度快，尾焰温度低，对环境破坏最小。唯一的缺点是不能造大火箭。但小小的单人空间把弄起来也格外有趣。火箭部分我完全没看过攻略，有了前面足够的游戏经验，我感觉自己摸索更为有趣。火箭上主要需解决的问题是怎么让小人在里面舒服的活上几天。燃料和航程在这个阶段都不需要考虑。</p>

<p>而小人的需求无非是食物、卫生和氧气，以及避免高压力。</p>

<p>这个时候，因为人少的优势，每个人都会成长的很好，所以洗掉不必要的技能点，只点出驾驶的话，压力完全不会是问题。短途并不需要储备太多的食物，如果是两三天往返的话，随便扔点食物在火箭内就不会挨饿。</p>

<p>氧气用藻类制氧机就能解决，只要在出发前排空舱内的二氧化碳即可。如果肯盯着高气压的负面 buf 的话，把高压氧气压入舱内也能用很久，这样也可以不必设制氧机。所以这里也有很多不同的解决方案。舱内空间非常的小，所以需要做很多空间上的选择。</p>

<p>最后是舱内上厕所的问题。无疑需要用壁挂强排厕所最省空间，但充厕所的水怎么办？我第一反应是装个水箱，但一个水箱（3x2）就占掉了一半的有效空间。随之发现，其实排灌器就是用来这里的。1x2 的空间可以存 200kg 的水，只是用于冲厕所搓搓有余。</p>

<p>等开发完第三星球，以及搜罗完太空的数据卡，基本上科技树就爬完了。这时可以拆掉基地的科研设施，开始转石油火箭去更远的星球拿石墨做富勒烯，制造超级制冷剂。利用它降温才能制备液态氢，然后就是做液态氢引擎达到最大航程通关游戏了。</p>

<p>我暂时还没有玩到最后，所以这里就无法介绍后期的游戏体验。</p>
]]></description>
         <link>https://blog.codingnow.com/2026/04/oxygen_not_included.html</link>
         <guid>https://blog.codingnow.com/2026/04/oxygen_not_included.html</guid>
         <category>游戏</category>
         <pubDate>Tue, 28 Apr 2026 21:06:28 +0800</pubDate>
      </item>
            <item>
         <title>除法的意义</title>
         <description><![CDATA[<p>可可已经在三年级下学期了，数学似乎还是有点问题。这个阶段考试成绩其实都不会太差，但一旦作业或考卷上的错题并非粗心大意就值得警惕。乘除法是二年级学的，三年级已经在学两位数除一位数的除法。但会计算并不难，计算只是一项机械性技能，难的是理解乘除法的意义。理解乘除比理解加减法困难的多。</p>

<p>我翻出<a href="https://blog.codingnow.com/2025/12/coco.html">几个月前的一篇 blog</a>，发现过了 4 个月，她的问题依旧：乘除法作为一项计算技能和其背后的意义是割裂的。这导致了很多问题到底如何解决一筹莫展。固然多作练习就能开悟，毕竟几乎没有成人回头看小学数学会觉得难以理解的。但我还是想尽力搞清楚她的小脑袋里到底是哪打结了。</p>

<p>今晚讲一道相当简单的数学题：</p>

<blockquote>
  <p>有 96 个鸡蛋，8 个一盒装，可以装多少盒？</p>
</blockquote>
]]><![CDATA[<p>可可不知道如何解决这个问题，我一开始是很诧异的。我先反复确认她理解了题目的文本，并非语言理解的问题。真的是无法联想到应该使用除法这个工具，而 96 这个数字过大，即使不使用除法，也不知道该如何处理。我默不作声，让她仔细想想，她愣在那里不知所措，都急得掉眼泪了。</p>

<p>我决定一步步推演这个问题。</p>

<p>先问一个简单的版本：有 12 个鸡蛋，10 个一盒装，最多可以装满几盒？</p>

<p>我本以为她能一口答出，但可能是前面的问题受挫，她还是不知道如何下手。我想想，从桌游盒中找了一堆 token 和若干小碗，说你自己装碗试试吧。装完 12 个后，又把问题改成了 30 个，她重新摆弄了一次，这下明白了。</p>

<p>我说，现在要把道具收起来了，换成草稿纸，你该如何解决这个问题呢？</p>

<p>我教她用减法：用 30 - 10 = 20 ， 20 - 10 = 10 ，10 - 10 = 0 ；数一下一共减了 3 次。可可说，我知道了，其实不用数，只要看数字是几十，那么就是几盒了。</p>

<p>那么，回到一开始的问题，不是 10 个一盒而是 8 个一盒就不能直接看出来了，该怎么办呢？可可说那我也会：她从 96 - 8 = 88 开始一步步的做减法计算，很耐心的减到了 0 ，数了一下是 12 ，中间居然没有算错。</p>

<p>我说，96 / 8 = 12 ，并不真的要花这么多时间做减法。你其实会算除法，只是不知道除法有什么用。除法就是连续计算减法的次数，就好比乘法就是连续做多次加法一样。你需要把 token 一个个放进碗里的过程抽象化成数字写到草稿纸上，打草稿就是把脑子里想的东西具象化出来。这个过程借助数学符号可以更简单。数字是符号，加减乘除也是符号，符号能帮助你思考，但先要明白这些符号代表的道理。</p>

<p>我再换个问题：</p>

<blockquote>
  <p>有 80 个鸡蛋，8 个一盒装，可以装多少盒？</p>
</blockquote>

<p>可可没犹豫，马上告诉我是 8 盒。我说你别着急，拿草稿纸仔细算一下。她算完不好意思的告诉我是 10 盒。我画了张矩形图，给她讲解了一下 8 x 10 = 10 x 8 的道理：10 行 8 个与 8 行 10 个其实只是图形旋转了一下，总数是一样的。</p>

<p>那么，从 96 个鸡蛋里先拿出 80 个装满 10 盒后，剩下的还可以装多少盒呢？她计算了一下 96 - 80 = 16 ，16 / 8 = 2 ；然后 10 盒与 2 盒合在一起也是 12 。</p>

<p>再看除法的竖式草稿，其实是一样的。</p>

<p>今天花了一个小时讲这道数学题（她的考卷上的错题），这次似乎真的懂了。</p>
]]></description>
         <link>https://blog.codingnow.com/2026/04/division.html</link>
         <guid>https://blog.codingnow.com/2026/04/division.html</guid>
         <category>育儿</category>
         <pubDate>Sun, 12 Apr 2026 20:52:17 +0800</pubDate>
      </item>
            <item>
         <title>soluna 外挂 C 模块</title>
         <description><![CDATA[<p><a href="https://github.com/cloudwu/soluna">soluna</a> 集成了 lua 虚拟机，但默认构建方式是将 lua 库静态链接到唯一的执行文件中。这将导致无法以动态库的形式外挂 Lua 的 C 扩展。</p>

<p>这是因为，如果独立编译 Lua 的 C 扩展库，通常需要链接 Lua 的 C API 。标准的方法是动态链接 lua 实现，如果静态链接 liblua.a ，会导致进程中有多份 lua 的实现。在 Lua 的历史版本中，这将导致运行期错误。</p>

<p>这是因为，Lua 的实现中有一个静态的“空”对象，所有的 nil 都指向这个对象。如果进程空间中有多份 Lua 实现，就会出现多个空对象。运行时的数据结构中会引用这个空对象，而不同副本的实现将“空”和自身保留的“空”对象引用做比较时，就会出现错误的判断。</p>

<p>在更早期的版本，出现这种链接出现的项目，bug 会隐藏得很深。所以后来 Lua 增加了 <code>luaL_checkversion()</code> ，倡议在外部库初始化时调用，除了检查版本号，还会检查当前执行的 lua 实现是否和虚拟机创建时用的实现是同一个副本。</p>
]]><![CDATA[<p>但不知道怎样正确链接 lua 的项目（保证进程中只有一份 Lua 实现）还是太多，从 Lua 5.4 以后，这个“空”对象就被移入了 <code>lua_State</code> 这个运行期结构。以牺牲一点运行时的代价，挽救那些似乎永远也搞不懂“加载和链接”的程序员。终于，错误的链接 Lua 也能不出错了。</p>

<p>但我还是认为，在同一进程中置入多份 Lua 实现是不好的。</p>

<p>注：这也是 Windows 动态库的一个独有问题。因为 Windows 的 DLL 不允许有未完成的符号，必须在编译链接时指定所有符号（Lua C API）的来源；如果是 Linux ，可以不链接 Lua C API 的库，在运行时加载动态库，加载器就能把进程内的对应符号装载起来。</p>

<hr />

<p>回到题头的问题：soluna 静态链接了 Lua ，并未导出 C  API ，要用 C 写额外的库怎么办？</p>

<p>曾经在 Ant Engine 中，我采用了一个方法：提供一个假的代理动态库，提供所有 Lua C API 的符号。外部库可以动态链接它，而它将所有 Lua C API 调用转发到 engine 内部链接的 Lua 实现中。</p>

<p>这样做的好处是，即使是预编译好的 Lua C 库，只要它正确的以动态链接形式链接了 Lua ，就能直接被 Ant Engine 加载。如果不需要外部库，这个代理库也可以不发布。</p>

<p>今天，我想给 soluna 加上类似的特性，但尝试了新的方案：外部库在构建时额外实现一个简单的入口函数，它不依赖真的 Lua 实现，而是链接 soluna 项目中的 extlua/extlua.c 这个 Lua API 代理实现。再由 soluna 的定制加载器来加载这个外部库。</p>

<p>比如，我有一个叫做 foobar 的外部库，原本的实现是这样的：</p>

<pre>
static int
lhello(lua_State *L) {
    lua_pushstring(L, "Hello World");
    return 1;
}

extern int
luaopen_foobar(lua_State *L) {
    luaL_Reg l[] = {
        { "hello", lhello },
        { NULL, NULL },
    };
    luaL_newlib(L, l);
    return 1;
}
</pre>

<p>当我们编译成动态库时，导出的 <code>luaopen_foobar()</code> 是库的入口。lua 的 require 可以正确的导入它。但这个实现依赖若干 lua C APIs ，例如 <code>lua_pushstring()</code> 等。</p>

<p>如何在 soluna 里正确加载它呢？我们需要在调用 <code>luaopen_foobar()</code> 这个入口函数前，将进程中的 Lua C APIs 注入这个动态库。</p>

<p>在这个方案中，只需要链接 soluna 项目中的 extlua/extlua.c 单个文件，然后导出一个额外的库入口函数：</p>

<pre>
extern int
extlua_init(lua_State *L) {
    luaapi_init(L);
    luaL_Reg l[] = {
        { "ext.foobar", luaopen_foobar },
        { NULL, NULL },
    };
    luaL_newlib(L, l);
    return 1;
}
</pre>

<p>这个函数的第一行需要调用 <code>luaapi_init(L)</code> ，它的实现在 extlua.c 中。然后用 <code>luaL_newlib()</code> 注入原有的模块入口函数即可。</p>

<p><code>luaapi_init(L)</code> 并不依赖任何 Lua 的内部实现，只依赖 Lua 的一个官方宏 <code>lua_getextraspace()</code> 完成了注入 Lua C APIs 的魔法。</p>

<p>这是个有趣的技巧：</p>

<p><code>lua_getextraspace(L)</code> 的官方定义是这样的：</p>

<pre>
#define lua_getextraspace(L)    ((void *)((char *)(L) - LUA_EXTRASPACE))
</pre>

<p>每个 <code>Lua_State</code> 结构前都保留有一个指针的空间，可以用来传递数据。soluna.external.load 会构建一个空的 Lua 虚拟机，并把所有 Lua C APIs 的引用放在它的 extraspace 。因为上面的 <code>extlua_init()</code> 是一个标准的 <code>lua_CFunction</code> ，所以可以用标准函数 package.loadlib 读出。传入这个带 C APIs 的空 Lua 虚拟机，<code>luaapi_init()</code> 就能正确的导入所有 API 了。随后的 <code>luaL_newlib()</code> 会把所有真正的入口函数放在这个空虚拟机中。当然，只是一些字符串（入口名）和 C 函数指针。</p>

<p>接下来，soluna.external.load 再从这个虚拟机中把整个入口函数表复制到当前虚拟机，并销毁掉这个临时虚拟机，就完成了整个外部模块的动态加载。</p>

<hr />

<p><code>soluna.extlib(name)</code> 的实现是这样的：</p>

<pre>
function soluna.extlib(name)
    local extlua = require "soluna.extlua"
    local filename = assert(package.searchpath(name, package.cpath))
    settings = settings and soluna.settings()
    local entry = assert(package.loadlib(filename, settings.extlua_entry))
    return extlua.load(entry)
end
</pre>

<p>要使用上面例子中的放在 sample.dll 中的库 ext.foobar 只需要这样：</p>

<pre>
local libs = soluna.extlib "sample"
local foobar = require "ext.foobar"
assert(libs["ext.foobar"] == foobar)
</pre>

<hr />

<p>即使要静态链接 sample 模块（iOS 不支持动态库，可能必须静态链接），只需要采用以下编译方案即可正确工作：</p>

<ol>
<li>静态链接 sample 模块</li>
<li>不要链接 extlua/extlua.c</li>
<li>将 <code>luaapi_init()</code> 定义为一个空函数</li>
<li>把 <code>extlua_init()</code> 这个入口函数导入</li>
<li>用 soluna.extlua.load(入口函数) 加载</li>
</ol>
]]></description>
         <link>https://blog.codingnow.com/2026/03/soluna_external_lib.html</link>
         <guid>https://blog.codingnow.com/2026/03/soluna_external_lib.html</guid>
         <category>lua与虚拟机</category>
         <pubDate>Wed, 11 Mar 2026 14:49:58 +0800</pubDate>
      </item>
            <item>
         <title>Star Trek : Captain&apos;s Chair 初体验</title>
         <description><![CDATA[<p>今年过年，我沉迷于 <a href="https://boardgamegeek.com/boardgame/422541/star-trek-captains-chair">Star Trek : Captain's Chair</a> 这款 2025 年的桌游。暂时还没有中文版，如果直译的话，名为《星际迷航：船长之椅》。这是一款以卡牌构筑为核心玩法的桌游，在游戏过程中，不断完善自己的牌堆，构筑一个高效的得分引擎。如果能比对手获得更多的 VP 就可以获得游戏胜利，但也要避免突然死亡。这是一款新游戏，但作者 Nigel Buckle 和 David Turczi 之前已经用类似的系统出过 Imperium （帝国）三部曲。其中《帝国：经典版》和《帝国：传奇版》有中文版，在淘宝上就可以买到。btw, 前段时间我玩过的 VoidFall 也是他们的作品。</p>

<p>这个游戏的规则还是挺复杂的，在 BGG 上的 weight 评级达到了 4.06 。注：游戏的重度（weight）是由玩家评分综合而来，最高为 5 。它指的是规则的繁杂程度，而并非游戏的策略深度（通常有相关性）。例如围棋虽然策略深度几乎达到了桌游的天花板，但它的 weight 就不到 4 。而 bgg 上 weight 超过 4 的游戏并不多见，大部分超过 3 的桌游，一般就被归为重度游戏了。我大概花了 10 多个小时试玩，看了几个小时的教学视频，才感觉学会了游戏的基本规则。不过一旦理解了游戏的设计逻辑，玩起来还颇为流畅，规则书以及规则助记版都非常符合直觉，简单好认。重度游戏大多不太讨人喜欢，但设计良好的重度游戏也能带来更多乐趣。</p>
]]><![CDATA[<p>我认为 ST:CC 是我这些年玩过的所有卡牌构筑类桌游中机制、策略和局势变化最丰富的。它提供了及其丰富的机制让玩家控制牌组的构成，这也是“构筑”这个机制的核心玩点。和最早的《Dominion 领土》作比较：这类游戏的基本玩法就是从市场购买新卡，构建一个得分引擎。分往往也体现在牌组中，但会稀释行动牌的价值（通常分卡在游戏过程中没有收益），让玩家在构筑过程中做出权衡。Dominion 每局游戏的后期通常会面对厚厚的牌堆，行动会变得越来越不可预测。后来的同类游戏逐步加入了更丰富的机制来帮助卡组瘦身，提供给玩家更多确定性，更好的控制自己的行动。</p>

<p>ST:CC 以及它的前身 Imperium 提供了非常丰富的卡组瘦身机制：</p>

<ol>
<li><p>可以把卡堆里的牌 LOG 起来：和早期卡牌构筑游戏不同，得分卡并不是专门的卡，而是每张卡本身就带有 VP 。这更像银河竞逐这样的引擎构建游戏。收集的卡越多得分越高。LOG 可以把当局游戏不再用的卡从当前卡堆里移除，但得分依旧保留。</p></li>
<li><p>可以把卡片 deploy 到桌面：放在桌面的卡可以提供持久的被动能力，也可以有限的提供主动能力或响应能力。同时，活动卡组也得到的瘦身。根据卡片属性不同，提供有差异的回收规则。船员卡可以常驻一张，新的船员卡晋升后 dismiss 旧的；飞船卡则在占领星球后自动 dismiss ；事件卡则每张有不同的回收前置行动（不回收会在游戏结束结算时计为负分）。</p></li>
<li><p>卡片可以 beam 到飞船或星球上：这可以对卡组作更灵活的临时瘦身。几乎所有的飞船都有主动能力可以 beam 手牌，但反向回到手牌的 recall 操作却比较稀少。不过，beam 在飞船上的卡也可以随飞船 dismiss 而一同回到弃牌堆。</p></li>
</ol>

<p>永久（不可逆）和临时（可收回）的卡牌瘦身操作，可以让玩家在游戏过程中动态的调整卡组，让游戏的确定性更高，而不会在抽牌堆太大时，过于依赖抽卡的手气。就我这几天玩的数盘游戏体验，通常我的活动牌组（抽牌堆加上弃牌堆和手牌）在整局游戏里也很少超过 20 张。</p>

<p>ST:CC 在游戏过程中的卡组升级也有新意。</p>

<p>首先，和大多数卡牌构筑游戏一样，初始卡组是 10 张左右，每轮抽 5 张。这样可以保证前两轮可以作一个轮回，让随机性限制在 10 张卡的不同组合上。但和之前的很多游戏不同，它的 10 张卡是完全不同的，每张都特别设计过。甚至游戏带了 6 套风格迥异的初始牌组。而传统上的设计更偏好在初始卡组中放上雷同的初始能力卡，加上很少量的特殊卡。如果没玩过桌游的话，可以对比杀戮尖塔这样受桌游启发的电子游戏：一开始的初始卡组中只有一张特殊能力卡加上普通的打击和防御。</p>

<p>而和一般的卡牌构筑游戏的升级流程不同，它会为每个初始牌组设计 5 张左右的固定补充卡堆和 8 张左右的高级补充卡，以固定节奏补充进来：每次抽牌堆抽空都会自动触发这个补充操作，加入一张额外的补充卡。基本的补充卡的随机性在于每局游戏的进入次序是打乱的，而高级卡则需要用不同资源购买，但可以让玩家指定（没有抽卡的随机环节）。这样熟练牌组的玩家可以预先学习好每个角色牌组的策略，再实际玩的时候又不至于形成太固定的套路。</p>

<p>因为补充卡是通过卡组循环进入抽卡堆的，添加新的补充卡可以带来更多的组合，相当于卡组升级。所以调节抽卡堆的消耗速度就相当于控制玩家卡组升级速度。ST:CC 在主动控制牌堆轮转这一点上设计得比大多数卡牌构筑前辈出色。</p>

<p>1.抽牌能力。这是一个常规设计，不仅用来补充当前回合的行动选择，同时加快了抽牌堆的轮转。</p>

<ol>
<li><p>弃掉抽牌堆顶的牌。这在《Dominion》中多以攻击效果出现，说明早期这种设计更多强调的是其负面影响：让玩家暂时失去潜在的行动能力。但由于卡组瘦身很容易，它也出现了有益的一面：加快牌堆轮转。</p></li>
<li><p>在回合结束时，玩家可以任意保留手牌。这给玩家了选择：确保下一回合能做的行动，但减缓了牌堆轮转速度。</p></li>
<li><p>从市场获取新牌后，可以自由选择放入弃牌堆还是放入抽牌堆。前者加快了牌堆轮转，后者提供了确定性：可用抽牌能力立刻获取，或确保下一轮可以抽到。</p></li>
</ol>

<p>由于有大量从弃牌堆抽牌的能力。这极大的丰富了获取行动卡的途径。抽牌堆抽卡是随机的，弃牌堆抽卡是确定的（挑选），牌堆轮转加快固然是好事，但弃牌堆清空也是需要考虑的问题。</p>

<p>除了固定补充卡升级，游戏还提供公共市场和供双方争夺的中立地点卡。但很多传统的市场机制是用资源从市场买卡，而 SC:CC 并不通过积累资源购买市场卡，而是改为用特定行动卡片直接获取。市场被分为了四类：船员、货物、飞船、盟友，分别对应不同的行动卡去获取。根据选择的初始牌组不同，获取这些市场卡的行动卡使用方式也不一样。由于行动力有限，规划行动的分配获取市场卡就变成了卡片 combo 重要的一环。玩家很难积累获取市场卡的能力，抢夺地点卡更是这样：规则限制了每个回合最多只能获得一个中立地点。整局游戏中不会获得太多的额外卡片，且每张公共卡都是单独设计的，这让引入每张卡到自己的卡组都需要仔细规划。</p>

<p>ST:CC 的卡片被设计成一卡多用。卡片处于不同位置：从手牌打出或桌面上激活会有不同的能力。而即使是同一种方式使用它，一般也有多种能力供选择，只能选其一使用。虽然每种使用方式大多有前置条件或副作用，但本身的多种选择让每张卡片在不同场景下都有用。</p>

<p>因为卡片的位置非常丰富：除了传统的抽牌堆、弃牌堆、手牌外，还有桌面区、市场区、当前市场、市场库存、中立地、废牌堆、附着在其它卡片上、LOG 区、升级区、事件区等等。就我主要玩的 PICARD 牌阵来说，大量的行动就是将卡在这些这些区域之间调度。所以在玩的时候，有一点工人分配游戏的感觉。不仅提供了丰富的卡牌策略，还非常好的契合了星际迷航那种驾驶飞船探索宇宙的主题。</p>

<hr />

<p>为了让游戏不限于千篇一律的构建得分引擎循环，游戏给每个牌组都设计了不同主题的任务。任务不同于很多引擎构建游戏的终局任务卡，那个在 ST:CC 里也有，被设计为 Encouter 卡片，通常可以提供大笔的 VP 。任务就是固定在每个初始卡组上的，像是堆每组不同风格的牌作一个游戏引导，引导在游戏过程中侧重某种玩法。例如，PICARD 的基本任务就是获得三张同盟卡，并把他们都 beam 到同一艘飞船上，且获得至少 4 点科技点和 4 点影响力，就可以完成。</p>

<p>这个设计不会让玩家（熟悉后）玩游戏时不会走一步看一步，每步寻找当下行动的利益最大化。玩家必须作一个长远规划：因为任务必不可少的需要分成很多步骤，同步相当多的行动在好几个回合才可能达成。以我玩的经验来看，基本任务一般在游戏中后期才可以达成，而以开始不作计划的话，常常忙到快结束时还差上一点点。</p>

<p>由于只靠固定牌组很难有效的完成任务，随机出现的公共牌加入卡组都能带来意想不到的高效组合，所以每局游戏的过程都会差异很大。我用 PICARD 玩了 3,4 局游戏，都选的 KOLOTH 这个 bot ，但每局游戏体验完全不同。更别说换掉对手会有完全不同的局面。游戏为每个舰长的 bot 定制了不同的自动化策略来模拟人类玩家选择不同舰长会出现的不一样的打牌倾向。</p>

<p>这是一个两人对战游戏，但也可以用设计好的自动化规则来模拟一个对手。但在 BGG 上，大多数玩家认为这个单人对抗 bot 的玩法更好玩。游戏的教学作的不错，提供了一个更存粹没有对手的单人模式，通常用于熟悉牌组。这个教学模式就是无干扰的刷分，刷够足够的分就胜利了。通过玩这个模式，可以体验不同舰长牌风格迥异的 combo 策略。通常建议把 6 个舰长都刷够分，这样在对战时既能知道自己应该怎么玩，还能熟知对手的策略。</p>

<p>正式的单人模式是对抗固定规则的对手。采用的是不对称规则：玩家和 bot 的行动法则是不一样的。我没有玩过对战模式，但据 BGG 论坛玩家的反馈，预设规则把和真人玩家的对抗时会产生的交互：争夺市场卡片、抢占中立地点等模拟的很好。一开始玩的时候，操作 bot 很容易出错，但玩过一盘之后就非常顺畅了，bot 每个回合一两分钟就能操作完，反之自己这边的行动每个回合会花很长时间。可想而之，和人对战应该会有极大的 downtime ，怪不得大多数人都选择了单人 solo 。</p>

<p>但我觉得，如果有个人类对手和自己一样玩过很多盘 solo 的话，再在一起对战应该也是非常有趣的。</p>

<p>官方还为单人模式设计了一个长线的五年计划规则。让玩家可以连着玩 5~10 盘游戏，在每盘游戏间加入了牌组升级：每次胜利都可以加入当局游戏终局时的某张市场卡进入初始牌组，或是 boost 一些初始能力。由于游戏设计了 6 组不同的牌，这相当于需要击败 5 个不同的对手（自动化 bot ），想来不会有太多重复感。我打算熟悉玩所有卡组后就尝试一下这个长线任务，应该会很有趣。</p>

<hr />

<p>很想买一套实体版，但在淘宝上找不到代购，甚至目前美国那边也缺货等着重印。我这几天都是在桌面模拟器上玩的（有玩家制作的 mod ）。我的感觉是，由于电子版缺少触感，细节更容易玩错。即使很熟悉后，游戏效率还是比不上实体。这点和版图游戏颇为不同，这个几乎全部用卡牌作道具，假若是实体牌的话，电子版只在洗牌时会便利一点，打牌及查看牌面要麻烦很多。而很多版图游戏，电子模拟器在 setup 以及游戏过程中的摆放都会更方便。</p>

<p>作为 solo 游戏，实体版最方便的地方在于易于反悔。只要没有信息揭示环节（例如抽牌后查看），大多数行动你都可以方便的在牌桌上 undo ，尝试各种不同的组合。电子模拟器上的 undo 操作一不小心就把桌面状态弄乱了。毕竟桌游除了桌面，人脑里还有一整套游戏状态，缺少实体会让大脑负荷要重得多。</p>

<hr />

<p>谈点体外话。由于这款游戏规则相对繁杂，我尝试用 AI 辅助学习游戏规则，使用的 Gemini 。可惜这个游戏还太新，网上资料太少。导致 Gemini 对游戏规则细节知之甚少。但它又表现得很懂，对话中自信满满。我问了很多规则细节结果都是错的，即使我让它指出细节出至规则书上具体哪里，也全是幻觉。甚至引用论坛网友的讨论也能理解错误。最后，我还是得自己推敲规则书，或是用传统的搜索方法找到 bgg 论坛规则讨论版面的帖子，研读作者写的 FAQ 等等。和 AI 的问答阅读起来固然舒服，针对性很强（不像规则书读起来那么累），但我实在没有能力鉴别 AI 的错误。毕竟我原本就是因为不懂规则才去问的呀。</p>

<p>有些错误还是能看出来。毕竟我玩的游戏很多，可以从作者的游戏设计思路角度去考虑。玩的过程中有疑问去问 AI 。对反直觉（感觉游戏不应该这样设计）的答案有所警惕，可继续追问。但有些真看不出来。</p>

<p>比如我在和 bot 对战时，触发了一条 bot 需要 log 一艘飞船，我不知道该如何处理。（特地用英文术语）问了下 AI 。AI 告诉我应该把最近 bot 部署的 ship 卡 log 起来，并将同一地区的所有外派部队收回。但后一条是 AI 自己编的规则，我在规则书中怎么都找不到对应的文字。反复询问，AI 都表现的信誓旦旦。让我去查规则书某个章节（其实不存在）。它还引用了 BGG 论坛的帖子。而我仔细研读了大篇的帖子后，确定是 AI 混淆了 log 和 dismiss 的处理方法。</p>

<p>再有一例：游戏的舰长面板分 A/B 两面，供玩家选择。A 面只有一个任务，B 面有三个任务，其中一个和 A 面任务完全相同。完成 B 面的任务还有额外的 VP 奖励。我一开始非常不解，初看起来，A 面没有任何优势，因为 B 面不仅提供了 A 面的选择，有额外 VP ，还可以有更多选项。我在规则书上也没有找到选择 B 面的惩罚。带着这个问题我询问了 gemini ，它在搜索了 bgg 论坛后，又胡扯了一堆什么 A 面让玩家更专注，完成难度和行动奖励不同（实际完全一致的）。但实际上，核心差别其实是：B 面的科技/军事/影响力等导轨设计不同（我一开始没注意到，规则书里也没提这个差异），而 BGG 论坛里针对这点讨论的帖子中，下面好几条回复都强调了这一点，gemini 恁是在查看帖子后，把这条最重要的信息忽略掉了没告诉我。</p>

<p>结果，我和 AI 的这些对话并没有帮我节省理解规则的时间。不仅自己重新反复研究规则书，还花了更多时间去论坛看帖（当然这不是坏事）。我想，如果我让一个人类游戏玩家教我，若是自己没怎么玩这个游戏的话，都不会表现的如此自信吧。如何辨别 LLM 提供的信息中哪些确有价值会变得更加重要。LLM 的语言表达能力越来越强，也会变得越来越有欺骗性。</p>
]]></description>
         <link>https://blog.codingnow.com/2026/03/star_trek_captains_chair.html</link>
         <guid>https://blog.codingnow.com/2026/03/star_trek_captains_chair.html</guid>
         <category>桌面游戏</category>
         <pubDate>Sun, 01 Mar 2026 19:24:54 +0800</pubDate>
      </item>
            <item>
         <title>用 AI 辅助读书</title>
         <description><![CDATA[<p>最近一年闲下来，我重新挖掘了读书的乐趣，尤其是读小说。</p>

<p>读小说真的需要时间和心境，因为进入心流状态更慢。如果长时间无法进入状态，很容易就读不下去；但一旦读进去了，比玩游戏（互动形式）或看影视剧（多媒体形式）更让人沉浸和回味。你可以对精彩处反复斟酌体会其中的情感，也更容易停下来脑补作者在情节上的留白。阅读节奏完全由自己控制，可快可慢。鉴于制作成本，小说的多样性远超其它媒介，提供的选择就更为宽泛。</p>

<p>我最近尝试使用 AI 来提升我的阅读体验。首先发现的是 AI 非常适合荐书。我使用的主要是 Gemini ，免费的版本就足够了。我可以先列举一些我很喜欢的书，让它帮我推荐更多。在初选的名单中，再通过对话了解书的特色。为了避免自己总是阅读类似的书，也会让 AI 推荐一些我之前没有尝试过的类型。当然，小说本身还是人创作的，通过推荐作者比推荐书本身更有效率。</p>

<p>这两个月我想读点太空歌剧类的小说，但老一点的名著基本都看过了，所以转向近十年的新作。另一方面的原因是大多数科幻小说本身就有时效性，这些年人类现实中的科技发展很快，文学家的幻想很容易随着时间和现实脱节。</p>

<p>但我很快就发现，想读新一点科幻小说最大的问题是中文版的翻译速度完全跟不上。AI 推荐的书 90% 都没有中译版。即使把时间放宽一点，十年前的长篇，往往也只翻译了开头。这很好理解：如果出版了第一本销量不如意，可想而知后续会更不理想。这在经济上是绝对理性的行为，可对粉丝来说颇有点难受。</p>

<p>题外话，桌游领域也有点类似。桌面游戏通常也是由单个人设计，受者也非大众。即使设计者想好了出一个系列，若前作卖得不够好，扩展包也就难以发行。我最喜欢的桌游设计师 Thomas Lehmann 解释过 Res Arcana 的第三个扩展 Res Arcana Duo 为什么作为一个（看似简化过的）独立游戏发行而不延续扩展包的形式：必须想点办法扩大这个系列的玩家群，否则扩展包的销量只会越来越少。作为中文用户，我对 Res Arcana Duo 至今没能出中文版还是有点伤心的。希望今年的新作 Dark Pact (2026) 黑暗契约可以出中文版。看完介绍，我对这个纯粹的卡牌构筑游戏颇感兴趣。</p>
]]><![CDATA[<hr />

<p>我是 Old Man's War 系列的忠实粉丝，很喜欢 John Scalzi 。他的书读起来一点也不累，那种书中遍处可见的程式员式的冷幽默颇对我胃口。我前段时间在京东上买了一本互惠帝国系列的第一本《崩塌的帝国》。收到书时是一个暖日的下午五点，晚上十点就合上了书页，中间除了正常吃了个晚饭，别的时间都在读书。读这本书的另一个动机是我想多看看关于太空旅行的不同设定（以给设计我那个关于太空航行的游戏提供灵感）。读完了这本书后，除了很满意书里的科幻设定外，还很期待后续的故事发展。</p>

<p>可惜这套三部曲的后两本一直都没有翻译成中文版。</p>

<p>我觉得我近些年的英文阅读水平提升了不少，要不尝试一下直接读英文吧。试了一下，离享受读书还是颇有距离。阅读小说需要一个流畅的体验过程，无法顺势进入心流，阅读就变成了一个苦差事。文学类作品和技术类文章差异很大，能顺利阅读技术类英文，不等于读小说也没问题。我想还需要更多的阅读练习，而学习必然辛苦，这不是我目前想要的。</p>

<p>隔了两天，我尝试了另一个方法，这是我发现 AI 能提供给我的另一项重要帮助：翻译阅读。</p>

<p>我觉得，技术类文章和文学类创作最大的不同是：前者追求用精炼准确表达知识，后者需要在描述作者构思的情节之外传达情感。理解一本小说，需要基于对小说中人物和故事的理解；正如翻译一本技术书，你得理解其中的技术原理。这也是为何机器的逐句翻译无法做到准确的原因。大语言模型应该能改善翻译，但我一开始尝试的还是直接的 google translate 和装在本地 ollama 中的 translategemma 本地模型，对小说直译。</p>

<p>用不同方法，经过几个章节的体验，我发现最适合我的是让机器完全对译，不做任何针对中文语境的加工，并以中英对照的形式一段段话展示供我阅读。我主要还是针对中文阅读，虽然语言感觉有点蹩脚，但因为我知道信息原本是英文的，而我又有相当的英文语法知识，所以大脑很快就能适应，在阅读过程中自动转换为合适的中文理解。由于是机械直译，反而不会缺失信息。当觉得句子难以理解时，迅速跳转到英文原文处，通常就明了了。读到精彩的对话，往往回味一下英文原句更有感触。</p>

<p>有些句子颇难理解。这时可以打开一个 Gemini 对话，提供它足够多的上下文，然后贴上原文，Gemini 可以解释得非常清楚。毕竟这是 10 年前的小说了，我估计小说的原文本身（甚至第一卷的中译本）就是大模型的训练材料。比如这次我就学到一个知识：在英文语境中，皇帝会自称 We/Our 而不是 I/My ，用来指代个人和背后皇权的双重身份，这和中文背景下，皇帝自称“朕”颇有共通之处。第一次读英文直译时，我会对翻译器输出的“我们”有所疑惑，但随即和 AI 讨论就学到了这个。第一卷的中译本中，译者恰如其分的选择了“朕”来翻译 We ，google translate 这种直译显然是做不到的，但 Gemini 有了上下文就能选择这种译法。我很怀疑它受了训练语料的影响（被中译版的文本训练过）。</p>

<p>我用这个方法读完了第二本《the consuming fire》，大约花了 2-3 倍第一本的时间，阅读速度的下降是很明显的，但可以接受。我觉得稍加训练就可以改善到完全不影响阅读心流的状态。然后我读了第三本《The Last Emperox》，居然和读第一本一样的时长。但我觉得倒不是我快速适应了这些新的阅读方法，而是这个系列三本书的故事结构其实是类似的，读到了后面，跟上书的节奏越来越容易了。阅读长篇小说的过程有点像是在在脑子里逐步搭建作者构建的世界，然后一点点填上细节，最艰难的部分在最前面，后面就是顺理成章的活。</p>

<p>即使情节上有点雷同，我还是很喜欢这套三部曲。</p>

<hr />

<p>这两天在补《The Expanse》小说的最后三卷，不需要等美剧了 :)</p>
]]></description>
         <link>https://blog.codingnow.com/2026/02/ai_reading.html</link>
         <guid>https://blog.codingnow.com/2026/02/ai_reading.html</guid>
         <category>读书</category>
         <pubDate>Mon, 16 Feb 2026 13:23:19 +0800</pubDate>
      </item>
            <item>
         <title>日常锻炼的一些记录</title>
         <description><![CDATA[<p>我大概是去年 4 月左右开始跑步的。<a href="https://blog.codingnow.com/2025/07/jogging.html">离上次的记录已过了半年</a>。</p>

<p>最近坚持的还不错，每周可以保证至少 3 次跑步。现在的心肺能力明显好了很多。去年刚跑步时，每跑 5-10 分钟就需要步行几分钟缓缓，不然心率很容易超过 140 （我给自己定的心率上限）。现在差不多可以保持心率在 140 以下连续跑完 4 km 了。大约花费 30 分钟。这差不多是 8km/h 的平均速度，前半程会更快一点，后半程为了保持心率需要降一点速度。</p>

<p>我觉得另一方面的原因是在冬季，跑起来不那么热。现在会挑选中午有太阳的时候跑，晒晒太阳更舒适。跑完后也没特别累的感觉，只是在最后 10 分钟有一点点难受，希望快点结束。但每次还是坚持跑满 30 分钟。</p>
]]><![CDATA[<hr />

<p>我家附近 500 米处开了家抱石馆。在两个月前，我带可可去了一次，她莫名其妙的喜欢上了抱石。去了两次后就让我给她办张月卡。我说，次卡每次 95 ，月卡 750 ，一个月要去 8 次以上才划算。她说没问题，几乎天天晚上让我带她去。虽然有一半的动力是去岩馆撸那只胖猫，但看得出来是真的喜欢。我之前也带云豆出去攀岩，从小到大爬过上百次，谈不上讨厌，但始终爱不起来，我也没逼他。后来可可长大了一点，去过两次明显没有兴趣，我干脆就不带她去了。这次莫名其妙的爱上抱石，我是没想到的。</p>

<p>一开始，她只能爬 v0/v1 的线路。但进步非常快，她有从小练起的舞蹈基本功打底，身体的柔韧性特别好，尤其在爬平衡线上特别有优势。在岩馆中超过很多大人（新手）也颇为得意。在第二张月卡时，几乎可以完成所有的 V2 线路，并勉强可以挑战 V3 了。毕竟身高臂展上有劣势，一些成人可以顺利完成的 V2 线路，她需要多做几个动作，无形中提高了难度。</p>

<p>我跟着她也办了月卡，但不会每天爬，有时就是看着教一下，但也比过去勤快了许多。水平也跟着上升。现在可以爬一些 V4-V5 的线路了，而上次在这个水平还是小孩没出生前，体重在 75kg 以下的时候。</p>

<p>现在体重保持在 83kg-84kg 之间，已经很久没有降低了。比半年前再减了大约 2 kg ，比开始跑步前最重 93kg 时几乎减了 10 kg 。考虑到力量（肌肉？）也有所增长，还算满意。身边很多人都说我前两年日益见长的肚子又消失了。希望未来一年可以把体重降到 80kg 以下。</p>

<p>体能的上升对爬高墙的帮助特别明显。去年时，我去岩馆爬高墙，差不多 3-4 条线后就需要躺下休息。现在可以爬满两个小时。最近开始恢复爬先锋（比顶绳更消耗体力，我已经有 10 年没爬过了），发现自己又可以比较轻松的完成 5.10c/d 左右的先锋线路了。去年野外去了多次英西，一次阳朔，一次六盘水。野外先锋还没怎么爬，明年应该可以逐步恢复。</p>

<p>另，痛风未再来过。但尿酸水平并未降低，也没有更高。</p>

<p>还有一个身体的小问题值得注意：有次在去阳朔的车上和同车的岩友聊天。我说我的指关节常年疼痛，是不是大部分攀岩者都是这样。他们的水平都比我高一大截，说并不是这样，这种现象只在部分超高水平的岩友中听过，并建议我保护好指关节，减少抱石中那些指力线路。</p>

<p>我回头和 gemini 讨论了一下，建议是差不多的。另外可以做一些反向的力量训练，我买了一根套在指头上外撑的橡皮筋每日练习。也正是这个原因，我现在没有跟着可可一起每天抱石，并在刻意减少了需要做 Crimps 的线路。目前恢复的还不错，至少日常不爬的时候关节不疼了。</p>

<p>可可还拉了一个同班的小女孩一起抱石，我意外的发现她爸爸的爱好是跑马拉松。我请教了他许多长跑的问题，他说下次带我跑一次 8km 再加到 10km 。据说他从高中开始长跑，一直停留在每次 5km 的量，直到有人带着跑才越过这个坎。虽然他真的很爱长跑，但说每次跑马拉松，跑到最后也是非常难受的，全靠意志力坚持下来。</p>

<hr />

<p>虽然云豆对和我攀岩兴趣不大，却意外的愿意和我一起跑步。部分原因是他意识到自己体重有点超标了。目前是六年级的寒假，身高 1.74m ，体重最重时有 77kg 。我说你还是跟我跑步吧，我能减下来，你也可以。</p>

<p>寒假第一次跟我跑了 4 km 累得不行，后来我便随着他减到 3km 一次。毕竟是小孩，慢慢的就适应了。和他一起跑步，也帮我把速度提了起来。他嫌我跑得太慢（一开始我跑 4km 需要 35 分钟），父子俩跑了几次后便在半小时之内了。这跑步的兴趣也来得莫名其妙，最近一周就跑了 5 次。（体重还真减了一些，75kg）</p>

<p>今天跑完我告诫他，切忌一时热情，锻炼身体是个长期的过程，贵在坚持。每次跑到最后，总会有点难受的，需要一些意志力说服自己坚持下来。有个伴当然最好，可以相互督促。养成习惯后，日后住校，也能有自驱力。</p>

<p>ps. 教育子女真的是个长期的活。我琢磨着儿子愿意跟我跑步还有一部分原因是最近两个月每晚带着妹妹攀岩有点懈怠了他，或许是有点吃醋：过去我总是陪他比妹妹多一点的。而妹妹似乎不愿意跑步…… 结果，我也被动的增加了颇多的运动量，何尝不是件好事。</p>
]]></description>
         <link>https://blog.codingnow.com/2026/02/physical_training.html</link>
         <guid>https://blog.codingnow.com/2026/02/physical_training.html</guid>
         <category>育儿</category>
         <pubDate>Wed, 11 Feb 2026 17:28:25 +0800</pubDate>
      </item>
            <item>
         <title>介绍几款单人桌游</title>
         <description><![CDATA[<p>上个月我花了不少时间在 dotAge 这个游戏中。我很喜欢这种通过精算规划应对确定风险的感觉。由于 dotAge 有很强的欧式桌游的设计感，所以我在桌游中尝试了一些有类似设计元素的单人游戏。</p>

<p>我感觉体验比较接近的有 Voidfall (2023) 和 Spirit Island (2017) 。因为灵魂岛（spirit island ）更早一些，而且 steam 上有官方的电子版，bgg 上总体排名也更高，所以我在上面花的时间最多。</p>

<p>这两个游戏的特点都是确定性战斗机制，即在战斗时完全没有投骰这类随机元素介入。在开战之前，玩家就能完全确定战斗结果。战斗只是规划的一环，考虑的是该支付多少成本或许多大的收益。而且灵魂岛作为一款卡牌驱动的游戏，完全排除了抽牌的随机性，只在从市场上加入新牌（新能力）时有一点随机性。一旦进入玩家牌组，什么时候什么卡牌可以使用，完全是在玩家规划之内的。这非常接近 dotAge 中规划应对危机时的体验。</p>

<p>灵魂岛的背景像极了电影 Avatar ：岛的灵魂通过原住民发挥神力赶走了外来殖民者。每个回合，把神力的成长、发威（玩家行动）和殖民者（系统危机）的入侵、成长和破坏以固定次序循环。其中，殖民者的入侵在版图上的地点有轻微的随机性，但随后的两个回合就在固定规则下，在同一地点地成长和破坏（玩家需要处理的危机）。扮演岛之灵魂的玩家可以选择到破坏之刻去那个地块消除危机，在此之前玩家有两个回合可以准备；也可以提前在殖民者成长之前将其消灭在萌芽之中，但这给玩家的准备时间更少，却往往意味着更小的消耗；还可以暂时承受损失，集中力量于它处或更快的发展神力。游戏提供给玩家的策略选择着实丰富。</p>
]]><![CDATA[<p>法术卡并不多，每个神灵只有几张专属的固定初始能力卡，其它所有的能力都是所有神灵共用，让玩家自由组合的。每当玩家选择成长时，可以随机 4 选 1 。不像卡牌构筑类游戏会有很多卡片，这个游戏总体卡片不多，每张都有决定性作用。每个回合通常也只能打出一两张 张，待到可以一回合可以打出三张甚至四张（很少见）时，已经进入游戏后期在贯彻通关计划了。法力点数用来支付每张卡的打出费用这个设计粗看和卡牌构筑游戏类似，但实际玩下来感觉有挺大的不同。灵魂岛每个回合未用完的法力点并不会清零，而会留置到下回合使用且没有上限。从玩家规划角度看，更像是需要玩家去规划整局游戏的法力点分配。精确的打出每个回合的很少的几张卡片。因为抽回打过的法术卡并不随机，玩家便要在法力成长和法术重置上做明确选择。挑选法术序列变成了精密规划的一环。</p>

<p>在 dotAge 中，版图是需要规划的，玩家需要取舍每个格子上到底放什么建筑以达到连锁功效最大化。而在灵魂岛中，每张法术会提供一些元素，同一回合激活的元素组合可以给法术本身效果加成。我觉得这两个设定有异曲同工之秒。我在思考游戏设计时，受 dotAge 和 Dawnmaker 的影响，总觉得需要在版图的位置上做文章才好体现出建筑的组合，玩过灵魂岛才发现，其实单靠卡牌不考虑版图布局其实也能实现类似的体验：几张特定的法术卡组合在同一回合打出会对单一法术有额外加成，而这种组合可以非常丰富。去掉随机抽卡机制，让玩家可以 100% 控制自己牌库中的组合选择；而且总牌量很少，每个回合出牌数及其有限（受单回合出牌数及法力点双重限制），让发牌组合必须有所取舍。这像极了我在 dotAge 的狭小地图空间中布局建筑的体验，这个格子放了这个，那个建筑就得不到加成。</p>

<p>但受限于桌游，灵魂岛的游戏体验和 dotAge 差别还是很大的。我玩了（并击败了）多级难度的灵魂岛，难度越高差异越明显。桌游必须要求短回合快节奏，这让游戏规划的容错性大大降低。dotAge 一局游戏可以玩一整天，即使是超高难度，也允许玩家犯点小错误。由于电子游戏可以把元素做得更多，让机器负责运转规则，单点的数值关系就可以更简单直白。而灵魂岛这种需要在很少的行动中体现复杂计划的多样性，那些法术的真正功效就显得过于晦涩：虽然法术字面上的解释并不负责，但理解每个法术背后的设计逻辑，在游戏中做出准确的决策要难得多。</p>

<p>我在标准难度下，玩了十几盘才真正胜利过一次灵魂岛。之后每增加一点难度，感觉挑战就大了不少；反观 dotAge 我在第二盘就领会了游戏得玩法而通关，困难难度也并未带来太大的挫折感。但现在往上加难度玩灵魂岛，我还是心有余悸，不太把握得住。而且直到现在我都没敢尝试 2 个神灵以上的组合玩法，那真是太烧脑了。难怪实体版桌游都是多人合作，而不是 1 控 2 去玩。</p>

<hr />

<p>Voidfall 从游戏结构上更接近 dotAge 一点。它完全没有战斗，就是纯跑分。只要你跑分速度超过了系统规则，就胜利了。dotAge 几乎就是这个框架：玩家需要在疾病、恐惧、温度和自然四个领域积累积分抵抗系统产生的四类危机。在每次危机来领前做好准备，也就是积累产生对应领域积分的能力。</p>

<p>但无论是 spirit island 还是 voidfall 都没有 dotAge 中最重要的工人分配机制。从游戏机制角度看，dotAge 更像是电子化的 Agricola (2007) 农场主。因为农场主在桌游玩家中太经典，几乎所有桌游玩家都玩过，这里就不多作介绍了。虚空陨落（voidfall）则是一个比较新的游戏，值得简单讲一下。它没有官方电子版，但在 Tabletop Simulator 中有 mod 可以玩。</p>

<p>和 dotAge 的四个领域有点类似，voidfall 中玩家有军事、经济、科技、政治四个方向的议程可以选择。获得对应的议程卡后，就可以大致确定一个得分路线。不同的路线同时影响着玩家当局游戏的游戏过程。</p>

<p>桌游的流程不会设计的太长，在 voidfall 中只设计了三个阶段，每个阶段有一张事件卡，引导玩家的得分手段。这些事件的效果是可预测的，这和 dotAge 的预言很像。三个阶段也和 dotAge 的季节交替末日来临类似：用规则控制游戏节奏，明确的区分游戏不同阶段要作的事情。一开始生产建设、然后扩张战斗、最后将得分最大化。</p>

<p>我没有特别仔细的玩这个游戏，但从粗浅的游戏体验看，还是颇为喜欢的。过几天会多试试。</p>

<hr />

<p>我对“确定性战斗机制”这点其实没有特别的偏爱。基于骰子的风险管理机制也很喜欢。</p>

<p>前两年就特别关注过 ISS Vanguard (2022) 这个游戏。最近又（在 Tabletop Simulator 上）玩了一下 
Robinson Crusoe: Adventures on the Cursed Island (2012) 和 Civolution (2024) 。这几个游戏都特别重，几句话比较难说清楚，而且我游戏时长也不多，这里就不展开了。</p>

<p>顺便说一句，同样是鲁宾逊的荒岛求生题材的单人桌游 Friday (2011) 是一个非常不错的轻量游戏。如果不想花太多时间在重度游戏上，它非常值得一玩。这是一款及其特别的卡牌构筑类游戏，整个游戏机制不多见的把重点放在卡组瘦身上：即玩家更多考虑的是如何有效的把初始卡组中效率低效的卡精简掉。</p>

<p>游戏上手容易，大约花 5 分钟就能读完规则；设置成本极低，只使用一组卡片；但却颇有难度，我差不多在玩了 20 盘之后才找到胜利的诀窍。淘宝上就可以买到中文版（中文名：星期五），推荐一试。</p>
]]></description>
         <link>https://blog.codingnow.com/2026/01/solo_boardgame.html</link>
         <guid>https://blog.codingnow.com/2026/01/solo_boardgame.html</guid>
         <category>桌面游戏</category>
         <pubDate>Sat, 24 Jan 2026 21:08:25 +0800</pubDate>
      </item>
            <item>
         <title>和 AI 聊游戏设计</title>
         <description><![CDATA[<p>最近一段时间和 AI 聊游戏设计比较多。我主要用的是 google 首页上的 AI 模式，也试过 twitter 上的 grok 。</p>

<p>去年也和朋友聊过很多，但对理清楚自己的想法帮助有限。因为和人聊容易陷入不断的细节解释当中，一些天马行空的想法更容易被质疑，一旦陷入辩论就不太容易跳出来。而且每个人的时间并不固定，很容易造成时间和精力的浪费。和 AI 聊要轻松得多，AI 毕竟见多识广，随便提到的点都能接得上话。不想聊下去尽可以中断，不用担心浪费时间。即使怀疑 AI 出现幻觉，也可以随时暂停下来通过搜索核实。</p>

<p>不过，我觉得和 AI 讨论也有另一方面的问题。那就是太容易顺着你的思路夸大其词。它更像是一个貌似领域知识渊博但只是想讨好你的同好，不断的放出一些华丽的辞藻却在逻辑上经不起推敲。分析起游戏来头头是道，直到谈到我真的玩过上百小时的游戏时，对游戏的细节错误百出。如果我没有这些游戏的真实体验，几乎不可能分辨真伪。一开始我还会想和真人讨论时一样指出它的错误，让它修正后重新发表观点。后来就渐渐放弃了从 AI 那里直接获得真知。把它当成一个比搜索引擎更方便的信息获取入口就够了。</p>

<p>但我依然偶尔被 AI 的总结惊艳到。比如说有一场主题为模拟类型游戏的话题，聊到最后 AI 总结：</p>
]]><![CDATA[<blockquote>
  <p>成功的模拟游戏本质上是“熵增模拟器”。设计师的任务是制造一个不断趋向于“热寂”和“混乱”的世界（交通拥堵、热量堆积、资源耗尽、情感崩溃），而玩家的乐趣在于投入自己的脑力作为“负熵”，去建立一个脆弱但精密的秩序。</p>

<p>所以，在设计时，不要害怕设计“笨拙”的规则（如巡逻员、有体积的货车、会变质的食物）。这些“物理摩擦力”正是通往成就感的阶梯。如果没有这些混乱，游戏就只是一张算好了答案的报表。</p>

<p>要让玩家感到“战胜了混乱”而不是“被垃圾规则恶心”，需要遵循以下原则：确定性的混乱：混乱的原因必须是可回溯的。
提供“高级工具”来解决“基础问题”：游戏初期给玩家简单的工具去面对混乱，中后期给玩家更高级的逻辑工具。从“点”到“面”的连锁反应：混乱不应是孤立的错误，而应该是系统性的连锁反应。 </p>
</blockquote>

<p>我觉得在这种形而上的话题上，它讲得还是蛮有道理的。但我依然不觉得这些总结真的可以成为游戏设计的指导工具。</p>

<p>当然，让 AI 说什么依然极度依赖你对它说了什么。大多数时候 AI 会信誓旦旦的帮忙做一些具体设计（当我想设计卡牌驱动的游戏时），可我真的实体化这些卡片试玩后，完全玩不下去。我实在没信心再和它讨论这些卡片设计上的具体细节：真的不如我自己从零设计高效。但有时候，AI 也会对设计游戏这件事情有所畏缩，一旦强调我应该先用卡纸或在桌面模拟器中自己尝试做个原型试试。总之，让我感觉本质上它还是在跟着我的情绪走。完全没有独立思考的痕迹。</p>

<p>意识到这点，我现在更想把今天的 AI 当成一种更高级的知识搜索引擎。从这个角度看，这段时间 AI 的确给我推荐了不少不错的游戏。虽然我仔细玩过之后，这些游戏给我的体验并非完全符合 AI 的描述，我还是感谢 AI 让我挖掘出了它们。</p>

<p>尤其值得一提的是 AI 极力向我推荐的 dotAGE 这个游戏。我一口气玩了接近 160 小时。</p>

<p>其实它刚上 steam 的时候我就玩过 demo ，当时只是觉得还不错，但没有深入。前几天 AI 反复督促我要仔细玩一下，我才沉浸了进去。这是款回合制没有战斗的城市建设游戏。随机元素很少，几乎所有要面对的灾难，都在游戏规则下提前预示给玩家。玩家要做的就是提前规划每个回合的行动，通过精算赢得游戏。值得一提的是，游戏推荐的难度和最高难度给我的几乎是截然不同的游戏体验。在默认难度下，即使对游戏规则不甚了解，不需要精确规划，也能通过运气赢得游戏胜利。那种“Push Your Luck”机制驱动带来的胜利是一种相当刺激的游戏体验；但在最高难度下，游戏变得无法通过运气获胜，转而必须精密规划。而这种精算带来的又是另一种成就感。</p>

<p>在玩游戏之余，我又阅读了作者在游戏发售前夕于 reddit 上写的两篇文章，方才了解到这个游戏几乎是作者一人在攻读完游戏设计专业的博士学位后，独自花了 9 年时间制作出来的，颇为震撼。怪不得我在玩的时候感觉这个游戏要深度有深度要广度有广度，完全不像是能短期做出来的。只能说，设计出好的游戏真的很难。</p>
]]></description>
         <link>https://blog.codingnow.com/2026/01/ai_game_design.html</link>
         <guid>https://blog.codingnow.com/2026/01/ai_game_design.html</guid>
         <category>游戏</category>
         <pubDate>Thu, 15 Jan 2026 16:05:08 +0800</pubDate>
      </item>
            <item>
         <title>Skynet 升级到 Lua 5.5.0</title>
         <description><![CDATA[<p>Lua 5.5.0 已经正式发布。所以，skynet 的 Lua 版本也随之升级。</p>

<p>skynet 维护了一份修改版的 Lua ，允许在多个虚拟机之间共享函数原型。这可以节省初始化 Lua 服务的时间，减少内存占用。</p>

<p>跨虚拟机共享函数原型最困难的部分是函数原型会引用常量字符串，而 Lua 在处理短字符串时，需要在虚拟机内部做  interning 。所以 skynet 的这个 patch 主要解决的是正确处理被  interning 的短字符串和从外部导入的函数原型中包含的字符串共存的问题。具体方法<a href="https://blog.codingnow.com/2019/06/string_comparison.html">记录在这篇 blog 中</a>。</p>
]]><![CDATA[<p>这个 patch 的副产品是允许在多个 Lua VM 间共享常量表。打了这个 patch 后，就可以使用 skynet.sharetable 这个库共享只读常量表了。</p>

<p>这次 Lua 5.5 的更新引入了 external strings 这个特性，已经大幅度提升了 Lua 加载字节码的速度。我比较倾向于在未来不再依赖额外的 patch 减少维护成本。所以建议新项目避免再使用共享常量表，减少对 patch 过的 Lua 版本的依赖。</p>

<hr />

<p>Lua 5.5 基本上兼容 Lua 5.4 ，我认为绝大多数 skynet 项目都不需要特别改动。但在升级后，还是建议充分测试。注意：更新仓库后，需要用 make cleanall 清除 lua 的编译中间文件，强制 Lua 重新编译。直接 make clean 并不清理它们。</p>

<p>Lua 5.5 有几处更新我认为值得升级：</p>

<ol>
<li><p>增加了 global 关键字。对减少拼写错误引起的 bug 很有帮助。skynet 自身代码暂时还没有使用，但后续会逐步添加。</p></li>
<li><p>分代 GC 的主流程改为步进式进行。过去版本如果采用分代模式，对于内存占用较大的服务，容易造成停顿。所以这类服务往往需要切换为步进模式。升级到 Lua 5.5 后，应该就不需要了。</p></li>
<li><p>新的不定长参数语法 ...args 可以用 table 形式访问不定长参数列表。以后可以简化一部分 skynet 中 Lua 代码的实现。</p></li>
</ol>
]]></description>
         <link>https://blog.codingnow.com/2025/12/skynet_lua_550.html</link>
         <guid>https://blog.codingnow.com/2025/12/skynet_lua_550.html</guid>
         <category>skynet</category>
         <pubDate>Tue, 23 Dec 2025 10:19:52 +0800</pubDate>
      </item>
            <item>
         <title>带可可学数学</title>
         <description><![CDATA[<p>可可三年级，前段老师说她数学成绩不好，需要在家加强一下。</p>

<p>这段时间我每天晚上给她讲一点点数学，都是课本上的内容，然后我再稍稍发挥一下。几次之后，我发现最大的问题是她觉得数学很无聊。</p>

<p>她似乎比较抗拒学新的知识，更喜欢用熟悉的方法。去年我发现她计算能力有问题，每天给她做加减法练习，总算不再用更早年我教她的 +1 法算加法了：即计算 7+8 的时候，算 8 次 +1 ，也就是数数。二年级学了乘法，乘法表也背了，但现在做应用题，本该用乘法的场合，她还是习惯连续算加法，一旦乘数太大就会出错。要用除法的时候就更混乱了，并不是用减法，而是靠猜测来试。大脑里完全没有建立乘除的概念。乘法表更像是独立的有背诵任务的诗词，还没古诗那么有趣。</p>

<p>我说：数学其实是这个世界上最有趣的东西。</p>

<p>她说：为什么呢？</p>

<p>我说：这个世界上有趣的东西很多，但数学是性价比最高的。一本数学书很便宜，但可以让你读很久。从中发现有那么多有趣的事实。原来解决不了的问题，知道方法突然就明白了。如果是自己找到的方法，就更让人兴奋了。</p>

<p>她说：我还是觉得数学没意思。</p>

<p>我说：慢慢来，不着急。首先不要排斥它。学数学其实不需要硬背那么多东西，我小时候最不喜欢背书了，所以才喜欢数学的。因为数学是最不需要背的，只要你从原理出发，一步步理解，最后什么问题都能解决，只是速度慢一点。多练习就快了。那些需要记住的知识，用的多就自动记住了，不需要专门背。</p>
]]><![CDATA[<hr />

<p>还好她不排斥我给她讲数学。前段还给我说，为什么只有你给我讲数学我才懂呢？</p>

<p>就这么，我每天（半个小时左右）给她讲一点点。我觉得其实也讲不了什么，聊胜于无。有一点我觉得还不错，她有数学作业不会做，会主动来问，不需要等我检查作业。一点拨就懂了，但第二天又会有新问题，依旧靠自己解决不了。不过，知道自己不懂算是个好的开始吧？</p>

<p>昨天晚上，她说今天学的直线、射线和线段。这个好简单，之前的都好难。</p>

<p>我说，我出道题目吧。在纸上画五个点，你看看一共可以连出多少条线段。</p>

<p>她说，我去拿张纸来。</p>

<p>画了半天，数错了。可可说，我脑子好乱啊。</p>

<p>我说，我来教你方法。我们先把你画的点标上数字编号，从 1 标到 5 。然后你每次连一条线段，就把两端的数字记在图案下方，顺着写整齐。最后，数下面的记号。</p>

<p>这次她算对了。但是，好麻烦啊，为什么要用这么麻烦的方法。我想偷懒就会搞错。</p>

<p>我说，其实，你不需要画图，只用写数字就可以了。但是不要随便连线。1 号点先和 2 号点连，再和 3 号点连…… 顺着数字从小到大，这样就不会搞错。不需要真的把连线图画在纸上，在脑子里相像就可以了，只需要在纸上写下 1 号点能和几个点连成线段就可以了。这里，你写个 4 。然后再看 2 号点。</p>

<p>她画了一点时间，终于发现了规律，列出了算式 4 + 3 + 2 + 1 = 10 。</p>

<p>我说看吧，其实这不是一道绘图题，用数学方法，它转化为一道计算题了。</p>

<p>可可好像有点兴趣了，说我现在可厉害了，我能算 10 个点的问题。我说你试试。可可的悟性没我相像的那么高，并没有直接写出 9 到 1 的等差数列，每个数字都想了一会，直到 4 以后才有点把握后面应该是 3 2 1 。但是，面对长达 10 个数字的加法算式，可可说，我知道方法了，这个算起来太麻烦了，我不算了。</p>

<p>我说，你不是学了乘法吗？其实，这个问题不一定要用加法做，我告诉你怎么用乘法解决这个问题，就不用算这么麻烦的算式了。你看，刚才你连线的时候，把 1 号和 2 号连起来之后，从 2 号做起点就不连 1 号了。因为 1 到 2 和 2 到 1 是同一条线段。两个点之间只可以连一次。但是，如果我们每两个点都连两次，那么这 10 个点，每个都可以连出去 9 条（射线）。</p>

<p>最后，总共画了 10 组，每组 9 条射线，一共是 10 * 9 = 90 条。因为我们每条线段都计算了 2 次，所以答案就是 90 / 2 = 45 条线段。</p>

<p>有了这个方法，100 个点的问题你也能算出来了吧。</p>

<p>可可问，家里有没有有趣一点的数学书，我要看。我翻出了前年带着云豆读过的《我的第一本数学书》。可可翻了一下说，怎么这么多字啊。我说，周末我带着你读。</p>

<hr />

<p>早上起床的时候，可可问，今天我能不能把昨天那本书带到学校去？</p>

<p>希望她能发现一点点数学的乐趣。</p>
]]></description>
         <link>https://blog.codingnow.com/2025/12/coco.html</link>
         <guid>https://blog.codingnow.com/2025/12/coco.html</guid>
         <category>育儿</category>
         <pubDate>Tue, 16 Dec 2025 09:50:28 +0800</pubDate>
      </item>
            <item>
         <title>最近玩的几款卡牌构筑类电子游戏</title>
         <description><![CDATA[<p>最近玩了几个卡牌构筑类的电子游戏，觉得颇为有趣，值得记录一下。</p>

<p>首先是 Decktamer（训牌师）。我玩了十几个小时，把初级难度通关了。</p>

<p>它的新设计是用卡牌构筑的形式重新做了一个宝可梦。和杀戮尖塔开创的战斗结束后抽卡，用战斗胜利的奖励钱买卡、洗卡、升级的模式不同。它的战斗卡是不需要洗的，战斗中死亡就直接消失；新卡片是在战斗中捕获对手获得。加强战斗卡的方式主要是用道具卡杂交战斗卡：从一张战斗卡上抽取需要的技能，加到另一张战斗卡上。</p>

<p>战斗过程更像是万智牌那种更传统的卡牌战斗模式：摆放战斗卡都场上，再由上场的卡片发动能力。这种传统战斗模式不同，发动战斗技能没有额外的资源消耗，而修改成每回合必然从卡片上所有技能中选择一个。这可以避免给同一张卡片合成太多能力造成的不平衡。更多能力往往只是增加了容错性，可以应付更多场景。</p>

<p>玩家卡牌被分成了两个牌堆：战斗卡堆和道具卡堆。这种双卡堆的模式最近的卡牌构筑游戏中比较常见，下面还会再提到。不过这里道具卡堆并不是抽牌堆，更像是一个道具背包，可以随时使用。</p>

<p>我在简单难度通关的感受是：只有最终 boss 有挑战。而这种挑战更像是一个谜题。所以第一次面对最终 boss 我没有一次通过。而是熟悉了它的技能，第二次刻意针对这些技能来升级牌组，这样才通关。整个游戏给我的感觉是，解密成分更重一些，也就是该如何养卡才能解决对手。所以游戏里（简单模式下）有无限次的 undo 。我相信选择更高难度后会有不同的感受。不过暂时没有玩下去。</p>
]]><![CDATA[<hr />

<p>第二个游戏是 Rogue Hex 。大约玩了 7 个小时，四个设计角色中，前三个获得了胜利。</p>

<p>这个游戏用卡牌构筑的形式重新实现了一个简化版的文明。这是我之前就特别想做，但是没想到合适方案的点子。所以，我在初玩时感觉相当有趣。能把卡牌构筑的乐趣融合到 4x 游戏中相当不错。不过，玩到游戏后期，数值还是有点崩，往往中盘就几乎碾压对手了，但为了胜利，依旧需要机械性的玩很多回合。</p>

<p>当然，我觉得它的核心机制设计的还是挺好的。这是个新游戏，平衡还有很大的改善空间。</p>

<p>在这个游戏中，基本资源分别是：劳动力用于抽牌打牌、食物用来发展发展城市、计划用来移动地图单位、信仰用来重置、金钱用来替换前面的基本资源。这些资源有仓储上限，回合结束的时候超过上限的部分会浪费掉。我认为“仓储限制”是让玩家有更多选择的核心设计点之一。</p>

<p>无仓储限制的成长涉及两个点数，科技用于增加正面 buf （类似杀戮尖塔中的神器）以及生产用于增强牌组。我认为设计两个的原因也是给玩家提供选择。大多数情况下，发展科技就会降低生产的增速，反之亦然。</p>

<p>像文明那样，玩家可以在版图上探索采集一次性资源转换为一次性消耗的卡片；扩展城市获得永久资源再用卡牌转换为永久建筑或用版图上的工人单位采集转换为卡片。这部分对文明原本的系统还原的挺不错的（至少在每局游戏的前半段很好）。</p>

<p>游戏循环基本上是用抽牌的手牌积累科技点、生产点和上述的基本资源。用这些点数兑换成发展。和文明一样，指挥地图单位探索和攻击对手也是发展重要的一环。把文明游戏机制中的各种操作翻译为打牌，最大的区别在于：原本玩家可以自由分配每个回合的行动，而在卡牌模式下，需要根据抽到的手牌做决定。这有两个方面的变化：其一、原始机制下提供给玩家的选择非常多，容易产生选择困难；手牌是有限的，玩家可以聚焦在有限行动选择中。其二、可选行动有了随机性，玩家需要根据牌组、抽牌堆、抽牌能力去管理随机性。这也是卡牌构筑类型游戏的核心玩法。</p>

<p>但嫁接卡牌构筑类型和 4x 类型系统有一个需要设计的地方： 4x 游戏中每个回合给玩家的选择都是有意义的，如果把太多东西做成卡牌供玩家选择，而每个回合选择受限，很可能极大的降低游戏的容错性，变得太看脸。所以，在这个游戏中，劳动力即用于支付打牌成本，又可以用来抽牌。这等于提供给玩家一个主动增加选择的能力，而不是像杀戮尖塔中那样，抽牌本身也是特定卡片的技能。</p>

<p>我发现很多类似游戏（下面会再次提到）都有这个设计：允许玩家主动花一个特定资源点，就可以抽牌。</p>

<p>但是，这类游戏成长点太多。不像杀戮尖塔那样只是提高和优化卡组，每局游戏后期很难不崩掉。要么前期开荒死掉，要么后期碾压变得选择没有太大意义。我觉得这个游戏还可以改进，我也很期待会使用怎样的解决方法。</p>

<hr />

<p>最后一个游戏是 Dawnmaker ，也是最近我最喜欢的一个。我玩了 30 个小时，所有难度都通关了。</p>

<p>这个游戏回答了我很多之前没想到设计解法的问题。它对卡牌构筑类型做了更多的保留，没有卡牌战斗部分，更接近一个生存类型的基地建造游戏。玩这个游戏时让我想到了 retromine 和 Stellar Orphans （星际孤儿）。但很多设计处理的更好。</p>

<p>这个游戏就是打出卡牌在六边形棋盘上建造建筑让基地活下去。没有敌人，无需战斗，需要对抗的只是逐步增加的粮食需求。</p>

<p>单局游戏内只有五种资源：粮食、科技点、工程点、行动点、胜利点。赢得单局游戏可以获得金钱，金钱用来升级卡组，在后续的游戏局种获得优势，以对抗难度逐步上升的挑战。</p>

<p>游戏设定了两个卡堆：手牌堆和市场建筑堆。这两个卡堆分开抽牌。手牌在当前回合打出的工程点可用来在市场堆购买建筑，并在版图上建造。建筑放在版图上就有了持久能力，通常用于配合手牌更有效的获取资源。和 Rogue Hex 不同（它没有双卡堆），购买的建筑卡必须立刻摆在版图上，而不是置入抽牌/弃牌堆。从我游戏的感受看，这是个没有巧妙的改良设计。</p>

<p>星际孤儿也有市场设定，但受传统卡牌构筑规则的影响，还是设计成从市场购买，投入弃牌堆，抽到手牌使用的循环。控制前期卡和后期卡的方式也只是简单的通过市场掉率决定。但在 Dawnmaker 中，通过市场卡分级严格区分了前期卡和后期卡。如果城镇中心没升级到特定等级，对应的卡片也不会在市场上出现。刷市场只需要消耗科技点，这点和 Rogue Hex 用劳动力抽卡异曲同工。但双卡堆设计避免了直接抽行动牌。因为这类游戏中，行动牌和建筑牌本质上是有区别的。Rogue Hex 用一次性消耗卡表达建筑，我觉得是因为没能跳出传统卡牌构筑规则。</p>

<p>在版图上摆建筑是这个游戏的核心玩法。在非卡牌构筑游戏中非常常见，我也见过很多企图和卡牌构筑类型结合的游戏，只有这个我认为结合的最流畅。在星际孤儿中也有四个 slot 用于安装卡片获得永久能力，但只是点缀，而且安装位置并不改变游戏。</p>

<p>Dawnmaker 的建筑摆放相当重要。甚至比构筑行动卡堆更重要。其变化也非常多，留给玩家很大的选择空间。这种在六边形弃牌上填格子的玩法在桌游中很常见，技巧深度和乐趣很有保障。最近几年也有很多电子游戏基于这类玩法，但我觉得更像桌游电子化，而我玩 Dawnmaker 的感觉则更接近电子游戏的体验。</p>

<p>Dawnmaker 在 steam 上有 demo ，有兴趣的同学可以试试。但 demo 只能玩第一个角色，我在正式版本中尝试了另外两个角色，让我惊讶的是，其实三个角色共享同一套完整的卡池。仅仅只是初始卡组不同，但游戏风格差别非常巨大。而且，游戏中并不设常规卡和稀有卡，抽到的概率是一致的。也就是说无论你用那个初始卡组开局，都可能抽到所有卡片，概率是一致的。但玩游戏的感受却并没有那种：我抽到了特定卡以后，游戏就变容易了的感觉。在熟悉了游戏之后，几乎任何卡片搭配都可以玩出花来。</p>

<p>三个初始卡组（角色）标注的是 regular ，complex ，extreme 。我一开始以为是三档难度，通过调整数值减少容错性，需要逐级更熟悉游戏规则才能玩下去。</p>

<p>通关后的感受是：的确越后面的角色需要对游戏更熟悉。但并没有在数值上减少容错性，而是更复杂的角色代表的需要更复杂的卡片 combo 。三个角色玩的体验也很不一样。</p>

<p>虽然在玩第一个角色时，我能感受到不同的流派：用大块农田组合堆砌粮食产能、用主动激活的方式生产粮食、用科技转换粮食等等。胜利点的来源也可以来源于建筑、科技等。但直到我玩第二个角色才发现，靠版图主动技能的组合也可以另成一番景象。而第一个角色更多的思考如何在版图上摆放建筑的位置。第三个角色更是要求不断的拆建建筑，让版图的布局变成动态的。学会这些玩法后，回头在不同角色中也可以实现（因为共享一个大卡池）。</p>

<p>Dawnmaker 的数值调的非常好。每局游戏的生死总是在一线间。尤其是后面的角色更偏重于主动触发版图技能，相比调配行动手牌多了一层选择。有时候看似死局，仔细思考后居然是有解的。同时又需要一点点运气，概率管理原本就是卡牌构筑游戏的核心，在小丑牌中体现得淋漓尽致，Dawnmaker 也有类似的体验。</p>

<hr />

<p>在游戏规则上，给我的一点启发：</p>

<p>如何把卡牌构筑结合到基地建设类别游戏上？加入版图元素，把建筑卡堆和行动卡堆分离是个不错的设计。</p>

<p>游戏资源种类不需要太多，不需要完全用卡牌表达。传统的卡牌构筑基本规则中，资源点都是靠当前回合的行动手牌生成，不留到下个回合。但建设类游戏的建造资源完全可以跨回合保留积累，但需要强调仓储上限。这样就可以提供给玩家足够的行动选择和组牌的多样性。</p>

<p>非战斗类游戏，可以用不断上升的需求来制造压力。而需求的上升速度可以和玩家的发展规模挂钩。这样玩家就必须在长期发展和短期生存困境上做出抉择。从自己组建的卡堆中抽卡是卡牌构筑类游戏的核心乐趣来源：提供给玩家更丰富的风险管理手段。</p>
]]></description>
         <link>https://blog.codingnow.com/2025/12/deckbuilder_games.html</link>
         <guid>https://blog.codingnow.com/2025/12/deckbuilder_games.html</guid>
         <category>游戏</category>
         <pubDate>Mon, 15 Dec 2025 16:32:32 +0800</pubDate>
      </item>
            <item>
         <title>欧陆风云5的游玩笔记</title>
         <description><![CDATA[<p>最近一个月共玩了 270 小时的欧陆风云5 ，这两天打算停下来。最近在游戏后期打大战役时，交互已经卡得不行。我已经是 i9-14900K 的 CPU ，估计升级硬件已经无法解决这个问题，只能等版本更新优化了。</p>

<p>ps. 其实只要把游戏暂停下来立刻就不卡了。虽然我直到这个游戏需要的计算量非常大，但是卡交互操作肯定是实现的不对。因为这并不是因为渲染负荷造成的卡顿，可以让游戏时间流逝更慢一些，也不应该让鼠标点击后的界面弹出时间变长。</p>

<p>在暂置游戏前，我先把一些关于游戏设计上的理解先记录下来。<a href="https://blog.codingnow.com/2025/11/eu5.html">也是对上一篇的补充</a>。</p>

<p>在最初几十小时的游戏时间里，我一直想确认游戏经济系统的基础逻辑。和很多类似策略游戏不同，欧陆风云5 在游戏一开始，展现给玩家的是一个发展过（或者说是设定出来）的经济系统版图。玩家更需要了解的是他选择扮演的国家在当下到底面临什么问题，该怎样解决。这不只是经济，也包括政治、文化和军事。而很多游戏则是设定好规则，让玩家从零开始建设，找到合适的发展路径。</p>

<p>大多数情况下，EU5 玩家一开始考虑的并不是从头发展，所以在游戏新手期也没有强烈的理解游戏底层设计细节的动机。不过游戏也有开荒玩法，在游戏中后期势必会在远方殖民、开拓新大陆；甚至游戏还设计了让玩家直接转换视角以新殖民地为核心来继续游戏。但即使的重新殖民，在四周鸟无人烟的地方开荒，和在已有部分发展的区域附近拓展也完全不同。</p>

<p>我十分好奇这样一个复杂的经济系统是怎样启动起来的，所以仔细做了一点归纳笔记。不一定全对，但很多信息在游戏内的说明和目前的官方 wiki 都不完整，只能自己探索。</p>
]]><![CDATA[<hr />

<p>游戏中的一切来源于“原产”，官方称为 ROG ，比较类似异星工厂里的矿石。上层的一切都是从原产直接或间接获得。版图上的任何一个最小单位的地块，只要上面有人口，就会不断生产出唯一品种的原材料进入这个世界。它和国家控制力、市场接入度都无关系。比原材料更高级的产品都是由原产直接或间接转换而来。</p>

<p>货币本身在世界中不以资源形式存在，货币本身也没有价值。货币的存在在于推动包括原产在内的原材料和产品等在世界中的流动。所以，世界中即使不存在经济活动、没有货币，亦或是货币急剧膨胀，这些因为国家破产而债务消失等让货币总值急剧变化的行为也不会直接影响这个世界中的物资变化。即没有很多游戏中直接用钱凭空兑换成物资的途径。</p>

<p>换句话说，如果整个世界缺铁，那么只能通过生产手段慢慢的产出，再多的钱也无法变出铁来。但分配更多的人力去生产、更高的科技水平可以获得铁产量的提升、使用更高效的配方、各种提升生产率的增益等等都可以加快铁的产出速度。</p>

<p>从一个世界的局部看（这是一般玩家的视野），获得原材料的方式有三种：</p>

<ol>
<li>养活更多的劳工或奴隶开发对应原产。</li>
<li>在合适的地理位置上用劳动或奴隶生产。</li>
<li>从附近的市场进口。</li>
</ol>

<p>第一种方式，玩家拥有对应原产地，然后在地皮上增加人口。但新增人口是农民，还需要从农民升级成劳工。国家 buf 中，默认只有原住民满意度对产量有轻微的增益。</p>

<p>第二种方式，玩家有更大的自主性。以铁为例，只要是湿地地形或者地皮邻接湖泊，就可以主动产铁。这种生产除同样需要劳工外，还有原料开销：把炭转换为铁。这种生产方式直接被市场接入率打折，即离市场越远的地方单位人口的生产效率越低，但同时有更多增加生产率的增益途径：最基本的就有当地劳工识字率和市民满意度。和虽然和第一种方式一样需要劳工，但游戏似乎会先满足原产需求的劳工，多出部分才进行建筑生产。所以在劳工不足时，若需进行建筑生产，需要主动减少原产等级。因为生产建筑可以由玩家主动关闭，但原产似乎不行。</p>

<p>第三种方式，通常需要在本地市场拥有一定的市场容量。在不考虑成本时，甚至可以亏本进货。对开荒来说，进口原料比进口成品的优势就在于占用更少的贸易容量。</p>

<p>为什么上面以铁举例，因为铁是开荒时最重要的资源。虽然木头和石头也很重要，但游戏把木材、粘土、沙、石头设定为一般物资，所有地块都有一个很低的默认产能，从市场角度看，根据市场规模，每个市场总有一定量的供给。但铁不属于这种物资。</p>

<p>建造建筑需要的基本材料是砖头，砖头可以通过基础建筑，从粘土或石头转换。</p>

<p>开发原产需要的基本材料是木头或工具。大多数基础建筑的生产配方里都需要工具。而工具在非城市生产建筑中，只有乡村市场可以把铁转换为工具。所以、如果开荒时的市场中缺铁，就只能通过进口。进口制造工具的铁比进口工具更能利用上贸易容量（铁和工具的单位贸易容量相同，但铁到工具以 4:5 转换）。</p>

<p>铁矿在版图上相对其它资源更少，所以一般开荒需更关注市场覆盖下有无湿地地形或有无湖泊，同时需要用充足的木材供应，可以把木材转换为炭再转换为铁。</p>

<p>一旦单一地块上的人口超过 5000 ，就可以升级为城镇，这种生产就有更多选择。以工具制造来说，城镇里就多出了用石头或铜转换为工具的配方。尤其是石制工具的途径，虽然效率很低，通常利润也很低，但贵在石头有保底产出。城镇的升级需要砖头和玻璃，玻璃可通过沙子转换，而沙子有保底产出或通过工具加木材转换。</p>

<p>开荒期间，解决了木头、砖头、玻璃、工具这四种基本货物（前三种是各种基础建筑建设需求，最后是大部分生产转换配方的必须）后，就要考虑提高产能的问题，这里的核心之一是纸。因为劳工识字率影响着生产率。纸是印书的原料、书是图书馆的维持品，而图书馆以及更多提高识字率相关的建筑都需要书。</p>

<p>造纸术需要纤维作物或皮革或布匹。纤维作物的基本生产方法是在对应农场通过牲畜木材工具制造；而牲畜则在耕种村落通过工具加粘土转换；皮革则可以在森林村落通过沙加焦油和野味转换，其中焦油在一般木材产区都可以通过木材转换得到。</p>

<p>另一个重要的资源是人口。它在游戏中和钱一样重要、甚至更重要。因为一切的生产行为都需要人。对开荒而言，升级到城市对效率影响最大，这里的硬性要求就是 5000 基础人口。除了主动殖民，就是本地土著转换、周边迁移（集中）以及自然生长率。这些都可以通过内阁行为略微加速，同样关键是修定居点（同时加移民吸引度和自然生长率）。定居点除了 250 农民外的维持成本是石头、木头、羊毛、野味。前两个一定有保底产出，后两个不是必须，缺少只会让效率打折，但供应充足可以发挥全部性能。定居点和乡村市场都占用农民，在最初阶段，我感觉乡村市场更重要一些，毕竟可以制造工具，还能提供宝贵的贸易容量。</p>

<p>不同阶层人口除了产生固定需求（吃掉本地市场的部分产出）外，更基本的需求是食物。EU5 中的食物系统设计，我觉得也是很巧妙的。</p>

<p>食物并不是货物的一种，而是和钱一样，表示为一个单一值。最主要的食物来源是生产带食物属性的货物（被称为食物原料）的副产品。属于食物原料的货物，被设定了不同的食物倍率，这就让有些食物原料产生食物的效率更高。不工作的劳工默认会以一个较低的产能生产食物，所以不必担心多出来的劳动力被浪费。另外，农民在森林村落里，虽然产品皮革并非食物原料，但生产行为本身被设定了食物产能。另外，农村相对城市有额外的食物产能的乘数增益。</p>

<p>食物的仓储按省份为单位计算，省份归属的若干地块共享一个食物仓库。在每个省份会优先填满仓亏，多出的部分卖给了所属市场，这里是不计市场接入度的。而一旦仓库有空间，就会从所属市场购买。战争是影响它的一个变数。因为军队也会从这个仓库中获取食物，围攻会阻止市场上的食物交易。</p>

<p>食物在本地市场上的交易行为会影响到本地食物价格，购买食物的开销和销售食物的收入是分开计算的，全部通过国库完成。市场间并不能单独交易食物，但通过对有食物原料属性的商品的交易，会产生附带的食物流通（但并不会产生额外的货币流动）。我觉得理论上会出现市场仓库中的食物储量为 0 ，但依然出口食物原料的情况，但实际玩的时候并没有发现，所以不清楚游戏怎样处理。但我猜测，食物流通是在单独层面计算的。既然超出市场食物容量的食物似乎就消失了，那么也可以接受万一食物储量为 0 却继续出口的情况，把储量设为 0 即可。</p>

<hr />

<p>最后，写写我对税收的看法。</p>

<p>简单说，游戏里的经济活动产生了税基。税基中按王室影响力直接把钱进入国库，另外的钱按阶层影响力分到了各阶层。但玩家可以对阶层分得的钱征税，让这些钱进入国库。</p>

<p>看起来，在不造成阶层不满的前提下，税率越高，国库收入就越高。但实际我玩的感觉是，其实税基才是整个国家的收入，国库仅仅是玩家可以主动调配的部分。阶层保留更多的收入，也会投入到国家发展中去，只不过有时不是玩家想要的方向，甚至是负方向。例如当玩家想削弱某个阶层的影响力时，阶层把钱投入都修建扩大本阶层影响力的建筑上。但总的来说，如果国库钱够用，更低的税收更好。因为税基相同时，税收影响的是分配。低税收必定增加阶层满意度，带来的正面增益是额外的。正所谓藏富于民。</p>

<p>而影响税基最重要的是地区控制度。当然地区控制度不仅仅影响税基，还影响了更多建筑的效率。从这个意义上来说，地方分权比中央集权更有利于经济发展。分封属国，尤其是朝贡国，比大一统国家会获得更好的经济局面。</p>

<p>但权力分配在游戏中也相当重要，因为它直接影响调配价值观的能力。价值观在一盘游戏进程中必须配合时代发展而演变才能更好的发展经济。而集权以及王室影响力是权利分配能力的来源。</p>

<p>所以说，最终玩整个游戏的体验还是在和面，只是多出了一份历史感。有了真实历史这种后验知识，才更为有趣。</p>
]]></description>
         <link>https://blog.codingnow.com/2025/11/eu5_notes.html</link>
         <guid>https://blog.codingnow.com/2025/11/eu5_notes.html</guid>
         <category>游戏</category>
         <pubDate>Fri, 28 Nov 2025 18:53:55 +0800</pubDate>
      </item>
            <item>
         <title>嵌入主线程消息循环的任务调度器</title>
         <description><![CDATA[<p>最近在网友协助下把 <a href="https://github.com/cloudwu/soluna">soluna</a> port 到包括 wasm 在内的非 windows 平台。其间遇到很多难题，大多是多线程环境的问题。因为 soluna 的根基就是基于 <a href="https://github.com/cloudwu/ltask">ltask</a> 的多线程调度器，如果用单线程实现它，整个项目的意义就几乎不存在，所以它是把项目维护下去必须解决的问题。</p>

<p>好在 lua 有优秀的 coroutine 支持，它可以把运行流程抽象成数据，而 Lua 本身并未限制数据的具体储存方式，所以完全可以存在于内存堆中，脱离于 C 栈存在，这为各种在 C 环境下的多线程难题开了后门。C 语言依赖栈运行代码逻辑，而栈绑定于线程，线程调度通常由操作系统完成，所以用常规方式无法让代码跨线程运行：即，无法通过常规手法让一段代码的流程前半段在一个线程运行，而用另一个线程运行后半段；但是，在 C 上建立一个 Lua 层，则很容易绕开这个限制，只用标准方法就可以自由控制程序运行流程。</p>

<p>上一次发现利用一些技巧就可以完成一些看似不可能却的确可行的调度方式是 <a href="https://blog.codingnow.com/2022/09/multithread_lua_vm.html">多线程串行运行 Lua 虚拟机</a> 。</p>

<p>简单复述一下当时的需求：</p>
]]><![CDATA[<p>希望可以在单个 Lua 虚拟机内模拟多线程并发。当一个 Lua 的 coroutine 运行到 C 函数中时，若此刻 C 函数希望阻塞等待一个 IO 请求，常规的方法是 yield 回 Lua 虚拟机，让调度器持有一个 Lua coroutine 的状态，待完成 IO 请求后，再由调度器 resume 这个 coroutine 。这样做的难题是，运行到一半的 C 函数，上下文状态还在 C 所属线程的栈中，一旦 yield 回 Lua 虚拟机，必须放弃 C 栈上的状态，并在下次 resume 时可以重建。这通常难以实现，这也是为何 Lua 的 coroutine C api 难以理解又很难使用的原因。尤其使用第三方 C 库，几乎没可能适配。</p>

<p>另一个折中的方法是让 Lua 虚拟机在 C 函数中阻塞，硬等到 IO 操作完成。但在阻塞过程中，无法使用这个 Lua 虚拟机。若使用者期待 Lua 虚拟机中多个 coroutine 以多线程方式并行工作，恐怕会失望。即使其它 coroutine 的业务和 IO 完全无关，一个 IO 阻塞操作会让它完全无法并行工作。</p>

<p>变通的方式是（在编译时）打开 Lua 的线程锁。在调用 IO 阻塞前解开线程锁，只要 IO 操作本身不涉及对 Lua State 的操作，那么 Lua  解释器在调用 C 函数前的那一刻会解开线程锁，这样就可以允许阻塞操作过程中，Lua 虚拟机可以执行其它操作。</p>

<p>线程锁本身依赖系统线程库的调度器。不适合像 ltask 这样自己实现任务调度（即在有限个系统线程下调度远超系统线程数的任务）。但是，我们可以配合 ltask 实现类似的锁机制。这就是之前这个 patch 实现的东西：Lua 层调用可能阻塞的 C 函数前加锁通知 ltask 调度器，在 C 函数中，用户主动在阻塞操作前解锁。ltask 的调度器在 C 函数返回前就将虚拟机提前放回调度表。当阻塞操作完成后，重新加锁会等待调度器完成（如果有）正在运行的在同一个 Lua 虚拟机上的任务完成。这样，整个 Lua 虚拟机实质上还在串行运行其中的任务。而使用者看起来在一个 coroutine 尚未 yield 之前就开始运行另一个 coroutine ，直到其它 coroutine yield 后再继续未完的工作。同一个 Lua 虚拟机的多个 coroutine 是在多个操作系统线程上完成的，但却保持串行。</p>

<p>这个 patch 最终并未合并进 ltask ，因为我觉得它对使用者有更高的要求。但经此，我开了不少脑洞，明白在必要时牺牲一些复杂度就可以完成一些超乎寻常的任务。</p>

<hr />

<p>这次我面临的是新的问题：sokol 并未设计成线程安全。api 不能并发。一开始我并不想使用复杂的解决方案，以为只要保证 sokol 不并发就够了。期间遇到的问题是 <a href="https://blog.codingnow.com/2025/08/setwindowtext_deadlock.html">Windows API 死锁</a> ，也很容易绕过。</p>

<p>对于图形 API ，我只是简单的将图形 API 调用都塞在同一个 render 服务中。并在主线程的 sokol 回调函数中利用一个信号量和渲染过程同步。虽然 Direct3D ，Matal ，Vulkan 这些为多线程设计的底层图形 API 这么用都没有问题，但 OpenGL （在 Linux 上开启）却将状态放在当前线程上。一开始，我们通过额外调用 MakeCurrent 绕开限制，但在我们向 wasm 移植时却遇到障碍。</p>

<p>最终，我还是希望找到一个方法让所有图形 API 的调用都真正从主线程，也就是 sokol 提供的 callback 函数中发起。而不是用信号量同步，让它们在其它工作线程运行。</p>

<p>难题在于，主线程是通过事件消息循环驱动的，没有全部的控制权。不适合在其上实现任务调度器。一个任务调度器最好有所有时间片的控制权，它才好简单有效的分配时间片，没有任务时可以休眠而不是在事件循环没有新事件时强制休眠。我不想为这种特别的工作方式改造 ltask 的任务调度器，让主线程的事件回调函数伪装成一个功能不完整的特殊工作线程。我实际需要的是：把一个 Lua 虚拟机内的特定任务分配给主线程回调函数运行，在没有这种特定任务时，其它任务还是交给 ltask 做常规调度。</p>

<p>细想之下，解决方法和上一个需求有异曲同工之处：Lua 在启动这种特殊任务（必须在主线程回调函数内运行）前通知调度器。这时把虚拟机暂时移出调度表，而在主线程的回调函数中（通过信号量）发现有新任务到来，就接手处理特殊片段。处理完毕后，再把它归还给调度器。</p>

<p>通过这个方案，我们顺利把 soluna port 到 wasm 环境，同时简化了 Linux/OpenGL 实现。当我了解到 wasm 上有 pthread api 和原生 web worker api 两套多线程 api 后，我又信心满满的想用 worker api 来实现。但最终未能如愿。<a href="https://github.com/cloudwu/soluna/pull/54">具体讨论在这个 issue 中</a> ，倒不是完全做不到，而是我觉得不应该牺牲太多复杂度。比如把 soluna 中所有的 IO 操作都转发到主线程中运行（这是 web worker 的限制所在，也是 wasm pthread 原本要解决的问题）。</p>

<hr />

<p>昨天发现了上面解决方案实施中的一点纰漏：虽然给 ltask 打了个洞，可以在系统主线程夺过指定任务运行，但在交换控制权回调度器时，忽略了 ltask 的所有工作线程可能因为没有任务而全部休眠的可能性。仅仅把任务推回（线程安全的）任务队列是不够的。还需要重启调度器（如果处于休眠状态）。具体讨论见这个 <a href="https://github.com/cloudwu/soluna/issues/58">issue</a> 。</p>

<p>ps. 自从搬家后，我的 Linux 机器一直没有开机。昨天为了在 Linux 环境下测试，才重新装起来。bug 虽然重现，但视乎在我的机器上更为严重：一旦程序失去响应，整个系统都卡住了，甚至冷启动都没用。直接把机器弄死，而且五分钟内都开不了机（BIOS 进不去，屏幕无信号）。我怀疑是显卡驱动的 bug ，因为太久没升级系统，头一次升级还失败了，pacman 报告出现依赖问题拒绝更新。强删了几个 Electron （这个毒瘤）的几个历史版本后，系统升级才得以继续。最后更新了最新版的 Nvidia 包似乎就一切正常了。</p>
]]></description>
         <link>https://blog.codingnow.com/2025/11/task_schedule_in_eventloop.html</link>
         <guid>https://blog.codingnow.com/2025/11/task_schedule_in_eventloop.html</guid>
         <category>lua与虚拟机</category>
         <pubDate>Sat, 22 Nov 2025 13:52:56 +0800</pubDate>
      </item>
      
   </channel>
</rss>
