Files
ngzz-mc/internal/ui/screens.go
NianGao Dev d52bf1126f feat(client): 植被染色、破坏裂纹叠加、手持方块与实体广告牌渲染;原版 HUD 精灵
- 植被染色(渲染.md §4.1):blocks.json 增 tint 标记,图集构建后按
  colormap/grass|foliage 中心色乘色——原版材质包草顶/树叶为灰度图,不染色全灰
- 破坏裂纹(方块交互与动画.md §4):destroy_stage_0..9 裂纹盒叠加破坏目标方块
- 第一人称手持方块(渲染.md §5.2):选中槽放置方块六面 UV 视图空间立方体
- 实体广告牌(渲染.md §5.3):僵尸/村民/动物按贴图渲染,动物按 ID 轮换外观
- HUD 换原版精灵:crosshair/hotbar/hotbar_selection/heart/food(UI还原.md §3.4)
- 主菜单标题/按钮标签按请求字号光栅化(修复 24px 固定字号);版本角标 CJK
- 恢复背面剔除(相机修复后 '脚下黑块' 误诊解除);ESC 光标请求原子化防竞态
- dev 测试参数:-give/-crackstage/-spawntest/-esctest
2026-08-16 10:08:40 +08:00

146 lines
4.8 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
// 界面屏幕:主菜单与暂停菜单(UI还原.md §2:布局/控件/版本角标复刻原版)。
package ui
// Button 一个按钮:区域 + 悬停高亮 + 标签纹理。
type Button struct {
X, Y, W, H float32
Tex uint32 // 标签纹理(预光栅化中文)
LabelW float32
LabelH float32
Action string
}
// TitleScreen 主菜单(UI还原.md §2:土质背景 + 标题 + 原版按钮 + 版本/版权角标)。
type TitleScreen struct {
TitleTex uint32
TitleW float32
TitleH float32
SplashTex uint32 // 黄色 splash 文本(原版风格)
SplashW float32
SplashH float32
BgTex uint32 // 土质背景(16px dirt 平铺预渲染)
BtnTex uint32 // 原版按钮控件(widget/button.png 200×20)
BtnHover uint32 // 悬停高亮(button_highlighted.png)
Buttons []Button
Version string // 左下角版本(原版角标)
Copyright string // 右下角版权
VersionTex uint32 // 版本/版权 CJK 光栅纹理(中文字形)
VersionW float32
VersionH float32
CopyrightTex uint32
CopyrightW float32
CopyrightH float32
}
// HitTest 命中检测(按钮区域)。
func (s *TitleScreen) HitTest(mx, my float32) string {
for _, b := range s.Buttons {
if mx >= b.X && mx <= b.X+b.W && my >= b.Y && my <= b.Y+b.H {
return b.Action
}
}
return ""
}
// Draw 绘制主菜单(原版布局:背景 → 标题+splash → 按钮列 → 版本/版权角标)。
func (s *TitleScreen) Draw(r *Renderer, mx, my float32) {
w, h := float32(r.w), float32(r.h)
// 土质背景(UI还原.md §2 背景)
if s.BgTex != 0 {
r.pushQuad(quad{x: 0, y: 0, w: w, h: h, u0: 0, v0: 0, u1: 1, v1: 1,
r: 1, g: 1, b: 1, a: 1, tex: s.BgTex})
} else {
r.DrawRect(0, 0, w, h, [4]float32{0.08, 0.1, 0.12, 1})
}
// 标题(居中偏上,原版 logo 位置)
if s.TitleTex != 0 {
r.pushQuad(quad{x: w/2 - s.TitleW/2, y: h * 0.15, w: s.TitleW, h: s.TitleH,
u0: 0, v0: 0, u1: 1, v1: 1, r: 1, g: 1, b: 1, a: 1, tex: s.TitleTex})
}
// splash(标题右下,原版黄色倾斜文本——MVP 水平放置)
if s.SplashTex != 0 {
r.pushQuad(quad{x: w/2 + s.TitleW/2 - s.SplashW + 20, y: h*0.15 + s.TitleH, w: s.SplashW, h: s.SplashH,
u0: 0, v0: 0, u1: 1, v1: 1, r: 1, g: 1, b: 1, a: 1, tex: s.SplashTex})
}
// 按钮列(原版:居中偏下,200×20 控件 + 标签)
for _, b := range s.Buttons {
hover := mx >= b.X && mx <= b.X+b.W && my >= b.Y && my <= b.Y+b.H
tex := s.BtnTex
if hover && s.BtnHover != 0 {
tex = s.BtnHover
}
if tex != 0 {
r.pushQuad(quad{x: b.X, y: b.Y, w: b.W, h: b.H, u0: 0, v0: 0, u1: 1, v1: 1,
r: 1, g: 1, b: 1, a: 1, tex: tex})
}
if b.Tex != 0 {
// 标签居中
r.pushQuad(quad{x: b.X + (b.W-b.LabelW)/2, y: b.Y + (b.H-b.LabelH)/2, w: b.LabelW, h: b.LabelH,
u0: 0, v0: 0, u1: 1, v1: 1, r: 1, g: 1, b: 1, a: 1, tex: b.Tex})
}
}
// 版本角标(左下)与版权(右下,原版样式;中文用预光栅纹理)
if s.VersionTex != 0 {
r.pushQuad(quad{x: 4, y: h - s.VersionH - 4, w: s.VersionW, h: s.VersionH,
u0: 0, v0: 0, u1: 1, v1: 1, r: 1, g: 1, b: 1, a: 1, tex: s.VersionTex})
} else if s.Version != "" {
r.DrawText(8, h-12, s.Version, 1, [4]float32{1, 1, 1, 1})
}
if s.CopyrightTex != 0 {
r.pushQuad(quad{x: w - s.CopyrightW - 4, y: h - s.CopyrightH - 4, w: s.CopyrightW, h: s.CopyrightH,
u0: 0, v0: 0, u1: 1, v1: 1, r: 1, g: 1, b: 1, a: 1, tex: s.CopyrightTex})
} else if s.Copyright != "" {
r.DrawText(w-r.TextWidth(s.Copyright, 1)-8, h-12, s.Copyright, 1, [4]float32{1, 1, 1, 1})
}
}
// PauseScreen 暂停菜单(UI还原.md §2:半透明遮罩 + 原版按钮)。
type PauseScreen struct {
TitleTex uint32
TitleW float32
TitleH float32
BtnTex uint32
BtnHover uint32
Buttons []Button
}
// HitTest 命中检测。
func (s *PauseScreen) HitTest(mx, my float32) string {
for _, b := range s.Buttons {
if mx >= b.X && mx <= b.X+b.W && my >= b.Y && my <= b.Y+b.H {
return b.Action
}
}
return ""
}
// Draw 绘制暂停菜单。
func (s *PauseScreen) Draw(r *Renderer, mx, my float32) {
w, h := float32(r.w), float32(r.h)
r.DrawRect(0, 0, w, h, [4]float32{0, 0, 0, 0.6})
if s.TitleTex != 0 {
r.pushQuad(quad{x: w/2 - s.TitleW/2, y: h * 0.15, w: s.TitleW, h: s.TitleH,
u0: 0, v0: 0, u1: 1, v1: 1, r: 1, g: 1, b: 1, a: 1, tex: s.TitleTex})
}
for _, b := range s.Buttons {
hover := mx >= b.X && mx <= b.X+b.W && my >= b.Y && my <= b.Y+b.H
tex := s.BtnTex
if hover && s.BtnHover != 0 {
tex = s.BtnHover
}
if tex != 0 {
r.pushQuad(quad{x: b.X, y: b.Y, w: b.W, h: b.H, u0: 0, v0: 0, u1: 1, v1: 1,
r: 1, g: 1, b: 1, a: 1, tex: tex})
}
if b.Tex != 0 {
r.pushQuad(quad{x: b.X + (b.W-b.LabelW)/2, y: b.Y + (b.H-b.LabelH)/2, w: b.LabelW, h: b.LabelH,
u0: 0, v0: 0, u1: 1, v1: 1, r: 1, g: 1, b: 1, a: 1, tex: b.Tex})
}
}
}