//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 } // 滑条区域(屏幕像素,1280×720 布局)。 var ( optSensBar = [4]float32{540, 260, 200, 10} optVolBar = [4]float32{540, 330, 200, 10} optDone = [4]float32{540, 560, 200, 20} ) // 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] } if hit(optSensBar) { if mx < optSensBar[0]+optSensBar[2]/2 { return "sens-" } return "sens+" } if hit(optVolBar) { if mx < optVolBar[0]+optVolBar[2]/2 { return "vol-" } return "vol+" } if hit(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, 255) s.blitLabel(r, s.VolLabelTex, s.VolLabelW, s.VolLabelH, 380, 325) // 滑条(底 + 填充 + 值) s.drawSlider(r, optSensBar, sens) s.drawSlider(r, optVolBar, vol) // 世界种子 s.blitLabel(r, s.SeedLabelTex, s.SeedLabelW, s.SeedLabelH, 380, 395) // 按键教程列表 s.blitLabel(r, s.ControlsTex, s.ControlsW, s.ControlsH, 380, 435) // 完成按钮 hover := mx >= optDone[0] && mx < optDone[0]+optDone[2] && my >= optDone[1] && my < optDone[1]+optDone[3] tex := s.BtnTex if hover && s.BtnHover != 0 { tex = s.BtnHover } if tex != 0 { r.pushQuad(quad{x: optDone[0], y: optDone[1], w: optDone[2], h: optDone[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: optDone[0] + (optDone[2]-s.DoneLabelW)/2, y: optDone[1] + (optDone[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 绘制滑条:底轨 + 按比例填充 + 数值文本(0–1)。 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}) }