Files
ngzz-mc/internal/ui/options.go
2026-09-19 14:21:08 +08:00

138 lines
4.0 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、UI与输入.md §6
// 鼠标灵敏度 / 音量滑条 + 世界种子显示 + 按键教程列表 + 完成按钮。
// 滑条点击左/右半区为减/加;值由调用方持有并注入。
package ui
import (
"fmt"
)
// OptionsScreen 设置界面。
type OptionsScreen struct {
BtnTex uint32 // 原版按钮控件
BtnHover uint32
TitleTex uint32 // 「设置」标题
TitleW float32
TitleH float32
// CJK 标签与按键教程(构建时预光栅化)
SensLabelTex uint32
SensLabelW float32
SensLabelH float32
VolLabelTex uint32
VolLabelW float32
VolLabelH float32
ControlsTex uint32
ControlsW float32
ControlsH float32
SeedLabelTex uint32
SeedLabelW float32
SeedLabelH float32
DoneLabelTex uint32
DoneLabelW float32
DoneLabelH float32
ox float32 // 相对 1280 基准的水平偏移(窗口居中)
}
// 滑条区域屏幕像素1280×720 布局)。
var (
optSensBar = [4]float32{540, 260, 200, 10}
optVolBar = [4]float32{540, 330, 200, 10}
optDone = [4]float32{540, 560, 200, 20}
)
// Layout 按窗口宽度水平居中控件(相对 1280 基准)。
func (s *OptionsScreen) Layout(winW float32) {
s.ox = winW/2 - 640
}
func (s *OptionsScreen) bar(base [4]float32) [4]float32 {
return [4]float32{base[0] + s.ox, base[1], base[2], base[3]}
}
// HitTest 命中检测:返回 "sens-"/"sens+"/"vol-"/"vol+"/"done"/""。
func (s *OptionsScreen) HitTest(mx, my float32) string {
hit := func(r [4]float32) bool {
return mx >= r[0] && mx < r[0]+r[2] && my >= r[1] && my < r[1]+r[3]
}
sens := s.bar(optSensBar)
if hit(sens) {
if mx < sens[0]+sens[2]/2 {
return "sens-"
}
return "sens+"
}
vol := s.bar(optVolBar)
if hit(vol) {
if mx < vol[0]+vol[2]/2 {
return "vol-"
}
return "vol+"
}
if hit(s.bar(optDone)) {
return "done"
}
return ""
}
// Draw 绘制设置界面(调用方已 Begin
func (s *OptionsScreen) Draw(r *Renderer, mx, my, sens, vol float32) {
w, h := float32(r.w), float32(r.h)
// 背景:土质暗色 + 标题
r.DrawRect(0, 0, w, h, [4]float32{0.10, 0.12, 0.10, 0.92})
if s.TitleTex != 0 {
r.pushQuad(quad{x: w/2 - s.TitleW/2, y: h * 0.10, 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})
}
// 标签
s.blitLabel(r, s.SensLabelTex, s.SensLabelW, s.SensLabelH, 380+s.ox, 255)
s.blitLabel(r, s.VolLabelTex, s.VolLabelW, s.VolLabelH, 380+s.ox, 325)
// 滑条(底 + 填充 + 值)
s.drawSlider(r, s.bar(optSensBar), sens)
s.drawSlider(r, s.bar(optVolBar), vol)
// 世界种子
s.blitLabel(r, s.SeedLabelTex, s.SeedLabelW, s.SeedLabelH, 380+s.ox, 395)
// 按键教程列表
s.blitLabel(r, s.ControlsTex, s.ControlsW, s.ControlsH, 380+s.ox, 435)
// 完成按钮
done := s.bar(optDone)
hover := mx >= done[0] && mx < done[0]+done[2] && my >= done[1] && my < done[1]+done[3]
tex := s.BtnTex
if hover && s.BtnHover != 0 {
tex = s.BtnHover
}
if tex != 0 {
r.pushQuad(quad{x: done[0], y: done[1], w: done[2], h: done[3],
u0: 0, v0: 0, u1: 1, v1: 1, r: 1, g: 1, b: 1, a: 1, tex: tex})
}
if s.DoneLabelTex != 0 {
r.pushQuad(quad{x: done[0] + (done[2]-s.DoneLabelW)/2, y: done[1] + (done[3]-s.DoneLabelH)/2,
w: s.DoneLabelW, h: s.DoneLabelH, u0: 0, v0: 0, u1: 1, v1: 1,
r: 1, g: 1, b: 1, a: 1, tex: s.DoneLabelTex})
}
}
// drawSlider 绘制滑条:底轨 + 按比例填充 + 数值文本01
func (s *OptionsScreen) drawSlider(r *Renderer, bar [4]float32, v float32) {
if v < 0 {
v = 0
}
if v > 1 {
v = 1
}
r.DrawRect(bar[0], bar[1], bar[2], bar[3], [4]float32{0.2, 0.2, 0.2, 1})
r.DrawRect(bar[0], bar[1], bar[2]*v, bar[3], [4]float32{0.4, 0.9, 0.4, 1})
r.DrawText(bar[0]+bar[2]+12, bar[1]-6, fmt.Sprintf("%.0f%%", v*100), 1, [4]float32{1, 1, 1, 1})
}
func (s *OptionsScreen) blitLabel(r *Renderer, tex uint32, w, h, x, y float32) {
if tex == 0 {
return
}
r.pushQuad(quad{x: x, y: y, w: w, h: h, u0: 0, v0: 0, u1: 1, v1: 1,
r: 1, g: 1, b: 1, a: 1, tex: tex})
}