summaryrefslogtreecommitdiff
path: root/src/LivingVillage.Desktop.Tests/P45CharacterAnimTests.fs
diff options
context:
space:
mode:
Diffstat (limited to 'src/LivingVillage.Desktop.Tests/P45CharacterAnimTests.fs')
-rw-r--r--src/LivingVillage.Desktop.Tests/P45CharacterAnimTests.fs112
1 files changed, 112 insertions, 0 deletions
diff --git a/src/LivingVillage.Desktop.Tests/P45CharacterAnimTests.fs b/src/LivingVillage.Desktop.Tests/P45CharacterAnimTests.fs
new file mode 100644
index 0000000..eba03aa
--- /dev/null
+++ b/src/LivingVillage.Desktop.Tests/P45CharacterAnimTests.fs
@@ -0,0 +1,112 @@
+namespace LivingVillage.Desktop.Tests
+
+open Microsoft.VisualStudio.TestTools.UnitTesting
+open LivingVillage.Kernel
+open LivingVillage.Kernel.Sim
+open LivingVillage.Desktop
+
+/// P45 角色动画帧升级回归:四向 x 6 帧原创点阵、帧序列确定性、行走推进、停止回 idle。
+[<TestClass>]
+type P45CharacterAnimTests () =
+
+ let assertEqualMsg (expected: 'a) (actual: 'a) (message: string) =
+ Assert.IsTrue((expected = actual), sprintf "%s (expected %A, actual %A)" message expected actual)
+
+ [<TestMethod>]
+ member _.EveryCharacterFrameIsAWellFormedDotMatrix () =
+ for variant in 0 .. CharacterArt.variantCount - 1 do
+ for direction in 0 .. CharacterArt.directionCount - 1 do
+ for frame in 0 .. CharacterArt.frameCount - 1 do
+ let rows = CharacterArt.frameLetters variant direction frame
+ assertEqualMsg CharacterArt.sourceHeight rows.Length (sprintf "v%d d%d f%d 行数" variant direction frame)
+ for row in rows do
+ assertEqualMsg CharacterArt.sourceWidth row.Length (sprintf "v%d d%d f%d 行宽" variant direction frame)
+ Assert.IsTrue(rows |> List.exists (fun r -> r.Contains "S"), "角色须有皮肤像素")
+
+ [<TestMethod>]
+ member _.WalkFramesAndIdleFramesDifferVisibly () =
+ let cellDiff (a: string list) (b: string list) : int =
+ List.zip a b
+ |> List.sumBy (fun (ra, rb) -> (Seq.zip ra rb |> Seq.filter (fun (x, y) -> x <> y) |> Seq.length))
+ for variant in 0 .. CharacterArt.variantCount - 1 do
+ for direction in 0 .. CharacterArt.directionCount - 1 do
+ let frames = [ 0 .. CharacterArt.frameCount - 1 ] |> List.map (CharacterArt.frameLetters variant direction)
+ // 四帧行走两两相邻必须有可见差异。
+ for i in 0 .. CharacterArt.walkFrameCount - 2 do
+ Assert.IsTrue(cellDiff frames.[i] frames.[i + 1] >= 4, sprintf "走帧 %d->%d 差异不足" i (i + 1))
+ // 待机两帧(呼吸)必须不同。
+ let idle1 = CharacterArt.frameLetters variant direction (CharacterArt.walkFrameCount)
+ let idle2 = CharacterArt.frameLetters variant direction (CharacterArt.walkFrameCount + 1)
+ Assert.IsTrue(cellDiff idle1 idle2 >= 4, "待机两帧差异不足")
+ // 行走帧与待机帧必须不同。
+ Assert.IsTrue(cellDiff frames.[0] idle1 >= 4, "行走帧与待机帧相同")
+
+ [<TestMethod>]
+ member _.FourDirectionsAreVisuallyDistinct () =
+ for variant in 0 .. CharacterArt.variantCount - 1 do
+ for frame in 0 .. CharacterArt.frameCount - 1 do
+ let heads =
+ [ 0 .. CharacterArt.directionCount - 1 ]
+ |> List.map (fun d -> (CharacterArt.frameLetters variant d frame) |> List.take 10)
+ Assert.IsTrue(heads.[0] <> heads.[1], "North 与 South 头部须不同")
+ Assert.IsTrue(heads.[2] <> heads.[3], "West 与 East 头部须不同")
+ Assert.IsTrue(heads.[0] <> heads.[2], "North 与 West 头部须不同")
+
+ [<TestMethod>]
+ member _.FiveVariantsHaveDistinctPalettes () =
+ let palettes = [ 0 .. CharacterArt.variantCount - 1 ] |> List.map CharacterArt.variantColors
+ assertEqualMsg CharacterArt.variantCount (palettes |> List.distinct |> List.length) "5 个变体配色须互不相同"
+
+ [<TestMethod>]
+ member _.AtlasDimensionsMatchTheSourceRectangleContract () =
+ assertEqualMsg 32 CharacterArt.cellWidth "cell 宽"
+ assertEqualMsg 48 CharacterArt.cellHeight "cell 高"
+ assertEqualMsg VillageArt.characterAtlasWidth CharacterArt.atlasWidth "图集总宽须与既有契约一致"
+
+ [<TestMethod>]
+ member _.DirectionMapsToDistinctDeterministicSourceCells () =
+ let rect variant direction frame =
+ VillageArt.characterSourceRectangle
+ { Variant = variant; Direction = direction; Frame = frame }
+ for direction in [ VillageArt.NorthFacing; VillageArt.SouthFacing; VillageArt.WestFacing; VillageArt.EastFacing ] do
+ let a = rect VillageArt.IndigoRobe direction (VillageArt.WalkFrame VillageArt.FrameOne)
+ let b = rect VillageArt.IndigoRobe direction (VillageArt.WalkFrame VillageArt.FrameOne)
+ assertEqualMsg a b "同一输入的源矩形必须确定"
+ let xs =
+ [ VillageArt.NorthFacing; VillageArt.SouthFacing; VillageArt.WestFacing; VillageArt.EastFacing ]
+ |> List.map (fun d -> (rect VillageArt.IndigoRobe d (VillageArt.WalkFrame VillageArt.FrameOne)).X)
+ assertEqualMsg 4 (xs |> List.distinct |> List.length) "四向必须落在不同源格"
+ // 同向 6 帧源格逐一不同且按序排列。
+ let frameXs =
+ [ VillageArt.WalkFrame VillageArt.FrameOne
+ VillageArt.WalkFrame VillageArt.FrameTwo
+ VillageArt.WalkFrame VillageArt.FrameThree
+ VillageArt.WalkFrame VillageArt.FrameFour
+ VillageArt.IdleFrame VillageArt.IdleOne
+ VillageArt.IdleFrame VillageArt.IdleTwo ]
+ |> List.map (fun f -> (rect VillageArt.IndigoRobe VillageArt.SouthFacing f).X)
+ assertEqualMsg 6 (frameXs |> List.distinct |> List.length) "同向 6 帧源格须互不同"
+ assertEqualMsg frameXs (List.sort frameXs) "帧源格须按帧序递增"
+
+ [<TestMethod>]
+ member _.WalkingAdvancesFramesAndStoppingReturnsToIdle () =
+ // 行走:帧随 tick 按 WalkWindow 推进并循环。
+ assertEqualMsg (VillageArt.WalkFrame VillageArt.FrameOne) (VillageArt.characterFrame true 0L) "走 0"
+ assertEqualMsg (VillageArt.WalkFrame VillageArt.FrameTwo) (VillageArt.characterFrame true 4L) "走 4"
+ assertEqualMsg (VillageArt.WalkFrame VillageArt.FrameThree) (VillageArt.characterFrame true 8L) "走 8"
+ assertEqualMsg (VillageArt.WalkFrame VillageArt.FrameFour) (VillageArt.characterFrame true 12L) "走 12"
+ assertEqualMsg (VillageArt.WalkFrame VillageArt.FrameOne) (VillageArt.characterFrame true 16L) "走 16 循环"
+ // 停止:无论 tick,回 idle 两帧呼吸循环。
+ assertEqualMsg (VillageArt.IdleFrame VillageArt.IdleOne) (VillageArt.characterFrame false 0L) "停 0"
+ assertEqualMsg (VillageArt.IdleFrame VillageArt.IdleTwo) (VillageArt.characterFrame false 24L) "停 24"
+ assertEqualMsg (VillageArt.IdleFrame VillageArt.IdleOne) (VillageArt.characterFrame false 9999L) "停任意 tick 仍在 idle"
+ // 玩家行走规格:方向 + 是否移动 + tick 决定帧,四向共享同一帧序(方向只换源格)。
+ let framesFor direction =
+ [ 0L; 4L; 8L; 12L ] |> List.map (fun t -> (VillageArt.avatarSpriteSpec direction true t).Frame)
+ let north = framesFor VillageArt.NorthFacing
+ assertEqualMsg [ VillageArt.WalkFrame VillageArt.FrameOne
+ VillageArt.WalkFrame VillageArt.FrameTwo
+ VillageArt.WalkFrame VillageArt.FrameThree
+ VillageArt.WalkFrame VillageArt.FrameFour ] north "方向->帧序确定"
+ for direction in [ VillageArt.SouthFacing; VillageArt.WestFacing; VillageArt.EastFacing ] do
+ assertEqualMsg north (framesFor direction) "四向帧序一致"