拨号序列
拨出一个地址后,星门的 7 个雪佛龙会依次旋转到位,每个雪佛龙对准地址中对应的那一位符号。本文状态机、角度与时长全部取自 TileStargateController.java(DialPhase 枚举、CHEVRON_ANGLES、SYMBOL_STEP、updateEntity()、startSpinToSymbol())与 RenderStargateTESR.java。
基本信息
| 属性 | 值 |
|---|---|
| 类型 | 特殊机制(连接动画状态机) |
| 触发条件 | 在 拨号装置 界面点击拨号按钮,调用 controller.dialOut(dialingAddress) |
| 状态数 | 4(IDLE / SPINNING / LOCKED / CONNECTED) |
| 雪佛龙数 | 7(= CHEVRON_ANGLES 数组长度 = 地址位数) |
状态机
IDLE ──dialOut()──► SPINNING ──动画完成──► LOCKED ──停 25 tick──► ┐
▲ ▲ │
│ └──────── 下一个符号 ──────────────────────┤
│ │
│ addressIndex ≥ 7 ─┘
│ ▼
└────────────────────────────────────────────────────── CONNECTED
| 状态 | 进入条件 | 行为 | 退出条件 |
|---|---|---|---|
IDLE |
初始值;或 dialed 被置回 false 后区块重载 |
无动画 | dialOut() 成功 |
SPINNING |
startSpinToSymbol() |
按 smootherStep 缓动转动外环 |
t ≥ 1(经过 animTotalTicks tick) |
LOCKED |
一次旋转完成 | 每个符号静止停顿 | lockTicksRemaining 减到 0 |
CONNECTED |
addressIndex >= dialAddress.length(7) |
传送已就绪,不再有动画 | 无退出路径 |
角度计算
| 常量 | 值 | 含义 |
|---|---|---|
SYMBOL_STEP |
360f / 16 = 22.5° |
相邻符号之间的角步进 |
CHEVRON_ANGLES[7] |
{308.57, 257.14, 205.71, 154.29, 102.86, 51.43, 0} |
7 个雪佛龙各自的基准角,间隔 51.43°(= 360°/7) |
目标角度:
targetRotationFor(addressIndex, chevronIndex)
= CHEVRON_ANGLES[chevronIndex] + addressIndex × 22.5°
其中 addressIndex 是目标地址中该符号的数值(0–15),chevronIndex 是雪佛龙序号(0–6)。所以雪佛龙 0 转到 308.57° + 符号值 × 22.5°,雪佛龙 6 转到 0° + 符号值 × 22.5°,以此类推。
shortestDelta(from, to) 把角度差归一化到 (−180°, 180°],总是走较短弧;角度差超过 180° 时反向旋转。
动画参数
| 参数 | 值 | 来源 |
|---|---|---|
| 每符号最短旋转时长 | 20 tick(1 秒) | animTotalTicks = Math.max(20, (int)(Math.abs(animDelta) / 3f)) |
| 常规转速 | 3°/tick | 上式的除数 3f |
| 每符号锁定停顿 | 25 tick | lockTicksRemaining = 25 |
| 缓动函数 | smootherStep:6t⁵ − 15t⁴ + 10t³ |
smootherStep() |
| 插值 | prev + (cur − prev) × partialTicks |
RenderStargateTESR |
因为时长下限是 20 tick,转动幅度小于 60°(20 × 3)时也会被钳制到 20 tick,所以每个符号的耗时下限固定。
完整耗时
7 个雪佛龙依次锁定,每一步 = 旋转(≥20 tick)+ 停顿(25 tick):
- 最快:7 × (20 + 25) = 315 tick ≈ 15.75 秒(每个符号都取最短旋转)
- 最慢:取决于符号序列与雪佛龙基准角的角度差,公式为
Σ max(20, |Δi| / 3) + 7 × 25,最大单步 180° → 60 tick,故上界 7 × (60 + 25) = 595 tick ≈ 29.75 秒
[!WARNING] 传送在动画开始时就已经可用,而不是在
CONNECTED之后。dialOut()成功时立刻把dialing设为目标坐标,而BlockFormedGate.addCollisionBoxesToList()只检查controller.isTransmitting()(即dialing != null),不检查dialPhase。因此雪佛龙还在转动时走进门就会被传送走。CONNECTED状态没有任何功能性作用,纯粹是动画的终态标记。
拨号的前置检查
dialOut(int[] sigils) 依次执行 4 项检查,任何一项失败都返回 false 且不产生任何动画:
| 顺序 | 检查 | 失败含义 |
|---|---|---|
| 1 | if (dialed) return false; |
本星门已经是别的星门的拨入目标 |
| 2 | if (sga.equals(address)) return false; |
目标地址与自身地址相同(不能拨自己) |
| 3 | StargateRegistry.INSTANCE.lookup(sga) == null → false |
目标地址未注册(全服不存在该地址的星门) |
| 4 | !gate.getDialed(address) → false |
目标星门拒绝被拨入 |
第 4 步的 getDialed() 恒返回 true,其唯一作用是把目标星门的 dialed 标志置为 true。该标志在全类中没有任何复位点,所以目标星门一生只能被拨通一次;第四次检查实际上永远通过不了。dialed 不写入 NBT,区块卸载重载后会恢复为 false。
渲染
RenderStargateTESR 分两次绘制 OBJ 模型:
- 整体平移到
(x+0.5, y+2.5, z+0.5),按 facing 绕 Y 轴旋转(北 90° / 南 −90° / 西 180°),renderOnly("static")画固定部分。 - 再绕 X 轴旋转
ringAngle(即插值后的ringRotation),renderOnly("spinners")画旋转的外环。
模型 stargatenh:models/stargate.obj,贴图 stargatenh:textures/models/stargate.png。