From 0b9753543bd726818c61d76384d56a30b862e1ac Mon Sep 17 00:00:00 2001 From: "Somhairle H. Marisol" Date: Mon, 21 Sep 2026 13:15:39 +0800 Subject: feat(desktop): 村民与主角四帧行走动画 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 人物图集 5 变体 x 4 向 x 4 帧(2560x48,全部经 make-jiangnan-art.py 原创,四种腿步:站立/左跨/站立微跳/右跨镜像);animationFrame 按 tick/4 mod 4 确定性取帧(16 tick 一循环,禁实时时钟);快照式回归断言四帧循环与逐帧图集列映射,主角同规则。 --- src/LivingVillage.Desktop.Tests/PrototypeTests.fs | 29 ++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) (limited to 'src/LivingVillage.Desktop.Tests') diff --git a/src/LivingVillage.Desktop.Tests/PrototypeTests.fs b/src/LivingVillage.Desktop.Tests/PrototypeTests.fs index d77abd6..450e94b 100644 --- a/src/LivingVillage.Desktop.Tests/PrototypeTests.fs +++ b/src/LivingVillage.Desktop.Tests/PrototypeTests.fs @@ -324,7 +324,7 @@ type PrototypeTests () = Assert.IsTrue(first.PanelY + first.PanelHeight <= 720) [] - member _.NpcVariantsAreStableAndAnimationCoversFourDirectionsAndTwoFrames () = + member _.NpcVariantsAreStableAndAnimationCoversFourDirectionsAndFourFrames () = let sameNpc = npcVisualVariant (NpcId 7) let repeatedNpc = npcVisualVariant (NpcId 7) let otherNpc = npcVisualVariant (NpcId 8) @@ -341,7 +341,9 @@ type PrototypeTests () = Assert.IsTrue(sprites |> List.forall (fun sprite -> sprite.Frame = FrameTwo)) Assert.AreEqual(FrameOne, (VillageArt.animationFrame 0L)) Assert.AreEqual(FrameTwo, (VillageArt.animationFrame 4L)) - Assert.AreEqual(FrameOne, (VillageArt.animationFrame 8L)) + Assert.AreEqual(FrameThree, (VillageArt.animationFrame 8L)) + Assert.AreEqual(FrameFour, (VillageArt.animationFrame 12L)) + Assert.AreEqual(FrameOne, (VillageArt.animationFrame 16L)) [] member _.VillagersUseFourDistinctVariantsDistinctFromTheAvatar () = @@ -359,7 +361,28 @@ type PrototypeTests () = rect.X) |> Set.ofList Assert.AreEqual(4, variantXes.Count) - Assert.AreEqual(1280, VillageArt.characterAtlasWidth) + Assert.AreEqual(2560, VillageArt.characterAtlasWidth) + + [] + member _.WalkAnimationCoversFourFramesDeterministically () = + // 16-tick cycle, new frame every 4 sim ticks, no wall-clock dependency. + let frames = [ 0L; 4L; 8L; 12L; 16L; 20L ] |> List.map VillageArt.animationFrame + Assert.AreEqual([ FrameOne; FrameTwo; FrameThree; FrameFour; FrameOne; FrameTwo ], frames) + + let cycleFrames = + [ 0L; 4L; 8L; 12L ] + |> List.map VillageArt.animationFrame + |> List.distinct + Assert.AreEqual([ FrameOne; FrameTwo; FrameThree; FrameFour ], cycleFrames) + + // Every frame maps to a distinct atlas cell column across the five variants. + // Every frame maps to a distinct atlas cell column across the five variants. + let perFrameX = + [ FrameOne; FrameTwo; FrameThree; FrameFour ] + |> List.map (fun frame -> + let rect = characterSourceRectangle { Variant = IndigoRobe; Direction = NorthFacing; Frame = frame } + rect.X / 32) + Assert.AreEqual([ 0; 1; 2; 3 ], perFrameX) [] member _.ChineseCopyProvidesFormalSceneTimeMenuAndStatusLabels () = -- cgit v1.2.3