
如果你在 Besiege 里用轮子、活塞和逻辑门搭建过一些能“动起来”的装置那么“在三进制计算机里做一块全加器”这个目标会是一个非常有趣的进阶挑战。今天这篇文章我从三进制全加器的逻辑设计讲起再落到 Besiege 机械结构的具体拆分思路完整记录机械三进制计算机开发中最关键的这个环节。内容偏工程向适合对 Besiege 机械感兴趣、或想了解非二进制逻辑电路设计的玩家与开发者读完你不仅能理解三进制全加器难在哪里、怎么设计还能照着搭建思路在自己游戏存档里从零拼出一个可以验证的全加器模块。1. 机械三进制计算机与全加器为什么值得做1.1 Besiege 里也能造“计算机”Besiege 是一款以物理沙盒建造为主的游戏看似目标只是造攻城机器但它提供了足够丰富的机械模块梁、轮子、活塞、弹簧、铰链、旋转关节、逻辑门、传感器甚至炸弹。这些模块组合起来可以做成复杂的机械传动系统也可以做成类似早期机械计算机的逻辑装置。普通玩家在 Besiege 里搭投石机、坦克、飞机而计算机制作者在 Besiege 里搭的是“能计算的机器”。你可能见过用二进制逻辑门搭出的内存、加法器、甚至简单 CPU这类作品的核心思路就是用逻辑门与机械结构表示“0 / 1”两个状态再用这些状态完成运算。三进制机械计算机则是更小众、更偏门的方向。它不依赖“通 / 断”二进制而是用三种状态表达一位数字。在 Besiege 这种物理引擎下三进制的实现方式反而有可能比二进制更自然因为一个转轴可以用三个角度位置表达三个状态不需要额外组合多路信号。1.2 为什么选择三进制经典数字电路中二进制占了绝对统治地位因为电子器件最容易区分“低电平 / 高电平”。但三进制在数学上并非没有优势尤其平衡三进制使用-1 / 0 / 1三个数字可以直接表达负数某些运算规则比二进制更对称、更直观。在 Besiege 机械环境中三进制更加诱人。原因有两点二进制信号必须用“有没有位移”“有没有电压”来表达需要一个明确的阈值但机械零件受物理引擎影响很难精确卡在“有 / 无”两个边界。三进制可以用一个旋转关节或多个挡块的位置直接表示比如状态 0旋转关节角度为 0° 状态 1旋转关节角度为 45° 状态 2旋转关节角度为 90°这种“连续位置表达多值状态”的方式比纯逻辑门组合更接近机械本质也更容易让新材料玩家理解。1.3 全加器机械计算机的起点全加器是数字电路里最基础、也最重要的运算模块之一。它能把两个一位数字相加并接受来自低位的进位输出本位和以及向高位的进位。有了全加器就能并联成多位加法器有了多位加法器就能继续扩展减法、乘法、除法等更多运算。因此在做机械三进制计算机时全加器往往是整个运算器开发的第一块里程碑。把三进制全加器在 Besiege 里跑通后面很多模块就有了稳定的复用单元。2. 从二进制全加器到三进制全加器2.1 先复习二进制全加器二进制全加器有三个输入本位加数A、加数B、低位进位Cin两个输出和位S、进位Cout。二进制一位加法的规则是A B Cin 2 * Cout S其中S是除以 2 的余数Cout是除以 2 的商。真值表如下ABCinCoutS0000000101010010111010001101101101011111二进制全加器的逻辑表达式是S A ⊕ B ⊕ Cin Cout (A AND B) OR (Cin AND (A ⊕ B))其中的核心思想是先把两个加数相加如果溢出就向高位进位。2.2 三进制全加器的定义三进制全加器的输入从两个“0 / 1”变成了三个可能的数字0 / 1 / 2。它的输入仍然有三个一位三进制加数A、一位三进制加数B、以及来自低位的三进制进位Cin。三进制一位加法的规则是A B Cin 3 * Cout S也就是说A B Cin的和值除以 3商就是进位Cout余数就是本位和S。为什么进位也用三进制而不是只有 0 / 1因为两个三进制位相加最大值是2 2 2 6而 6 用三进制表示是6 2 * 3 0也就是说进位可以是 2而不只是 0 或 1。这是三进制全加器和二进制全加器非常关键的区别。2.3 三进制全加器的统一真值表因为加法结果只取决于A B Cin的总和total我们可以用一张非常紧凑的表来描述三进制全加器totalCoutSum000101202310411512620从这张表可以看出total 3时不产生进位Sum total。3 total 5时产生 1 次进位Sum total - 3。total 6时产生 2 次进位Sum 0。这个“查表式”的设计思路比推导布尔表达式更适合 Besiege 机械实现。因为机械结构很难表达复杂的逻辑门网络但可以用“位置”“角度”“凸轮”等物理方式把不同的total值映射到不同输出位置。2.4 关于平衡三进制的扩展说明三进制还有一种更优雅的编码方式平衡三进制也叫对称三进制使用-1 / 0 / 1三个数字。它的优势是负数可以直接表示不用单独引入符号位进位规则也更加对称。但在机械计算机的实际开发中平衡三进制需要一个额外的问题如何处理负角、负位移比如用转轴角度表示-90° / 0° / 90°那么在加法模块中两个负角相加就可能超出机械限位。因此本文第一版全加器采用0 / 1 / 2编码等主逻辑跑通后再扩展平衡三进制版本会更稳妥。3. Besiege 中表达三态信号的几种方案在开始动手搭建之前先确定“三态信号”在游戏里到底用哪种物理形态表达。这个选择直接决定了后面所有模块的设计复杂度。3.1 方案一旋转关节角度用一个旋转关节Rotation Joint或铰链通过限位块限制它在三个固定角度之间切换例如数字 00° 数字 145° 数字 290°好处是状态直观、便于观察配合齿轮或连杆可以和其他模块联动。缺点是传动过程中存在间隙Backlash角度可能漂移高负载下容易滑齿需要加锁定结构。3.2 方案二多路二进制信号组合用两根活塞或两个逻辑门分别表示几个布尔信号然后组合出三态。例如状态 0信号 X 0信号 Y 0 状态 1信号 X 1信号 Y 0 状态 2信号 X 1信号 Y 1本质上是用“二进制的多根线”编码一个三进制位。这样做的好处是逻辑门、传感器等现有模块可以直接复用坏处是占空间、布线复杂而且失去了“三进制直观”的意义。3.3 方案三滑块位移使用活塞或滑块的长短位置来表示三态数字 0活塞收回 数字 1活塞半伸出 数字 2活塞完全伸出滑块位移的好处是方便通过碰撞体触发逻辑坏处是高精度定位比较困难物理引擎的碰撞抖动会影响位移稳定性。3.4 三种方案对比与选择方案空间占用精度逻辑门依赖推荐度旋转关节角度小中低高多路二进制组合大高高中滑块位移中低中低在实际搭建中我更推荐以“旋转关节角度”作为主编码方案。它最贴合机械三进制的概念而且能够用较少的零件表达三个状态。后文中的核心模块设计也默认采用角度编码。4. 三进制全加器总体架构设计4.1 输入与输出约定先约定输入输出接口输入 A0 / 1 / 2 输入 B0 / 1 / 2 输入 Cin0 / 1 / 2 输出 Sum0 / 1 / 2 输出 Cout0 / 1 / 2在机械上每个信号都对应一个“三态角度轴”。4.2 模块划分三进制全加器可以拆成四个模块输入编码模块把玩家操作或前级模块带来的状态转换成统一的三态角度信号。求和判定模块接收三个输入的角度根据total A B Cin判断总和大类。进位生成模块根据 total 是否大于等于 3生成Cout的角度输出。求余输出模块根据 total 对 3 取余的结果生成Sum的角度输出。4.3 模块之间的信号关系可以用文字描述整体数据流A ──→ 求和判定模块 ──→ 进位生成模块 ──→ Cout B ──→ 求和判定模块 ──→ 求余输出模块 ──→ Sum Cin ──→ 求和判定模块求和判定模块内部不做复杂逻辑运算只负责把三个输入位置“叠加”起来得到一个物理位置或机械结构状态供后续两个输出模块读取。这种拆法对应了前面的真值表total A B Cin Cout total / 3整除 Sum total % 3取余5. 核心模块的机械实现思路5.1 求和判定模块角度叠加Besiege 里要把两个或多个转轴的角度“相加”最常见的机械结构是差速器差动齿轮组。差速器的特性是两个输入轴的旋转角度之差会体现在输出轴上。理论上只要把输入 A 和输入 B 接到差速器的两个输入端输出轴就能得到“A 的角度 - B 的角度”或“A 的角度 B 的角度”的某种线性组合。但在实际游戏中差速器对安装精度和齿轮咬合要求较高容易出现滑齿和卡死。一个更容易落地的替代方案是使用连杆 滑块两个输入轴分别推动滑块滑块位移通过连杆叠加成一个总位移再用这个总位移去触发后续判定。这种方案牺牲了一些精度但调试难度低很多也更容易理解。求和判定模块的最终目标是让机械结构产生七个可区分的位置区域对应total 0 到 6。例如给输出轴安装一个有 7 个齿的棘轮使它能卡在 7 个不同角度位置之一。5.2 进位生成模块阈值判定当total 3时需要产生进位。进位有 0 / 1 / 2 三种状态total 012 → Cout 0 total 345 → Cout 1 total 6 → Cout 2机械实现一种方式是“挡块判定”在total对应的角度轴上安装几个碰撞体只有当轴转到特定位置时才触碰进位输出杆total小于 3进位杆保持 0 位total等于 3、4、5进位杆被推到 1 位total等于 6进位杆被推到 2 位。这种设计类似于机械式凸轮控制不需要逻辑门纯靠物理位置完成阈值判定。5.3 求余输出模块余数映射求余输出模块处理Sum total % 3total 036 → Sum 0 total 14 → Sum 1 total 25 → Sum 2注意这里比较特殊三个不同的total值可能对应同一个Sum。这意味着求余模块不能简单只判断“某个位置”它需要把不同位置归并到三个输出状态之一。一种可行做法是使用带凸轮轮廓的转盘转盘根据 total 旋转到 0 到 6 的对应位置。 转盘边缘有一个曲线槽曲线高度按 0,1,2,0,1,2,0 规律变化。 一根随动杆压在曲线槽里槽的高度决定了 Sum 输出角度。这种“机械凸轮映射”非常经典也是三进制机械计算机比二进制机械计算机更有趣的地方之一。5.4 同步与精度问题机械计算机与电子计算机最大的不同是它没有全局时钟。各模块之间的运动速度受物理引擎影响可能不同步。比如 A 输入已经到位但 B 输入还在路上求和判定模块就会短暂处于错误状态。为了避免这个问题通常在输入模块加入“锁定结构”输入未全部就位前求和判定模块被一个挡块锁住不跟随输入变化。 当三个输入都到位后释放挡块让求和模块完成一次求值。这种“先锁定后释放”的流程本质上就是机械计算机的门控时钟。6. 用 Python 验证三进制全加器逻辑在进入 Besiege 搭建前强烈建议先用脚本把逻辑验证一遍避免游戏里机械结构搭好了最后却发现整个真值表设计错了。下面是一段可以直接运行的 Python 模拟脚本。# 文件名ternary_adder.py # 作用模拟三进制全加器逻辑验证真值表 def ternary_add(a: int, b: int, cin: int): 三进制一位全加器。 参数: a: 0/1/2 b: 0/1/2 cin: 0/1/2 返回: (sum, cout)sum 为本位和cout 为进位 total a b cin sum_ total % 3 cout total // 3 return sum_, cout def run_all_tests(): 遍历所有输入组合检查输出与真值表是否一致。 expected { (0, 0, 0): (0, 0), (0, 0, 1): (1, 0), (0, 0, 2): (2, 0), (0, 1, 0): (1, 0), (0, 1, 1): (2, 0), (0, 1, 2): (0, 1), (0, 2, 0): (2, 0), (0, 2, 1): (0, 1), (0, 2, 2): (1, 1), (1, 0, 0): (1, 0), (1, 0, 1): (2, 0), (1, 0, 2): (0, 1), (1, 1, 0): (2, 0), (1, 1, 1): (0, 1), (1, 1, 2): (1, 1), (1, 2, 0): (0, 1), (1, 2, 1): (1, 1), (1, 2, 2): (2, 1), (2, 0, 0): (2, 0), (2, 0, 1): (0, 1), (2, 0, 2): (1, 1), (2, 1, 0): (0, 1), (2, 1, 1): (1, 1), (2, 1, 2): (2, 1), (2, 2, 0): (1, 1), (2, 2, 1): (2, 1), (2, 2, 2): (0, 2), } for (a, b, cin), exp in expected.items(): sum_, cout ternary_add(a, b, cin) if (sum_, cout) ! exp: print(f错误: A{a}, B{b}, Cin{cin} Sum{sum_}, Cout{cout}, 期望{exp}) return False print(全部 27 组测试通过) return True if __name__ __main__: run_all_tests()运行脚本python ternary_adder.py预期输出全部 27 组测试通过有了这个测试脚本你在 Besiege 里搭出来的机械结构就可以对照真值表逐项验证后续做多位加法器时也可以直接复用这个函数。7. Besiege 内搭建流程实战下面给出一个从零开始的搭建顺序。每个步骤都尽量给出设计意图而不是简单贴零件清单。7.1 第一步搭建基础机架全加器至少有三个输入模块、一个核心判定模块、两个输出模块。建议先做一个规整的底座机架把各模块固定在确定位置避免后期机械结构移动导致信号错位。机架要点使用大块木质或金属梁做底盘保证重量稳定四个角加轮子或固定块防止整体位移为每个模块预留足够操作空间。7.2 第二步制作输入编码模块输入编码模块的任务是让玩家可以手动切换 0 / 1 / 2 三个状态。这里可以用旋转关节加上两个限位块角度 0° 到 45° 对应数字 0 角度 45° 到 90° 对应数字 1 角度 90° 以上对应数字 2更好的做法是直接用三个固定挡块卡死三个位置这样玩家每次都能精准停在 0°、45°、90°不会因为手抖而停在中间状态。7.3 第三步搭建求和判定模块这一步是核心也是调试时间最长的部分。推荐先用“连杆位移叠加”方案验证逻辑再优化成齿轮差速器方案。连杆位移叠加的基本思路是三个输入轴分别连接到三根连杆三根连杆共同推动一个滑动块滑动块的位移等于三个输入位移之和。这样我们就可以通过滑动块的位置判断total。接着在滑动块旁边安装一个有 7 个定位槽的棘轮让滑动块每次只能停在0 到 6的某个槽位。这个槽位就是total。要注意的是机械结构在高负载下会变形如果三个连杆推同一个滑块摩擦力可能很大建议给滑块加滚动支撑或使用轻质材料。7.4 第四步搭建设进位生成模块进位生成模块要从求和判定模块取走total位置信号并输出Cout。最简单的实现当滑块在槽位 0、1、2 时进位杆保持 0 位当滑块在槽位 3、4、5 时进位杆被推杆推到 1 位当滑块在槽位 6 时进位杆被推到 2 位。可以用一个带不同高度凸点的“随动板”完成映射。随动板的高度差直接决定进位输出轴的角度。7.5 第五步搭建求余输出模块求余输出模块类似但需要把槽位映射到三个 Sum 状态槽位 0、3、6 → Sum 角度 0° 槽位 1、4 → Sum 角度 45° 槽位 2、5 → Sum 角度 90°实现方式是做一个“三段高度曲线”的凸轮板或者用三个逻辑门分别识别三组槽位再驱动同一根输出轴。7.6 第六步加装锁定与校准装置最后在所有输入模块上加装“到位锁定”结构。只有当三个输入都处于稳定状态时才释放求和判定滑块避免中间状态进入判定模块。校准方法逐个输入组合观察Sum和Cout是否落在预期槽位如果某个槽位偏差检查联动杆的长度和限位块每个状态固定后在对应位置添加挡块防止再次漂移。8. 测试用例与验证方法8.1 关键测试用例不建议第一次就把 27 组全部测完先测代表性组合即可。以下覆盖了total从 0 到 6 的所有情况编号ABCin期望 Sum期望 Couttotal1000000200110130112024111013512111460221157222026这 7 组用例覆盖了进位 0 / 1 / 2 三种情况也覆盖了 Sum 为 0 / 1 / 2 的所有取值能快速暴露大部分逻辑问题。8.2 手动测试步骤把三个输入模块分别调整到期望角度等待 1 到 2 秒让物理结构稳定观察Sum输出轴和Cout输出轴所在角度对照期望值表格记录结果。如果出现错误不要急着改整个结构先确认是“输入没有到位”还是“判定模块映射错误”。一个简单的方法是在求和判定模块的滑动块上看当前total槽位是否正确。如果滑动块位置正确说明问题出在输出映射模块如果滑动块位置不对说明问题出在求和层级。8.3 自动验证思路随着模块变大手动测试会越来越耗时。可以在所有关键轴上安装角度传感器或颜色标记然后用 Besiege 的录像/截图功能记录每一帧的信号角度再批量对照真值表。更好的方式是利用外部工具用 Python 读取导出的测试记录文件逐帧校验角度是否处于合法状态集合。这样可以把整个验证过程自动化为后续多位加法器铺路。9. 常见问题与排查思路机械结构与纯逻辑脚本不同容易出现各种物理层面的问题。下面整理几个高频问题与排查建议。问题现象常见原因排查思路Sum 输出偶尔跳动齿轮间隙、连杆虚位在关键轴增加限位块使用轻质高强度材料进位信号比 Sum 慢进位判定链路过长缩短传动链将进位判定移到靠近求和模块的位置输入状态无法稳定停在 0/1/2限位块位置不准确或反冲重新校准限位块增加阻尼或弹簧锁定滑块被卡死多个连杆同时推动产生过约束改用差速器或减少推动点增加滑块润滑高负载下零件变形结构强度不足加装支撑梁尽量让受力沿直线传递求和位置偏移导致进位误触发联动杆长度不一致逐级测量每个输入到滑块的位移比测试时结果时对时错没有等待物理稳定就读取每次操作后延迟 1 到 2 秒再读取输出遇到问题时建议遵循“先定位、再修改”的原则。不要一上来就拆整个结构而是用标识笔或颜色标记为每个轴标出目标角度然后分段检查。10. 最佳实践与工程建议10.1 模块先独立验证再组合在 Besiege 里搭机械计算机最容易犯的错误是“一步到位”。全加器整体结构复杂如果一次性拼完再排查bug 可能散布在多个模块之间极难定位。建议的做法是先单独搭好输入模块验证三个角度稳定可切换再搭求和判定模块用三个独立的手动输入去推滑块验证total槽位正确最后才接入进位生成和求余输出模块。每一步验证通过后再进入下一步出问题就能缩小范围。10.2 做好存档版本管理每完成一个里程碑模块建议另存一个新存档。机械结构在调试时经常发生“之前还能动改了两根梁就卡死”的情况。存档版本管理能让你快速回退。10.3 优先使用机械结构而不是逻辑门堆砌Besiege 的性能对零件数量很敏感逻辑门虽然方便但数量多了会导致帧率下降、物理模拟不稳定。如果同一功能可以用齿轮或凸轮实现优先选择机械结构。这既是性能考虑也是三进制机械计算机的乐趣所在。10.4 统一信号约定在搭建前先定好三态角度约定。比如0 → 0° 1 → 45° 2 → 90°所有模块都必须严格遵循这个约定。混用角度范围会导致后续扩展时进位输出无法前后级联。10.5 为扩展保留接口全加器将来可能需要连成多位加法器所以在设计输入输出轴时尽量把接口留在模块两侧不要被装饰零件遮挡。同时给Cout输出轴预留足够的连接空间方便把它接到下一级的Cin上。10.6 控制零件规模一个全加器如果用纯机械方式实现零件数量可能在几十到上百之间。如果零件太多可以考虑以下几个优化方向用更少的支撑梁减少冗余结构用逻辑门替代传动链过长的纯机械判定使用对称布局用同一套结构同时处理多路信号。11. 下一步向多位加法器与平衡三进制扩展全加器跑通后机械三进制计算机的运算核心就具备雏形了。下一步可以沿着两个方向深入。11.1 级联多位三进制加法器把多个全加器的Cout依次接到下一级的Cin就能构成 ripple carry 多位加法器bit0 全加器 (A0, B0, 0) → Cout0 bit1 全加器 (A1, B1, Cout0) → Cout1 bit2 全加器 (A2, B2, Cout1) → Cout2 ...这种结构简单、容易理解但存在进位传播延迟。在 Besiege 里机械传动的延迟更大所以多位加法器必须考虑同步问题。一种改进方案是“进位旁路”或“进位前瞻”但机械实现难度较高可以作为进阶挑战。11.2 尝试平衡三进制如果你已经熟悉了0 / 1 / 2编码可以挑战平衡三进制-1 / 0 / 1。平衡三进制的优点在于负数可以直接表示加法的进位规则更对称输出可以在结构上复用更多模块。但它对机械结构的要求更高因为状态对应角度可能是-1 → -90° 0 → 0° 1 → 90°这需要转轴范围覆盖更大的角度且要对负角度进行限位。调试时也需要一套不同的映射逻辑。11.3 从加法器走向完整运算器有了加法器之后可以通过补码实现减法通过“移位 加法”实现乘法通过循环减法实现除法。这些运算最终可以组成一个完整的机械三进制运算器。如果再搭配机械存储模块比如用齿轮组状态保存数据那么一个真正的 Besiege 机械三进制计算机就已经具雏形了。当然这会是一个非常宏大的工程需要极强的耐心和结构设计能力。12. 写在最后的实践建议三进制全加器只是整台机械三进制计算机开发的第一步但它完全值得单独作为一次开发记录来总结。逻辑上它比二进制全加器多了一倍以上的状态组合机械上又要在同一个物理世界里处理三态角度、进位判定和同步问题每个环节都考验设计与调试能力。如果你准备在 Besiege 里尝试同样的事情我的建议是不要追求一步到位。先从最小的输入编码模块开始实验把“角度表达三态”这个基础弄扎实再逐步搭建求和与进位模块。调试时不要嫌麻烦每一步都对照真值表记录下来错误会更容易定位。接下来我计划继续完善三进制全加器的机械结构再做一个小型的多位加法器原型并把平衡三进制的引入提上日程。如果你也在做 Besiege 机械计算欢迎一起交流思路和踩坑经验。