diff options
| author | Somhairle H. Marisol <[email protected]> | 2026-09-22 09:28:32 +0800 |
|---|---|---|
| committer | Somhairle H. Marisol <[email protected]> | 2026-09-22 09:28:32 +0800 |
| commit | fe5162a3e86fe63677c3be547750b1ac30337614 (patch) | |
| tree | 767e2abd668b78e22ac21fda98510b52ace7a699 /src | |
| parent | ebe7f80bcf54189cc51a15bf17b99293b6ff57a3 (diff) | |
| download | living-village-fe5162a3e86fe63677c3be547750b1ac30337614.tar.gz | |
feat(desktop): P26 HUD 常驻元素半透明底色 + 1px 描边(昼/夜可读)
Diffstat (limited to 'src')
| -rw-r--r-- | src/LivingVillage.Desktop.Tests/P16UiTests.fs | 21 | ||||
| -rw-r--r-- | src/LivingVillage.Desktop/Game.fs | 8 | ||||
| -rw-r--r-- | src/LivingVillage.Desktop/HudLayout.fs | 22 |
3 files changed, 46 insertions, 5 deletions
diff --git a/src/LivingVillage.Desktop.Tests/P16UiTests.fs b/src/LivingVillage.Desktop.Tests/P16UiTests.fs index 2e474ac..b68bbd3 100644 --- a/src/LivingVillage.Desktop.Tests/P16UiTests.fs +++ b/src/LivingVillage.Desktop.Tests/P16UiTests.fs @@ -128,6 +128,27 @@ type P16HudTests () = Assert.IsTrue(ChineseText.requiredUiLabels |> List.contains "体力") Assert.IsTrue(ChineseText.requiredUiLabels |> List.contains "钱币") + [<TestMethod>] + member _.HudPersistentSegmentsGetBackdropAndOutlineInBothPeriods () = + // P26:白天/夜景两套调色板都要有半透明底色 + 不透明深色描边,保证常驻元素可读。 + for mode in [ Day; Night ] do + let palette = HudLayout.palette mode + Assert.IsTrue(palette.SegmentBackdrop.A > 0uy, "segment backdrop must be translucent, not opaque") + Assert.IsTrue(palette.SegmentBackdrop.A < 255uy, "segment backdrop must keep the scene visible") + Assert.AreEqual<byte>(255uy, palette.Outline.A) + Assert.IsTrue(palette.Outline.R < 40uy && palette.Outline.G < 40uy && palette.Outline.B < 40uy) + let outlineSum = int palette.Outline.R + int palette.Outline.G + int palette.Outline.B + let valueSum = int palette.Value.R + int palette.Value.G + int palette.Value.B + Assert.IsTrue(outlineSum < valueSum, "outline must contrast with the bright value text") + Assert.AreEqual<int>(4, HudLayout.textOutlineOffsets.Length) + for (dx, dy) in HudLayout.textOutlineOffsets do + Assert.IsTrue(dx <> 0 || dy <> 0, "outline offset must be non-zero") + let placed = HudLayout.layout (HudLayout.segments world SimulationControl.initial) + for (x, width, _) in placed do + let rect = HudLayout.segmentBackdrop x width + Assert.IsTrue(rect.X <= x && rect.X + rect.Width >= x + width, "backdrop must cover the segment text") + Assert.IsTrue(rect.Y >= 0 && rect.Y + rect.Height <= HudLayout.barHeight, "backdrop must stay inside the bar") + // ---- P16-FIX 菜单截图帧表 ---- [<TestClass>] diff --git a/src/LivingVillage.Desktop/Game.fs b/src/LivingVillage.Desktop/Game.fs index 537ae3b..591d1b9 100644 --- a/src/LivingVillage.Desktop/Game.fs +++ b/src/LivingVillage.Desktop/Game.fs @@ -1230,9 +1230,13 @@ type LivingVillageGame() as this = spriteBatch.Draw(pixel, Rectangle(0, 0, viewport.Width, barHeight / 2), palette.BarTop) spriteBatch.Draw(pixel, Rectangle(0, barHeight / 2, viewport.Width, barHeight - barHeight / 2), palette.BarBottom) spriteBatch.Draw(pixel, Rectangle(0, barHeight, viewport.Width, HudLayout.accentHeight), palette.Accent) - for (x, _, segment) in placed do - PixelText.draw spriteBatch pixel cjkAtlas x textY HudLayout.textScale palette.Label segment.Label + for (x, width, segment) in placed do + spriteBatch.Draw(pixel, HudLayout.segmentBackdrop x width, palette.SegmentBackdrop) let labelAdvanced = FloatingPrompt.measureWidth (segment.Label + " ") HudLayout.textScale + for (dx, dy) in HudLayout.textOutlineOffsets do + PixelText.draw spriteBatch pixel cjkAtlas (x + dx) (textY + dy) HudLayout.textScale palette.Outline segment.Label + PixelText.draw spriteBatch pixel cjkAtlas (x + labelAdvanced + dx) (textY + dy) HudLayout.textScale palette.Outline segment.Value + PixelText.draw spriteBatch pixel cjkAtlas x textY HudLayout.textScale palette.Label segment.Label PixelText.draw spriteBatch pixel cjkAtlas (x + labelAdvanced) textY HudLayout.textScale palette.Value segment.Value spriteBatch.End() diff --git a/src/LivingVillage.Desktop/HudLayout.fs b/src/LivingVillage.Desktop/HudLayout.fs index d943cec..f3e4c12 100644 --- a/src/LivingVillage.Desktop/HudLayout.fs +++ b/src/LivingVillage.Desktop/HudLayout.fs @@ -44,14 +44,26 @@ module HudLayout = (paddingX, []) List.rev placed + /// P26:常驻片段文本的 1px 描边偏移(上/下/左/右),保证白天/夜景下都可读。 + let textOutlineOffsets : (int * int) list = [ (-1, 0); (1, 0); (0, -1); (0, 1) ] + + /// P26:常驻片段的半透明底色药丸,贴合文字高度并在左右各留 6px;纯函数便于回归。 + let segmentBackdrop (x: int) (width: int) : Microsoft.Xna.Framework.Rectangle = + let height = 8 * textScale + 10 + let y = (barHeight - height) / 2 + Microsoft.Xna.Framework.Rectangle(x - 6, y, width + 12, height) + type Palette = { BarTop: Color BarBottom: Color Accent: Color Label: Color - Value: Color } + Value: Color + SegmentBackdrop: Color + Outline: Color } /// 白天暖色 / 夜晚(冬夜)冷色微调;均为半透明渐变窄条,无大黑块。 + /// 每个常驻片段再叠一层半透明底色 + 1px 深色描边,双时段都能读清。 let palette (mode: M6DayNight) : Palette = match mode with | Day -> @@ -59,10 +71,14 @@ module HudLayout = BarBottom = Color(22, 34, 40, 200) Accent = Color(212, 168, 96) Label = Color(206, 220, 208) - Value = Color(242, 248, 234) } + Value = Color(242, 248, 234) + SegmentBackdrop = Color(8, 14, 20, 150) + Outline = Color(6, 10, 14, 255) } | Night -> { BarTop = Color(22, 30, 48, 205) BarBottom = Color(13, 19, 33, 205) Accent = Color(120, 168, 196) Label = Color(176, 196, 214) - Value = Color(228, 238, 246) } + Value = Color(228, 238, 246) + SegmentBackdrop = Color(4, 8, 16, 160) + Outline = Color(3, 6, 12, 255) } |
