Learn-mindustry-logic-A4-Advanced
引言
这是《逻辑教学》的超完整版 (进阶篇), 适用于已经掌握基础语句的玩家.
本教程不是语句词典, 而是偏工程与实战的写法总结.
基线版本:
- Mindustry 155
执行模型与调试
执行四步
逻辑运行可抽象为四步2:
- 读取
@counter指向语句 @counter += 1- 执行被读取语句
- 回到 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
函数系统-非递归
函数组成
- 入口跳过
- 函数体
- 返回语句
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
函数系统-递归与栈
递归函数必须解决两个问题:
- 返回地址会多层嵌套
- 中间变量会被后续递归覆盖
所以要用内存做调用栈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 | 错误码 |
握手流程
- Producer 写 cmd + args
- Consumer 读取并执行
- Consumer 写 ack=cmd
- 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
世界处理器语句破坏性极强, 建议默认禁用, 仅在维护窗口开启.
性能优化与资源预算
资源预算模型
建议给每帧设置预算变量:
budgetOpsbudgetIObudgetDraw
高频热点
- 高频
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、元编程强 | 深度控制流玩家 |
工程化流水线
推荐流程:
- 源码仓库
- 编译脚本
- 自动导出 mlog
- 回归测试图
- 版本发布
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 |
要点:
mod与emod在负数时行为不同- 位运算前会转整数
- 三角函数是角度制
附录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
常量与数学
truefalsenull@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-排错清单
请按顺序检查:
- 变量是否初始化
- 链接名是否存在且正确
- 是否同队伍且有权限
- @counter 是否跳飞
- 是否被 wait/stop 阻塞
- 单位是否被抢控
- 协议版本是否一致
- ACK 是否回写
- flush 频率是否过高
- 是否触发了安全门
最小排错模板:
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] 本教程若与将来版本冲突, 以新版本实测为准.
