From ef78374c316587fb585b72e6d427cd6e61203655 Mon Sep 17 00:00:00 2001 From: "Somhairle H. Marisol" Date: Tue, 22 Sep 2026 02:28:31 +0800 Subject: feat(mapgen): 参数化确定性生成器与视口裁剪(P20 第一步) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增纯函数 MapGen:尺寸参数化(默认 64x48,可 256x192),splitmix64 随机游走河道 + 2-3 octave value noise + 泊松式布点;核心区整平后以 BFS 校验 5x6=30 个 spawn 全部可达,不可达确定性修整并复验。 - visibleTileRange/visibleTileCount 纯函数,drawWorld 改为调用之(行为不变); drawMapFitted/drawMapViewport 仅证据用。 - 存档沿用 v2/v3 边界字段,补参数化边界 round-trip 测试,未改 Sim 语义。 - 回归:Desktop 109->115、Kernel 89->90;final_digest 与基线一致。 --- src/LivingVillage.Desktop/VillageArt.fs | 67 +++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 4 deletions(-) (limited to 'src/LivingVillage.Desktop/VillageArt.fs') diff --git a/src/LivingVillage.Desktop/VillageArt.fs b/src/LivingVillage.Desktop/VillageArt.fs index 11a6d71..51e17e0 100644 --- a/src/LivingVillage.Desktop/VillageArt.fs +++ b/src/LivingVillage.Desktop/VillageArt.fs @@ -643,10 +643,8 @@ module VillageArt = | Some cc0 -> drawCc0Tile spriteBatch cc0 tint (cc0GroundRect position) (position.X * Sim.tilePixels - int camera.X) (position.Y * Sim.tilePixels - int camera.Y) | None -> draw position GrassSprite - let tx0 = max 0 (int camera.X / Sim.tilePixels) - let ty0 = max 0 (int camera.Y / Sim.tilePixels) - let tx1 = min (Sim.mapWidthTiles - 1) ((int camera.X + viewportWidth) / Sim.tilePixels + 1) - let ty1 = min (Sim.mapHeightTiles - 1) ((int camera.Y + viewportHeight) / Sim.tilePixels + 1) + let tx0, ty0, tx1, ty1 = + MapGen.visibleTileRange Sim.mapWidthTiles Sim.mapHeightTiles (int camera.X) (int camera.Y) viewportWidth viewportHeight for y in ty0 .. ty1 do for x in tx0 .. tx1 do drawBaseTile (tilePosition x y) @@ -724,6 +722,67 @@ module VillageArt = | House -> draw (offsetTile (worldTile plan prop.Position) 0 -3) smokeSprite | _ -> () + /// P20 证据用:把整张 MapGen 地图等比缩放到视口内绘制,返回绘制的瓦片数。 + /// 仅用于 `LV_MAPGEN_SHOT` 整图取证,不参与正常游戏循环。 + let drawMapFitted + (spriteBatch: SpriteBatch) + (textures: ArtTextures) + (viewportWidth: int) + (viewportHeight: int) + (map: MapGen.Result) + (tint: XnaColor) : int = + let scale = + min (float32 viewportWidth / float32 (max 1 (map.Width * Sim.tilePixels))) + (float32 viewportHeight / float32 (max 1 (map.Height * Sim.tilePixels))) + let cell = max 1 (int (float32 Sim.tilePixels * scale)) + let offsetX = (viewportWidth - map.Width * cell) / 2 + let offsetY = (viewportHeight - map.Height * cell) / 2 + let spriteOf code = + match code with + | 1 -> WaterSprite + | 2 + | 3 -> StonePathSprite + | _ -> GrassSprite + for y in 0 .. map.Height - 1 do + for x in 0 .. map.Width - 1 do + let code = map.Tiles.[y * map.Width + x] + spriteBatch.Draw(textures.TileAtlas, XnaRectangle(offsetX + x * cell, offsetY + y * cell, cell, cell), tileSourceRectangle (spriteOf code), tint) + map.Width * map.Height + + /// P20 证据用:按相机做视口裁剪,只绘制可见瓦片(1:1 像素),返回实际绘制数。 + let drawMapViewport + (spriteBatch: SpriteBatch) + (textures: ArtTextures) + (camera: XnaVector2) + (viewportWidth: int) + (viewportHeight: int) + (map: MapGen.Result) + (tick: int64) + (tint: XnaColor) : int = + let x0, y0, x1, y1 = + MapGen.visibleTileRange map.Width map.Height (int camera.X) (int camera.Y) viewportWidth viewportHeight + let spriteOf (x: int) (y: int) code = + match code with + | 1 -> + match waterFrameTickAt tick x y with + | 0 -> WaterSprite + | 1 -> WaterSpriteB + | _ -> WaterSpriteC + | 2 + | 3 -> StonePathSprite + | _ -> GrassSprite + let mutable drawn = 0 + for y in y0 .. y1 do + for x in x0 .. x1 do + let code = map.Tiles.[y * map.Width + x] + spriteBatch.Draw( + textures.TileAtlas, + XnaRectangle(x * Sim.tilePixels - int camera.X, y * Sim.tilePixels - int camera.Y, Sim.tilePixels, Sim.tilePixels), + tileSourceRectangle (spriteOf x y code), + tint) + drawn <- drawn + 1 + drawn + let drawCharacter (spriteBatch: SpriteBatch) (textures: ArtTextures) -- cgit v1.2.3