Learn-mindustry-logic-A4-Advanced

来自Mindustry中文wiki
Fpzwsl留言 | 贡献2026年3月4日 (三) 20:06的版本 (创建页面,内容为“__NOTOC__ <div class="mdtLogicTutorial"><span id="引言"></span> == 引言 == 这是《逻辑教学》的超完整版 (进阶篇), 适用于已经掌握基础语句的玩家. 本教程不是语句词典, 而是偏工程与实战的写法总结. 基线版本: * Mindustry 155 ---- <span id="执行模型与调试"></span> == 执行模型与调试 == <span id="执行四步"></span> === 执行四步 === 逻辑运行可抽象为四步<sup>2</sup>:…”)
(差异) ←上一版本 | 最后版本 (差异) | 下一版本→ (差异)

引言

这是《逻辑教学》的超完整版 (进阶篇), 适用于已经掌握基础语句的玩家.

本教程不是语句词典, 而是偏工程与实战的写法总结.

基线版本:

  • Mindustry 155



执行模型与调试

执行四步

逻辑运行可抽象为四步2:

  1. 读取 @counter 指向语句
  2. @counter += 1
  3. 执行被读取语句
  4. 回到 1

这条规则解释了:

  • jump 的本质是改写下一条语句位置
  • wait/stop 的“卡住”是通过 counter-- + yield 实现
  • 所有函数/跳表都能用 @counter 手工实现

变量表调试法

建议你固定三层调试变量:

  • 控制层: state phase step
  • 数据层: in out tmp err
  • 时序层: tick heartbeat watchdog

典型调试输出模板:

print "s="; print state
print " p="; print phase
print " t="; print tick
printflush message1

变量表刷新频率低于逻辑执行频率, 快速跳转下会误读 @counter.

最小监控框架

心跳 是什么: 持续变化的计数值。作用: 判断逻辑是否仍在运行。
卡死 是什么: 逻辑未崩溃但长期不推进。作用: 作为触发恢复的判据。
看门狗 是什么: 监控“是否长期无进展”的计数器。作用: 超时后触发重置或降级。

# 监控心跳和卡死
op add heartbeat heartbeat 1
op mod heartbeat heartbeat 1000000

op add watchdog watchdog 1
jump wd_ok lessThan watchdog 600
    print "watchdog trip"
    printflush message1
    set watchdog 0
wd_ok:
end



进阶控制流-基础

这一章只做“衔接”, 不再重复基础控制流定义。

以下基础结构已移动到基础教程《逻辑教学.wiki》中的“控制流基础补充”章节:

  • skip
  • if-else
  • do-while
  • while
  • gwhile

进阶篇只保留工程侧规则:

  • 多层循环必须用唯一标签前缀, 防止跳错行
  • break/continue 跳转必须显式写“目标层级”
  • 所有循环都要能从外部条件中断

多层循环命名模板:

# outer loop
O_LOOP:
    # inner loop
I_LOOP:
        jump I_CONT lessThan x 0
        jump I_BREAK equal innerStop true
        # inner body...
    I_CONT:
        # inner tail...
    jump I_LOOP lessThan i 10
I_BREAK:
    jump O_BREAK equal outerStop true
    # outer tail...
jump O_LOOP lessThan o 5
O_BREAK:



进阶控制流-选择

跳表基础

跳表 是什么: 用 @counter 偏移把输入映射到分支入口。作用: 用常数时间做多分支分派。

# sel in [0,2]
op add @counter @counter sel
jump C0 always 0 0
jump C1 always 0 0
jump C2 always 0 0
C0:
    print "0"
    jump END always 0 0
C1:
    print "1"
    jump END always 0 0
C2:
    print "2"
END:
printflush message1

跳表防越界

# clamp sel to [0,2]
op max sel sel 0
op min sel sel 2
op add @counter @counter sel
jump C0 always 0 0
jump C1 always 0 0
jump C2 always 0 0

等长块分派

每块长度固定时, 可减少子表行数:

# len=4
op mul off idx 4
op add @counter @counter off
    print "A"
    jump END always 0 0
    noop
    noop

    print "B"
    jump END always 0 0
    noop
    noop
END:
printflush message1

匹配守卫

