HUD 叠加层
hud/HudRenderer.java(480 行)+ hud/HudSettingsScreen.java。HUD 绘制被图钉的任务——即在任务列表里点图钉按钮挑出来的那几条,见 客户端图钉配置。
渲染入口
@SubscribeEvent
public void onRenderHud(RenderGameOverlayEvent.Post event)
由 ClientProxy.init() 注册在 MinecraftForge.EVENT_BUS。用 Post 而非 Pre,在游戏 HUD 文本之后绘制。
两个提前返回条件:
if (event.type != RenderGameOverlayEvent.ElementType.TEXT) return;—— 只在 TEXT 阶段画if (mc.currentScreen != null && !(mc.currentScreen instanceof HudSettingsScreen)) return;—— 有其他界面打开时不画,但 HUD 设置界面打开时照画(方便边拖边看)
之后 if (!cfg.isHudVisible()) return; 与「无图钉则 return」。
maxPinnedTasks 在这里生效:all.size() > maxTasks ? all.subList(0, maxTasks) : all——超出部分直接截断,且因为图钉列表有序,保留的是前 N 条。
缩放与坐标
渲染时 glPushAttrib(GL_ALL_ATTRIB_BITS) + glPushMatrix() + glScaled(scale, scale, 1.0),结束时对称还原。所有坐标都要除以 scale,因为 GL 矩阵已经放大过了(源码注释)。
背景用 ColorUtils.backgroundHud.getColor()(0x88000000)画一个矩形,可用 showBackground 关掉。
布局常量
| 常量 | 值 |
|---|---|
MAX_BLOCK_WIDTH |
160 |
LINE_H |
10 |
ICON_SIZE |
10(物品图标缩到与行高一致) |
ICON_GAP |
2 |
BLOCK_GAP |
4(两个任务块之间) |
PADDING |
4 |
computeHudPosition(cfg, sw, sh, fr, pinned) 返回 [startX, startY, blockW, totalH] 四元组。源码注释:「Every frame starts here, both on the HUD and on the settings screen, so the counts are checked once.」——它必须先于绘制计算,因为块高度依赖要显示多少行清单/子任务。
totalH 按固定公式累加:每任务一行状态行(LINE_H)、清单超出部分一行 +N more、清单与子任务都非空时两个小节标题各占 LINE_H * 2、子任务超出部分再一行 +N more。
九宫格锚点
computeHudPosition 的 switch 按 Anchor 枚举定位,default -> sw - blockW - 2 对应 RIGHT、下一层 default -> sh - totalH - 2 对应 BOTTOM。也就是说九宫格只决定「角落 / 边中 / 中央」三个层级的基准点,再用 offsetX / offsetY 微调。
HUD 上的物品计数
COUNTS 是一个 static final Map<ItemStack, Integer>,用 IdentityHashMap。类注释解释:按被追踪的栈做键,一个栈只属于一个任务或清单项,所以总是带着同一个矿辞典名;snapshotStacks / snapshotSizes 记录快照,物品栏与快照一致时计数有效。
两处防退化处理:
- 数量地板是 1(
Math.max(1, ...)),注释「Same floor as the server check」,与服务端判定一致,见 物品完成追踪 - 已勾选项不再显示计数——注释「A checked item stays checked once the items are spent, so its count would only confuse.」
- 缓存有上限:注释说明「Task syncs replace the tracked stacks, so stale keys pile up until the inventory changes. The cap keeps a long idle session from growing the map.」
HUD 调的是服务端同一个 ItemTrackHandler.countItem(),所以显示数与服务端判定数一致。HUD 的数字仅是展示,不触发完成。
清单 vs 子任务:两个独立概念
tasknh.hud.section.checklist=Checklist %s 与 tasknh.hud.section.subtasks=Subtasks %s 是两个小节,各有独立的显示上限 maxChecklistShown 与 maxSubtaskRowsShown(都是 1–10,默认 3)。源码在 computeHudPosition 中只有「清单与子任务都非空时」才加两个标题的高度。
HUD 设置界面
HudSettingsScreen extends GuiScreen,这是 TaskNH 唯一继承原版 GuiScreen 的类(主界面走 ModularUI)。TaskListWidget 里的主题切换按钮用 mc.displayGuiScreen(new HudSettingsScreen()) 打开它。
| 常量 | 值 |
|---|---|
HANDLE_SIZE |
10(拖拽手柄) |
PANEL_H |
24 |
PANEL_PADDING |
6 |
doesGuiPauseGame() 覆写为 false——在这个界面里游戏不暂停,所以拖动时能直接看到 HUD 在动。
拖动状态由 dragging / dragOffsetX / dragOffsetY / dragAnchorX / dragAnchorY 五个字段维持,其中注释标明 dragAnchorX/dragAnchorY 是「拖动开始时的锚点,拖动期间保持不变」。拖动过程中只调 setOffsetXRaw / setOffsetYRaw 写内存,松手才 save()(见 客户端图钉配置)。
面板上有各开关的文案:tasknh.hud.settings.scale(缩放)、.bg(背景 ON/OFF)、.hud(HUD ON/OFF)、.checklist(清单条数)、.subtasks(子任务条数)、.tasks(任务条数)、.reset(重置),以及提示 tasknh.hud.settings.hint = “Drag handle to reposition HUD | ESC to close”。reset 调 PinnedTasksConfig.resetToDefaults()。
ESC 关闭走 mc.displayGuiScreen(null)。