Files
ngzz-mc/internal/ui/hud.go

115 lines
3.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//go:build gl
// HUD 抬头显示:准星、热键栏、生命/饥饿、破坏进度(UI还原.md §2、§3.4 规格)。
package ui
import (
"fmt"
"mc/internal/game"
"mc/internal/item"
)
// HUD 游戏内 HUD。
type HUD struct {
sess *game.Session
items *item.Registry
// 物品图标:items 图集纹理与 UV
itemTex uint32
itemUV map[string][4]float32
// 逻辑分辨率与缩放(UI还原.md §3.1:427×240 基准 × 整数缩放)
baseW, baseH float32
scale float32
// OnlineCount 在线玩家数(联机时由调用方注入;nil 表示单机不显示)
OnlineCount func() int
}
// NewHUD 创建 HUD。
func NewHUD(sess *game.Session, items *item.Registry, baseW, baseH, scale float32) *HUD {
return &HUD{sess: sess, items: items, itemUV: map[string][4]float32{}, baseW: baseW, baseH: baseH, scale: scale}
}
// SetItemAtlas 设置物品图标纹理与 UV 表(items 图集)。
func (h *HUD) SetItemAtlas(tex uint32, uv map[string][4]float32) {
h.itemTex = tex
h.itemUV = uv
}
// Draw 每帧绘制 HUD(调用方已 Begin)。
func (h *HUD) Draw(r *Renderer) {
w, hgt := float32(r.w), float32(r.h)
s := h.scale
white := [4]float32{1, 1, 1, 1}
// 准星(UI还原.md §3.4:屏幕正中 15×15 十字)
cx, cy := w/2, hgt/2
r.DrawRect(cx-1*s, cy-8*s, 2*s, 16*s, white)
r.DrawRect(cx-8*s, cy-1*s, 16*s, 2*s, white)
// 热键栏(UI还原.md §3.4:182×22,底部居中)
barW, barH := 182*s, 22*s
bx, by := cx-barW/2, hgt-barH
r.DrawRect(bx, by, barW, barH, [4]float32{0, 0, 0, 0.5})
for i := 0; i < 9; i++ {
sx := bx + float32(i)*20*s + 1*s
sy := by + 3*s
if i == h.sess.Inv.Selected {
r.DrawRect(sx-2*s, sy-2*s, 20*s, 20*s, [4]float32{1, 1, 1, 0.9}) // 选中高亮框
}
h.drawItemIcon(r, h.sess.Inv.Slots[i], sx, sy)
}
// 生命:10 心(每心 9×9,屏幕左下,UI还原.md §3.4)
hearts := int(20 / 2)
hp := h.sess.PlayerHealth()
hx, hy := w/2-91*s, hgt-30*s
for i := 0; i < hearts; i++ {
color := [4]float32{0.2, 0.2, 0.2, 0.6} // 空
if i < hp/2 {
color = [4]float32{1, 0.1, 0.1, 1} // 满
}
r.DrawRect(hx+float32(i%10)*8*s, hy+float32(i/10)*9*s, 7*s, 7*s, color)
}
// 饥饿:10 鸡腿(右侧偏移)
for i := 0; i < 10; i++ {
r.DrawRect(w/2-91*s+100*s+float32(i%10)*8*s, hy+float32(i/10)*9*s, 7*s, 7*s, [4]float32{0.6, 0.4, 0.15, 1})
}
// 破坏进度(方块交互与动画.md §4:HUD 中央进度)
if prog := h.sess.BreakProgress(); prog > 0 {
r.DrawRect(cx-40*s, cy+20*s, 80*s, 4*s, [4]float32{0, 0, 0, 0.7})
r.DrawRect(cx-40*s, cy+20*s, 80*s*float32(prog), 4*s, [4]float32{1, 1, 1, 0.9})
}
// 坐标调试(后置移除)
r.DrawText(w/2, hgt-40*s, fmt.Sprintf("XYZ %.1f / %.1f / %.1f", h.sess.Player.Pos[0], h.sess.Player.Pos[1], h.sess.Player.Pos[2]), 1*s, white)
// 联机在线人数(多人同步.md §3)
if h.OnlineCount != nil {
r.DrawText(w/2, hgt-56*s, fmt.Sprintf("在线玩家: %d", h.OnlineCount()), 1*s, white)
}
}
// drawItemIcon 在槽位绘制物品图标 + 数量。
func (h *HUD) drawItemIcon(r *Renderer, stack item.Stack, x, y float32) {
s := h.scale
if stack.Empty() {
return
}
def, ok := h.items.Get(stack.Name)
if !ok {
return
}
if uv, found := h.itemUV[def.Texture]; found && h.itemTex != 0 {
r.pushQuad(quad{x: x + 2*s, y: y + 2*s, w: 16 * s, h: 16 * s,
u0: uv[0], v0: uv[1], u1: uv[2], v1: uv[3],
r: 1, g: 1, b: 1, a: 1, tex: h.itemTex})
}
if stack.Count > 1 {
r.DrawText(x+10*s, y+8*s, fmt.Sprintf("%d", stack.Count), 1*s, [4]float32{1, 1, 1, 1})
}
}