匹配守卫 是什么: case 之前的额外条件跳转。作用: 在不增加主分派复杂度的前提下细化命中条件。

# case 0 if a<b
op mul k n 2
op add @counter @counter k
    jump C0_G lessThan a b
    jump C0 always 0 0
    jump C1 always 0 0
C0_G:
    print "0 guard"
    jump END always 0 0
C0:
    print "0"
    jump END always 0 0
C1:
    print "1"
END:
printflush message1



复杂条件

逻辑代数定义

形式 名称 说明
!A 反转条件
A && B 短路与 A假时不看B
A B 短路或 A真时不看B

基本反转表

lessThan greaterThanEq
lessThanEq greaterThan
greaterThan lessThanEq
greaterThanEq lessThan
equal notEqual
notEqual equal

德摩根定律

  • !(A && B) = (!A) || (!B)
  • !(A || B) = (!A) && (!B)

构建算法

def build(cond, target):
    if cond.type == AND:
        tmp = newTag()
        build(not cond.left, tmp)
        build(cond.right, target)
        add(tmp)
    elif cond.type == OR:
        build(cond.left, target)
        build(cond.right, target)
    else:
        add(jump target cond)

实战示例-复杂条件

目标: (a<b && c<d) || e<f

jump T1 greaterThanEq a b
jump TARGET lessThan c d
T1:
jump TARGET lessThan e f
print "false"
TARGET:
print "true"
printflush message1



函数系统-非递归

函数组成

  1. 入口跳过
  2. 函数体
  3. 返回语句 set @counter ret
jump DEF_END always 0 0
fn_print:
    print msg
    set @counter ret
DEF_END:

set msg "Hello"
op add ret @counter 1
jump fn_print always 0 0

set msg "World"
op add ret @counter 1
jump fn_print always 0 0

printflush message1

函数参数约定

推荐约定:

  • 入参: arg1 arg2 ...
  • 出参: retv
  • 返回地址: ret

函数复用案例

平方函数:

jump D_END always 0 0
fn_sqr:
    op mul retv arg1 arg1
    set @counter ret
D_END:

set arg1 12
op add ret @counter 1
jump fn_sqr always 0 0
print retv
printflush message1



函数系统-递归与栈

递归函数必须解决两个问题:

  1. 返回地址会多层嵌套
  2. 中间变量会被后续递归覆盖

所以要用内存做调用栈3.

栈模型

建议每帧栈帧结构:

偏移 含义
+0 返回地址
+1 参数n
+2 临时值tmp

递归框架

# 仅示意骨架
set sp -1
jump DEF_END always 0 0
fib:
    # push ret
    # push n
    # recurse
    # pop
    set @counter ret
DEF_END:

尾递归优化

尾递归可改循环, 大幅降复杂度和 bug 风险.

# tail recursion -> loop
LOOP:
    # body
jump LOOP lessThan i n



多处理器协作协议

超大逻辑应拆分为多个处理器协作.

协议字段

协议 是什么: 多处理器共享内存字段和时序规则的约定。作用: 避免协作时读写歧义与乱序。
消费者心跳 是什么: 由消费者更新的心跳字段。作用: 让生产者判断消费者是否卡住。
ACK 是什么: 命令处理完成回执值。作用: 让发送方安全进入下一步。

地址 字段
0 协议版本
1 生产者心跳
2 消费者心跳
3 命令码
4 参数A
5 参数B
6 ACK
7 错误码

握手流程

  1. Producer 写 cmd + args
  2. Consumer 读取并执行
  3. Consumer 写 ack=cmd
  4. Producer 校验 ack

协作模板

生产者:

write 1 bank1 0
op add hb hb 1
write hb bank1 1
write 200 bank1 3
write x bank1 4
write y bank1 5
read ack bank1 6
jump 0 notEqual ack 200

消费者:

read ver bank1 0
jump 0 notEqual ver 1
read cmd bank1 3
jump 0 notEqual cmd 200
read x bank1 4
read y bank1 5
# do work...
write 200 bank1 6



单位控制进阶

抢控保护

抢控 是什么: 当前单位被其他逻辑或玩家接管。作用: 提醒当前控制逻辑立即重绑或回退。

find:
    ubind @flare
    sensor c @unit @controlled
jump find notEqual c 0

