diff options
Diffstat (limited to 'src/LivingVillage.Desktop')
| -rw-r--r-- | src/LivingVillage.Desktop/FloaterArt.fs | 103 | ||||
| -rw-r--r-- | src/LivingVillage.Desktop/Game.fs | 111 | ||||
| -rw-r--r-- | src/LivingVillage.Desktop/LivingVillage.Desktop.fsproj | 3 | ||||
| -rw-r--r-- | src/LivingVillage.Desktop/SceneDetail.fs | 118 | ||||
| -rw-r--r-- | src/LivingVillage.Desktop/SceneDetailRender.fs | 97 | ||||
| -rw-r--r-- | src/LivingVillage.Desktop/VillageArt.fs | 3 |
6 files changed, 435 insertions, 0 deletions
diff --git a/src/LivingVillage.Desktop/FloaterArt.fs b/src/LivingVillage.Desktop/FloaterArt.fs new file mode 100644 index 0000000..53a0793 --- /dev/null +++ b/src/LivingVillage.Desktop/FloaterArt.fs @@ -0,0 +1,103 @@ +namespace LivingVillage.Desktop + +/// P47 漂浮小元素(鸭子浮标 / 漂浮落叶)原创点阵图集生成器。 +/// 复用 P45 `CharacterArt` 的点阵像素代码管线:把 12x8 的字符画按 2x 放大成 24x16 的 +/// 单元,4 格一横排(鸭 A/B、叶 A/B)。全部为原创像素画,不采样任何外部素材, +/// 也不使用程序几何矩形冒充成品。 +module FloaterArt = + + let sourceWidth = 12 + let sourceHeight = 8 + let scale = 2 + let cellWidth = sourceWidth * scale // 24 + let cellHeight = sourceHeight * scale // 16 + let frameCount = 2 + let kindCount = 2 + let atlasWidth = cellWidth * frameCount * kindCount // 96 + let atlasHeight = cellHeight // 16 + + let duckIndex = 0 + let leafIndex = 1 + + let private duckFrameA = + [ "............" + "....2222...." + "....2422...." + "...32222...." + "..1111111..." + ".111111111.." + ".111111111.." + "..5555555..." ] + + let private duckFrameB = + [ "............" + ".....2222..." + ".....2422..." + "....32222..." + "..1111111..." + ".111111111.." + ".111111111.." + "..5555555..." ] + + let private leafFrameA = + [ "............" + ".....6......" + "....676....." + "...67776...." + "..6777776..." + "...67776...." + "....676....." + ".....6......" ] + + let private leafFrameB = + [ "............" + ".....6......" + "....676....." + "...67776...." + "..6776776..." + "...67776...." + "....676....." + ".....6......" ] + + let private colorOf (ch: char) : (int * int * int) option = + match ch with + | '1' -> Some(236, 228, 206) // 鸭身奶白 + | '2' -> Some(248, 242, 228) // 鸭头高光 + | '3' -> Some(232, 150, 60) // 鸭喙橙 + | '4' -> Some(40, 42, 54) // 鸭眼深色 + | '5' -> Some(70, 100, 120) // 水面暗线 + | '6' -> Some(96, 120, 64) // 叶缘深绿 + | '7' -> Some(150, 172, 96) // 叶身浅绿 + | _ -> None + + /// 单元索引:kind 0=鸭 1=叶,frame 0/1。 + let cellIndex (kind: int) (frame: int) : int = + (kind % kindCount) * frameCount + (frame % frameCount) + + let sourceRectangle (kind: int) (frame: int) : Microsoft.Xna.Framework.Rectangle = + Microsoft.Xna.Framework.Rectangle(cellIndex kind frame * cellWidth, 0, cellWidth, cellHeight) + + let buildAtlas (device: Microsoft.Xna.Framework.Graphics.GraphicsDevice) : Microsoft.Xna.Framework.Graphics.Texture2D = + let data = Array.zeroCreate<Microsoft.Xna.Framework.Color> (atlasWidth * atlasHeight) + let put (index: int) (rows: string list) = + rows + |> List.iteri (fun rowIndex row -> + row + |> Seq.iteri (fun columnIndex ch -> + match colorOf ch with + | None -> () + | Some(r, g, b) -> + let px = index * cellWidth + columnIndex * scale + for dy in 0 .. scale - 1 do + for dx in 0 .. scale - 1 do + let tx = px + dx + let ty = rowIndex * scale + dy + if tx >= 0 && tx < atlasWidth && ty >= 0 && ty < atlasHeight then + data.[ty * atlasWidth + tx] <- Microsoft.Xna.Framework.Color(r, g, b))) + put (cellIndex duckIndex 0) duckFrameA + put (cellIndex duckIndex 1) duckFrameB + put (cellIndex leafIndex 0) leafFrameA + put (cellIndex leafIndex 1) leafFrameB + let texture = new Microsoft.Xna.Framework.Graphics.Texture2D(device, atlasWidth, atlasHeight) + texture.SetData data + texture diff --git a/src/LivingVillage.Desktop/Game.fs b/src/LivingVillage.Desktop/Game.fs index 578be72..a187cc4 100644 --- a/src/LivingVillage.Desktop/Game.fs +++ b/src/LivingVillage.Desktop/Game.fs @@ -331,6 +331,11 @@ type LivingVillageGame() as this = let mutable p46CloudLateDone = false let mutable p46MenuADone = false let mutable p46MenuBDone = false + // P47 证据钩子:LV_P47_SHOT=1 时在真实生成器世界里拍「白天河道 / 夜晚河道 / 近景」三帧。 + let p47ShotMode = Environment.GetEnvironmentVariable("LV_P47_SHOT") = "1" + let mutable p47Step = 0 + let mutable p47PendingName = "" + let mutable p47Pending = false let mutable autoplayFrames = 0 let mutable flowStep = 0 let mutable flowHold = 0 @@ -986,6 +991,88 @@ type LivingVillageGame() as this = else p46MenuBDone <- true save p46PendingName + /// P47 证据:真实生成器世界里拍「白天河道 / 夜晚河道 / 近景」三帧,含 P47 场景细节。 + member private this.PrepareP47Shot () = + let setHour (hour: float) = + let tick = M6Presentation.resolveStartTick (Some hour) false + world <- { world with Tick = tick; Time = float tick * Sim.dtSeconds } + let placeCameraOn (tileX: int) (tileY: int) = + world <- { world with Avatar = { world.Avatar with Pos = { X = float32 (tileX * Sim.tilePixels); Y = float32 (tileY * Sim.tilePixels) } } } + this.CenterCamera() + if not p47Pending then + match riverscapeMap with + | None -> this.Exit() + | Some map -> + let riverside = + // 取第一处「光源正下方有水面」的倒影点,相机对准其光源格,倒影落在正下方。 + match SceneDetail.reflectionStreaks map with + | (rx, ry, _) :: _ -> (rx, ry - 1) + | [] -> + match VillageArt.cc0LanternTiles map 12 with + | (lx, ly) :: _ -> (lx, ly) + | [] -> + match map.Bridges |> List.sortBy (fun (x, y) -> (x, y)) |> List.tryHead with + | Some (bx, by) -> (bx, by) + | None -> (map.Core.MinX, map.Core.MinY) + let closeup = + match SceneDetail.floatingProps map with + | (fx, fy, _) :: _ -> (fx, fy) + | [] -> riverside + let reportVisibleReflections () = + let vw = this.GraphicsDevice.Viewport.Width + let vh = this.GraphicsDevice.Viewport.Height + for (lx, ly) in SceneDetail.lightSourceTiles map do + for d in 1 .. SceneDetail.reflectionRange do + let ry = ly + d + if SceneDetail.isWater map lx ry then + let ssx = lx * Sim.tilePixels - int camera.X + let ssy = ly * Sim.tilePixels - int camera.Y + let rsx = lx * Sim.tilePixels - int camera.X + let rsy = ry * Sim.tilePixels - int camera.Y + if ssx >= 0 && ssx + Sim.tilePixels <= vw && ssy >= 0 && ssy + Sim.tilePixels <= vh + && rsy + Sim.tilePixels <= vh then + printfn + "p47-reflection src=%d,%d dst=%d,%d alpha=%d" + ssx ssy rsx rsy (SceneDetail.reflectionAlphaAt d) + printfn + "p47-elements reeds=%d floats=%d reflections=%d sources=%d" + (List.length (SceneDetail.bankReedTiles map)) + (List.length (SceneDetail.floatingProps map)) + (List.length (SceneDetail.reflectionStreaks map)) + (List.length (SceneDetail.lightSourceTiles map)) + match p47Step with + | 0 -> + setHour 12.0 + placeCameraOn (fst riverside) (snd riverside) + reportVisibleReflections () + p47PendingName <- "p47-day" + p47Pending <- true + | 1 -> + setHour 23.5 + placeCameraOn (fst riverside) (snd riverside) + reportVisibleReflections () + p47PendingName <- "p47-night" + p47Pending <- true + | 2 -> + setHour 12.0 + placeCameraOn (fst closeup) (snd closeup) + reportVisibleReflections () + p47PendingName <- "p47-close" + p47Pending <- true + | _ -> + printfn "p47-shot=done" + this.Exit() + + /// P47 证据:在 `Draw` 末尾落盘当前后台缓冲。 + member private this.CaptureP47Shot () = + if p47Pending then + p47Pending <- false + System.IO.Directory.CreateDirectory recordDirectory |> ignore + this.SaveBackBuffer(sprintf "%s/%s.png" recordDirectory p47PendingName) + printfn "p47-shot=%s tick=%d pos=(%.0f,%.0f) camera=(%.0f,%.0f)" + p47PendingName world.Tick world.Avatar.Pos.X world.Avatar.Pos.Y camera.X camera.Y + p47Step <- p47Step + 1 + /// P46 证据:菜单阶段按帧表把 menuEntranceFrame 钉到灯笼呼吸的波峰/波谷,拍两相位。 member private this.PrepareP46Shot () = if p46MenuADone && p46MenuBDone then @@ -1385,6 +1472,14 @@ type LivingVillageGame() as this = elif p46ShotMode then if menu.Page = MainMenu then this.PrepareP46Shot() + elif p47ShotMode then + if menu.Page = MainMenu then + this.DispatchMenuInput Confirm + elif menu.Page = OccupationSelect then + if menu.Selected <> 0 then this.DispatchMenuInput Down + else this.DispatchMenuInput Confirm + elif menu.Page = Playing then + this.PrepareP47Shot() elif menu.Page = Playing then this.UpdatePlaying gameTime kb pressed pressedAny elif menuShotMode then @@ -1796,6 +1891,8 @@ type LivingVillageGame() as this = this.CaptureP45Shot() if p46ShotMode then this.CaptureP46Shot() + if p47ShotMode && menu.Page = Playing then + this.CaptureP47Shot() /// 启动画面:分层天色 + 屋脊/水面剪影 + 标题淡入上浮 + 跳过提示,全部为帧计数纯函数。 member private this.DrawSplash() = @@ -2047,6 +2144,20 @@ type LivingVillageGame() as this = world.Tick profile.WorldTint + // P47 场景细节:河道倒影 + 岸边芦苇加密度 + 漂浮元素 + 近景楼层层错落(统一光源阴影)。 + match riverscapeMap with + | Some map -> + SceneDetailRender.draw + spriteBatch + artTextures + pixel + camera + viewport.Width + viewport.Height + map + world.Tick + | None -> () + // Warm lantern / window glows strengthen as night falls (pure function of tick phase). // Concentric premultiplied circles follow a quadratic radial falloff (radius 3-4 tiles), // so the core stays a soft warm disc instead of saturating to a white block and no glow diff --git a/src/LivingVillage.Desktop/LivingVillage.Desktop.fsproj b/src/LivingVillage.Desktop/LivingVillage.Desktop.fsproj index 22d0d15..9aa3f94 100644 --- a/src/LivingVillage.Desktop/LivingVillage.Desktop.fsproj +++ b/src/LivingVillage.Desktop/LivingVillage.Desktop.fsproj @@ -11,7 +11,10 @@ <Compile Include="MapGen.fs" /> <Compile Include="WorldBootstrap.fs" /> <Compile Include="CharacterArt.fs" /> + <Compile Include="FloaterArt.fs" /> <Compile Include="VillageArt.fs" /> + <Compile Include="SceneDetail.fs" /> + <Compile Include="SceneDetailRender.fs" /> <Compile Include="ChineseText.fs" /> <Compile Include="CjkGlyphAtlas.fs" /> <Compile Include="LaunchScreen.fs" /> diff --git a/src/LivingVillage.Desktop/SceneDetail.fs b/src/LivingVillage.Desktop/SceneDetail.fs new file mode 100644 index 0000000..c51c47a --- /dev/null +++ b/src/LivingVillage.Desktop/SceneDetail.fs @@ -0,0 +1,118 @@ +namespace LivingVillage.Desktop + +/// P47 场景细节升级(纯函数层):河道倒影、岸边植被加密度、漂浮元素、近景楼层层错落。 +/// 全部为帧计数 / 地图的纯函数:同参数逐字节可复现,无时钟、无随机源。 +module SceneDetail = + + open LivingVillage.Kernel + + let private positiveModulo64 (modulus: int64) (value: int64) : int64 = + let remainder = value % modulus + if remainder < 0L then remainder + modulus else remainder + + /// 统一光源:来自画面左上,所有阴影一律投向右下(方向统一,不随物体改变)。 + let shadowOffsetPx : int * int = (3, 4) + + let isWater (map: MapGen.Result) (x: int) (y: int) : bool = + x >= 0 && x < map.Width && y >= 0 && y < map.Height + && map.Tiles.[y * map.Width + x] = int MapGen.GroundTile.Water + + // ---------------- 河道水面倒影 ---------------- + + let reflectionMaxAlpha = 128 + let reflectionRange = 3 + + /// 倒影 alpha 随离光源距离递减;到 0 即止,绝不留下硬块。 + let reflectionAlphaAt (distance: int) : int = + max 0 (reflectionMaxAlpha - distance * 34) + + /// 倒影微抖:复用 waterFrameTickAt 的 3 相位,映射到 -1 / 0 / +1 像素水平位移。 + let reflectionWobbleAt (tick: int64) (x: int) (y: int) : int = + match VillageArt.waterFrameTickAt tick x y with + | 0 -> -1 + | 1 -> 0 + | _ -> 1 + + /// 岸上光源:桥头灯笼 + 主路石灯笼 + 门口红灯笼,去重后即倒影源。 + let lightSourceTiles (map: MapGen.Result) : (int * int) list = + (VillageArt.bridgeLanternTiles map + @ VillageArt.cc0LanternTiles map 12 + @ VillageArt.cc0RedLanternTiles map) + |> List.distinct + + /// 每个光源在其正下方连续水面格生成一条垂直倒影 (x, y, alpha)。 + let reflectionStreaks (map: MapGen.Result) : (int * int * int) list = + lightSourceTiles map + |> List.collect (fun (sx, sy) -> + [ for d in 1 .. reflectionRange do + let y = sy + d + if isWater map sx y then yield (sx, y, reflectionAlphaAt d) ]) + + // ---------------- 岸边植被加密度 ---------------- + + /// 在既有 `cc0ReedTiles(spacing 4)`(x%4=0 的贴水陆格)基础上补一层: + /// x%4<>0、且四邻任一为水的陆格,按位置 hash 取一半。于是芦苇带更密, + /// 又与既有芦苇错列不重叠。 + let bankReedTiles (map: MapGen.Result) : (int * int) list = + if map.Width <= 0 then + [] + else + [ for y in 1 .. map.Height - 2 do + for x in 0 .. map.Width - 1 do + let nearWater = + isWater map (x - 1) y + || isWater map (x + 1) y + || isWater map x (y - 1) + || isWater map x (y + 1) + if not (isWater map x y) && nearWater + && x % 4 <> 0 + && (x * 7 + y * 13) % 2 = 0 then + yield (x, y) ] + + // ---------------- 漂浮元素(鸭子浮标 / 漂浮落叶) ---------------- + + type FloatKind = + | DuckFloat + | FallenLeaf + + let floatKindOf (x: int) (y: int) : FloatKind = + if (x * 3 + y * 5) % 3 = 0 then DuckFloat else FallenLeaf + + /// 确定性落在开阔水面的漂浮元素(2 格连续水面、非桥面),数量即「元素数量指标」。 + let floatingProps (map: MapGen.Result) : (int * int * FloatKind) list = + if map.Width <= 4 then + [] + else + let bridgeSet = map.Bridges |> List.map (fun (x, y) -> y * map.Width + x) |> Set.ofList + let isBridge x y = Set.contains (y * map.Width + x) bridgeSet + [ for y in 2 .. map.Height - 3 do + for x in 2 .. map.Width - 3 do + if isWater map x y + && isWater map (x + 1) y + && not (isBridge x y) + && not (isBridge (x + 1) y) + && (x * 11 + y * 17) % 23 = 0 then + yield (x, y, floatKindOf x y) ] + + /// 按 tick 的慢漂移(像素):水平 ±2、垂直 ±1,纯函数、有界、逐帧连续。 + let floatDrift (tick: int64) (seed: int) : int * int = + let dx = int (positiveModulo64 5L (tick / 24L + int64 seed)) - 2 + let dy = int (positiveModulo64 3L (tick / 40L + int64 (seed * 7 + 1))) - 1 + (dx, dy) + + /// 该漂浮元素的帧(0/1),只随 tick 慢速切换,保证水面鸭/叶有轻微起伏。 + let floatFrame (tick: int64) (seed: int) : int = + int (positiveModulo64 2L (tick / 32L + int64 seed)) + + // ---------------- 近景楼层层错落(屏幕空间前景层) ---------------- + + /// 前景层屋顶分段:等宽 tile 分段、高度按确定性函数错落(0/8/16/24 px 四层), + /// 并随 tick 极慢移动(视差)。返回 (x, y, width)。 + let foregroundRooftops (viewportWidth: int) (viewportHeight: int) (tick: int64) : (int * int * int) list = + let tile = Sim.tilePixels + let count = max 1 (viewportWidth / tile + 2) + let baseY = viewportHeight - 64 + [ for i in 0 .. count - 1 do + let x = (i - 1) * tile + let stagger = ((i * 7 + int (tick / 120L)) % 4) * 8 + yield (x, baseY - stagger, tile) ] diff --git a/src/LivingVillage.Desktop/SceneDetailRender.fs b/src/LivingVillage.Desktop/SceneDetailRender.fs new file mode 100644 index 0000000..2136d25 --- /dev/null +++ b/src/LivingVillage.Desktop/SceneDetailRender.fs @@ -0,0 +1,97 @@ +namespace LivingVillage.Desktop + +open System +open Microsoft.Xna.Framework +open Microsoft.Xna.Framework.Graphics +open LivingVillage.Kernel + +/// P47 场景细节渲染层:把 `SceneDetail` 的纯函数结果落成画面。 +/// 反射用「光源精灵垂直镜像 + 相位微抖」,岸边芦苇/漂浮元素用既有瓦片精灵与原创点阵图集, +/// 近景屋顶用既有屋瓦精灵 + 统一方向阴影。全部只读,不改瓦片/可达性。 +module SceneDetailRender = + + let private tileX (camera: Vector2) (x: int) = x * Sim.tilePixels - int camera.X + let private tileY (camera: Vector2) (y: int) = y * Sim.tilePixels - int camera.Y + + let private drawFlipVertical + (spriteBatch: SpriteBatch) + (atlas: Texture2D) + (source: Rectangle) + (x: int) + (y: int) + (color: Color) = + spriteBatch.Draw( + atlas, + Rectangle(x, y, Sim.tilePixels, Sim.tilePixels), + Nullable(source), + color, + 0.0f, + Vector2.Zero, + SpriteEffects.FlipVertically, + 0.0f) + + /// 河道倒影:岸上每个光源在其正下方水面格画一份垂直镜像,随 waterFrameTickAt 相位水平微抖。 + let private drawReflections (spriteBatch: SpriteBatch) (textures: VillageArt.ArtTextures) (camera: Vector2) (map: MapGen.Result) (tick: int64) = + let source, atlas = + match textures.Cc0TileAtlas with + | Some cc0 -> VillageArt.cc0TileRect VillageArt.cc0TileIndexStoneLantern, cc0 + | None -> VillageArt.atlasTileRectangle 20, textures.TileAtlas + for (x, y, alpha) in SceneDetail.reflectionStreaks map do + let wobble = SceneDetail.reflectionWobbleAt tick x y + let color = VillageArt.premultiply 255 214 168 alpha + drawFlipVertical spriteBatch atlas source (tileX camera x + wobble) (tileY camera y) color + + /// 岸边芦苇加密度:用既有芦苇精灵补绘一层贴水芦苇。 + let private drawBankReeds (spriteBatch: SpriteBatch) (textures: VillageArt.ArtTextures) (camera: Vector2) (map: MapGen.Result) = + match textures.Cc0TileAtlas with + | Some cc0 -> + for (x, y) in SceneDetail.bankReedTiles map do + spriteBatch.Draw(cc0, Rectangle(tileX camera x, tileY camera y, Sim.tilePixels, Sim.tilePixels), Nullable(VillageArt.cc0TileRect VillageArt.cc0TileIndexReeds), Color.White) + | None -> + let source = VillageArt.atlasTileRectangle 18 + for (x, y) in SceneDetail.bankReedTiles map do + spriteBatch.Draw(textures.TileAtlas, Rectangle(tileX camera x, tileY camera y, Sim.tilePixels, Sim.tilePixels), Nullable(source), Color.White) + + /// 漂浮元素:原创点阵鸭/叶,按 tick 慢漂移。 + let private drawFloaters (spriteBatch: SpriteBatch) (textures: VillageArt.ArtTextures) (camera: Vector2) (map: MapGen.Result) (tick: int64) = + for (x, y, kind) in SceneDetail.floatingProps map do + let seed = x * 31 + y * 7 + let dx, dy = SceneDetail.floatDrift tick seed + let frame = SceneDetail.floatFrame tick seed + let kindIndex = + match kind with + | SceneDetail.DuckFloat -> FloaterArt.duckIndex + | SceneDetail.FallenLeaf -> FloaterArt.leafIndex + let source = FloaterArt.sourceRectangle kindIndex frame + spriteBatch.Draw( + textures.FloaterAtlas, + Rectangle(tileX camera x + dx + (Sim.tilePixels - FloaterArt.cellWidth) / 2, tileY camera y + dy + (Sim.tilePixels - FloaterArt.cellHeight) / 2, FloaterArt.cellWidth, FloaterArt.cellHeight), + Nullable(source), + Color.White) + + /// 近景楼层层错落:屏幕空间前景屋顶带,所有阴影统一投向右下(SceneDetail.shadowOffsetPx)。 + let private drawForeground (spriteBatch: SpriteBatch) (textures: VillageArt.ArtTextures) (viewportWidth: int) (viewportHeight: int) (tick: int64) = + let ridgeSource = VillageArt.atlasTileRectangle 8 + let eaveSource = VillageArt.atlasTileRectangle 9 + let shadowColor = VillageArt.premultiply 8 12 18 120 + let dx, dy = SceneDetail.shadowOffsetPx + for (x, y, w) in SceneDetail.foregroundRooftops viewportWidth viewportHeight tick do + // 统一方向阴影:先把 eave 的暗色镜像平移一份到右下,再画本体。 + spriteBatch.Draw(textures.TileAtlas, Rectangle(x + dx, y + dy, w, Sim.tilePixels), Nullable(eaveSource), shadowColor) + spriteBatch.Draw(textures.TileAtlas, Rectangle(x, y - Sim.tilePixels, w, Sim.tilePixels), Nullable(ridgeSource), Color.White) + spriteBatch.Draw(textures.TileAtlas, Rectangle(x, y, w, Sim.tilePixels), Nullable(eaveSource), Color.White) + + /// 一次性绘制全部 P47 场景细节(倒影 → 岸边芦苇 → 漂浮元素 → 近景前景层)。 + let draw + (spriteBatch: SpriteBatch) + (textures: VillageArt.ArtTextures) + (pixel: Texture2D) + (camera: Vector2) + (viewportWidth: int) + (viewportHeight: int) + (map: MapGen.Result) + (tick: int64) = + drawReflections spriteBatch textures camera map tick + drawBankReeds spriteBatch textures camera map + drawFloaters spriteBatch textures camera map tick + drawForeground spriteBatch textures viewportWidth viewportHeight tick diff --git a/src/LivingVillage.Desktop/VillageArt.fs b/src/LivingVillage.Desktop/VillageArt.fs index 98e4cdf..3dbb8d4 100644 --- a/src/LivingVillage.Desktop/VillageArt.fs +++ b/src/LivingVillage.Desktop/VillageArt.fs @@ -123,6 +123,7 @@ module VillageArt = { TileAtlas: Texture2D CharacterAtlas: Texture2D InteriorAtlas: Texture2D + FloaterAtlas: Texture2D Cc0TileAtlas: Texture2D option } type private TileSprite = @@ -768,6 +769,8 @@ module VillageArt = // 不再采样任何外部/占位角色素材;尺寸与既有 characterSourceRectangle 完全一致。 CharacterAtlas = CharacterArt.buildAtlas device InteriorAtlas = loadTexture device interiorAtlasRelativePath interiorAtlasWidth interiorAtlasHeight + // P47:漂浮小元素(鸭/叶)改用原创点阵像素代码内嵌生成的图集(2 kind x 2 frame)。 + FloaterAtlas = FloaterArt.buildAtlas device Cc0TileAtlas = cc0 } let atlasTileRectangle (index: int) : Microsoft.Xna.Framework.Rectangle = |