ctrl:
    ucontrol move tx ty 0 0 0
    sensor dead @unit @dead
jump find notEqual dead false
    sensor owner @unit @controller
jump ctrl equal owner @this
jump find always 0 0

控制最小化

不用控制语句时不要发控制信号4:

# 用 op len 判定距离, 替代 ucontrol within
sensor ux @unit @x
sensor uy @unit @y
op sub dx ux tx
op sub dy uy ty
op len dist dx dy
op lessThan near dist 10

可靠解绑

# 确认当前确实在控制时再 unbind
sensor owner @unit @controller
jump skip notEqual owner @this
ucontrol unbind 0 0 0 0 0
skip:



单位批处理与调度

目标: 降低单帧峰值, 提升稳定性.

时间片调度

# task 0..2
op add @counter @counter task
jump T0 always 0 0
jump T1 always 0 0
jump T2 always 0 0
T0:
    # bind / select
    jump NEXT always 0 0
T1:
    # sense
    jump NEXT always 0 0
T2:
    # control
    jump NEXT always 0 0
NEXT:
op add task task 1
op mod task task 3
end

轮询类型

lookup unit utype slot
ubind utype
# ... process one ...
op add slot slot 1
op mod slot slot @unitCount

负载退避

# backlog高时减少控制频率
jump light lessThan backlog 20
    wait 0.05
    jump done always 0 0
light:
    wait 0.01
done:



世界处理器-安全框架

双开关安全门

建议两个按钮:

  • switch1: 总开关
  • switch2: 危险操作开关
sensor en1 switch1 @enabled
sensor en2 switch2 @enabled
jump END equal en1 false
jump END equal en2 false
# privileged dangerous ops...
END:
end

回滚点

任何会改地图状态的逻辑建议保留回滚参数:

# 写入前先存旧值
getblock block oldB x y
write oldB bank1 20
# ... setblock ...

速率保险丝

速率保险丝 是什么: 先短时提速再自动回落的控制策略(本教程命名)。作用: 避免 setrate 长期高负载导致全局卡顿。

# 防止 setrate 长时间高负载
setrate 20
wait 0.2
setrate 1



世界处理器-高级语句

fetch

fetch unit u @sharded i 0
fetch build b @sharded i @conveyor
fetch unitCount uc @sharded 0 0

message

print "系统广播"
message announce 1 @wait

setprop

# 高风险示例
setprop @health @this 0

setrule

setrule lighting 1 0 0 0 0

status

status false wet unit 10
status true boss unit 0

世界处理器语句破坏性极强, 建议默认禁用, 仅在维护窗口开启.




性能优化与资源预算

资源预算模型

建议给每帧设置预算变量:

  • budgetOps
  • budgetIO
  • budgetDraw

高频热点

  • 高频 printflush/drawflush
  • 全量遍历单位/链接
  • 重复 sensor 同一属性
  • 大量字符串拼接

优化手段

问题 手段
全量扫描卡顿 分帧批处理
输出过于频繁 合并输出/降低 flush 频率
逻辑难维护 状态机 + 协议层
控制抖动 降频 + 持续控制窗口

实战: 分帧遍历链接

# 每帧只处理一个链接
getlink b idx
# ... do something ...
op add idx idx 1
op mod idx idx @links
end



可维护性规范

命名规范

推荐前缀:

  • s_ 状态变量
  • t_ 临时变量
  • io_ 协议变量
  • cfg_ 配置变量

模块边界

建议一个处理器只负责一个职责:

  • 采集
  • 决策
  • 执行
  • 可视化

注释规范

注释写“为什么”, 不写“做了什么”.

# bad: op add i i 1  # i加1
# good: # 轮询链接索引, 防止固定打同一建筑
op add i i 1

发布规范

建议维护:

  • 版本号
  • 协议版本
  • 变更记录



编译器与工程化

什么时候上编译器

满足任一条件就建议上:

  • 代码 > 300 行
  • 需要函数与复用
  • 需要自动构建复杂条件
  • 需要多人协作

编译器对比

名称 特点 适配人群
mindcode 优化强、成熟 重性能
mlogjs JS/TS生态好 前端/脚本开发者
bang-lang 贴近 mlog、元编程强 深度控制流玩家

工程化流水线

推荐流程:

  1. 源码仓库
  2. 编译脚本
  3. 自动导出 mlog
  4. 回归测试图
  5. 版本发布

bang 最小用法

mindustry_logic_bang_lang cl <<< 'print "Hello, World!";'



155与146差异对照

本章整理“旧教程常见说法”与 155 行为的关键差异.

项目 146常见说法 155/当前基线
print缓冲上限 常被写成约220 源码上限为4005
二/十六进制负号 多写作 0b-101, 0x-80 也支持 +0b/-0b/+0x/-0x
read/write对象范围 常仅讲内存 可扩展到字符串/信息板/画板/逻辑变量等
select语句 旧版本无 新版本可直接用
ucontrol细节 文档较少 选项与行为更丰富
control范围 严格依赖链接 某些版本行为有变化, 以当版实测为准

若遇到“教程说法”和游戏行为冲突, 以当前版本实测 + 源码为准.




附录01-op方法进阶

类别 操作
算术 add sub mul div idiv mod emod pow
比较 equal notEqual lessThan lessThanEq greaterThan greaterThanEq strictEqual
位运算 shl shr ushr and or xor not
数学 abs sign floor ceil round sqrt log log10 logn max min rand
几何/三角 len angle angleDiff sin cos tan asin acos atan noise

要点:

  • modemod 在负数时行为不同
  • 位运算前会转整数
  • 三角函数是角度制

附录02-传感器速查

分类 属性
坐标 x y shootX shootY mineX mineY
生存 health maxHealth dead shield armor
资源 totalItems totalLiquids totalPower itemCapacity liquidCapacity powerCapacity
行为 enabled shooting boosting mining speed range
标识 team type id name controlled controller flag
载荷 payloadType payloadCount totalPayload payloadCapacity
文本/显示 size bufferSize displayWidth displayHeight

附录03-环境变量速查

逻辑信息

  • @this / @thisx / @thisy
  • @links
  • @unit
  • @ipt
  • @counter

常量与数学

  • true false null
  • @pi @e
  • @degToRad @radToDeg

世界变量

  • @time @tick @second @minute
  • @waveNumber @waveTime
  • @mapw @maph

lookup计数

  • @itemCount
  • @unitCount
  • @blockCount
  • @liquidCount

附录04-术语表

术语 解释
跳表 是什么: 用 @counter 偏移把输入值映射到不同代码块。作用: 用常数时间完成多分支分派。
匹配守卫 是什么: 在进入某个分支前额外检查一次条件的跳转。作用: 让同一个 case 支持更细粒度筛选。
穿透 是什么: 分支执行后没有跳出, 继续执行后续分支。作用: 可用于刻意复用后续逻辑, 也可能引发误执行。
阻塞 是什么: 在一行或几行上反复循环等待条件。作用: 保证条件满足后再继续, 但会牺牲推进速度。
协议 是什么: 多处理器共享内存字段和时序规则的约定。作用: 避免协作时读写歧义和乱序。
抢控 是什么: 单位控制权被其他逻辑或玩家接管。作用: 提醒当前控制逻辑需要重绑或回退。
心跳 是什么: 持续递增并回写的计数值。作用: 用来判断某个处理器是否仍在运行。
消费者心跳 是什么: 由消费者处理器维护的心跳值。作用: 让生产者检测消费者是否卡住或停机。
ACK 是什么: Acknowledgement, 命令已处理完成的回执值。作用: 让发送方确认可进入下一步。
卡死 是什么: 逻辑没有崩溃, 但长期停在同一等待或循环。作用: 作为故障信号触发恢复流程。
速率保险丝 是什么: 短时提速后自动回落的保护策略(本教程命名)。作用: 防止 setrate 长期高负载导致全局卡顿。
看门狗 是什么: 监控流程是否长期无进展的保护计数器。作用: 超时后触发重置或降级恢复。

高频术语(一句人话 + 最小例子)

心跳: 一句话人话: 一个持续变化的数字, 用来证明“这个逻辑还活着”.

最小例子:

op add hb hb 1
op mod hb hb 1000000
write hb bank1 1

消费者心跳: 一句话人话: 消费者处理器每轮报平安, 让生产者知道它没停机.

最小例子:

# consumer side
op add hb_c hb_c 1
op mod hb_c hb_c 1000000
write hb_c bank1 2

ACK: 一句话人话: “我这条命令做完了”的回执信号.

最小例子:

# producer
write 200 bank1 3
read ack bank1 6
jump 0 notEqual ack 200

# consumer
read cmd bank1 3
# ...do work...
write cmd bank1 6

卡死: 一句话人话: 逻辑没崩, 但一直卡在某个等待/循环里, 业务不前进.

最小例子:

wait_btn:
sensor on switch1 @enabled
jump wait_btn equal on false
# 按钮不变真就会一直卡在这里

速率保险丝(本教程命名): 一句话人话: 短时提速, 自动回落, 防止 setrate 长期高负载.

最小例子:

setrate 20
# heavy work ...
wait 0.2
setrate 1

看门狗: 一句话人话: 发现流程长期没进展就触发恢复动作.

最小例子:

jump progressed notEqual step lastStep
op add stuck stuck 1
jump chk always 0 0
progressed:
set stuck 0
set lastStep step
chk:
jump ok lessThan stuck 600
    # recovery
    set state 0
ok:

附录05-排错清单

请按顺序检查:

  1. 变量是否初始化
  2. 链接名是否存在且正确
  3. 是否同队伍且有权限
  4. @counter 是否跳飞
  5. 是否被 wait/stop 阻塞
  6. 单位是否被抢控
  7. 协议版本是否一致
  8. ACK 是否回写
  9. flush 频率是否过高
  10. 是否触发了安全门

最小排错模板:

print "state="; print state
print " err="; print err
print " ack="; print ack
printflush message1

注释

  • [2] @counter 四步模型是理解所有高级控制流的基础.
  • [3] 递归调用必须隔离返回地址和跨调用变量.
  • [4] 单位控制语句会发控制信号, 非必要不要滥用.
  • [5] 当前基线中 textBuffer 上限为 400.
  • [6] 跳表前务必约束输入范围.
  • [7] 复杂条件转换时优先做逻辑代数化简.
  • [8] guard 优先处理“高概率失败”分支可减少执行成本.
  • [9] while 和 gwhile 选型取决于条件反转成本.
  • [10] 函数建议统一入参/出参/返回地址变量命名.
  • [11] 非递归函数的返回地址常用 @counter+1.
  • [12] 递归函数要保证栈平衡, 否则会读错返回地址.
  • [13] 多处理器协作应先定协议再写代码.
  • [14] 心跳变量建议做取模防止无限增长.
  • [15] ACK 建议回写命令码本身, 便于无状态校验.
  • [16] 单位控制需持续刷新, 否则控制会过期.
  • [17] owner 校验可快速检测抢控.
  • [18] 批处理策略是性能优化的第一手段.
  • [19] 世界处理器高风险操作必须有硬开关.
  • [20] 任何 setblock 类操作建议保留回滚信息.
  • [21] setrate 只应短时升高, 长时高频会压垮整体.
  • [22] flush 过频是常见卡顿源.
  • [23] 输出日志尽量结构化, 便于排错脚本解析.
  • [24] 命名规范是多人协作里性价比最高的质量手段.
  • [25] 注释写“原因”优先于“动作”.
  • [26] 编译器能显著降低复杂控制流的人肉错误率.
  • [27] 版本迁移先做差异表再做迁移计划.
  • [28] mod/emod 的负数行为不同, 算法里要明确选型.
  • [29] 位运算要注意数值转换与范围精度.
  • [30] strictEqual 与 equal 的语义差别在类型一致性.
  • [31] 逻辑代数化简可减少 jump 数量和维护成本.
  • [32] 先保证正确性再做局部性能优化.
  • [33] 协议字段建议保留错误码便于远程诊断.
  • [34] 调试逻辑与业务逻辑建议分离处理器.
  • [35] 对外交互 (message/marker) 建议加频率限制.
  • [36] 内存地址使用建议维护单独映射表.
  • [37] 每次大改应维护 migration 日志.
  • [38] 逻辑蓝图发布建议附最小测试图.
  • [39] 复杂项目推荐 CI 自动导出并做语法检查.
  • [40] 本教程若与将来版本冲突, 以新版本实测为准.