A game framework written with osu! in mind.

Fix seeking and pausing support (with tests)

+65 -28
+36 -16
osu.Framework.Tests/Visual/Sprites/TestSceneAnimation.cs
··· 18 18 { 19 19 public class TestSceneAnimation : FrameworkTestScene 20 20 { 21 - private readonly Container animationContainer; 22 - private readonly SpriteText timeText; 21 + private SpriteText timeText; 23 22 24 23 public override IReadOnlyList<Type> RequiredTypes => new[] 25 24 { ··· 31 30 32 31 private TestAnimation animation; 33 32 34 - public TestSceneAnimation() 35 - { 36 - Children = new Drawable[] 37 - { 38 - animationContainer = new Container { RelativeSizeAxes = Axes.Both }, 39 - timeText = new SpriteText { Text = "Animation is loading..." } 40 - }; 41 - } 42 - 43 33 [SetUpSteps] 44 34 public void SetUpSteps() 45 35 { 46 36 AddStep("load video", () => 47 37 { 48 - animationContainer.Child = animation = new TestAnimation 38 + Children = new Drawable[] 49 39 { 50 - Repeat = false, 51 - Clock = new FramedClock(clock = new ManualClock()), 40 + new Container 41 + { 42 + RelativeSizeAxes = Axes.Both, 43 + Clock = new FramedClock(clock = new ManualClock()), 44 + Child = animation = new TestAnimation 45 + { 46 + Repeat = false, 47 + } 48 + }, 49 + timeText = new SpriteText { Text = "Animation is loading..." } 52 50 }; 53 51 }); 54 52 ··· 57 55 } 58 56 59 57 [Test] 58 + public void TestFrameSeeking() 59 + { 60 + AddAssert("frame count is correct", () => animation.FrameCount == TestAnimation.LOADABLE_FRAMES); 61 + AddUntilStep("wait for frames to pass", () => animation.CurrentFrameIndex > 10); 62 + AddStep("stop animation", () => animation.Stop()); 63 + AddAssert("is stopped", () => !animation.IsPlaying); 64 + 65 + AddStep("goto frame 60", () => animation.GotoFrame(60)); 66 + AddAssert("is at frame 60", () => animation.CurrentFrameIndex == 60); 67 + 68 + AddStep("goto frame 30", () => animation.GotoFrame(30)); 69 + AddAssert("is at frame 30", () => animation.CurrentFrameIndex == 30); 70 + 71 + AddStep("goto frame 60", () => animation.GotoFrame(60)); 72 + AddAssert("is at frame 60", () => animation.CurrentFrameIndex == 60); 73 + 74 + AddStep("start animation", () => animation.Play()); 75 + AddUntilStep("continues to frame 70", () => animation.CurrentFrameIndex == 70); 76 + } 77 + 78 + [Test] 60 79 public void TestJumpForward() 61 80 { 62 81 AddStep("Jump ahead by 10 seconds", () => clock.CurrentTime += 10000); ··· 109 128 110 129 private class TestAnimation : TextureAnimation 111 130 { 131 + public const int LOADABLE_FRAMES = 72; 132 + 112 133 [Resolved] 113 134 private FontStore fontStore { get; set; } 114 135 115 136 public int FramesProcessed; 116 137 117 138 public TestAnimation() 118 - : base(false) 119 139 { 120 140 Anchor = Anchor.Centre; 121 141 Origin = Anchor.Centre; ··· 130 150 [BackgroundDependencyLoader] 131 151 private void load() 132 152 { 133 - for (int i = 0; i <= 72; i++) 153 + for (int i = 0; i < LOADABLE_FRAMES; i++) 134 154 { 135 155 AddFrame(new Texture(fontStore.Get(null, (char)('0' + i)).Texture.TextureGL) 136 156 {
+23 -11
osu.Framework/Graphics/Animations/Animation.cs
··· 144 144 if (!startAtCurrentTime) 145 145 throw new InvalidOperationException($"A {nameof(Animation<T>)} with {startAtCurrentTime} = false cannot seek as it is dependent on an external clock."); 146 146 147 - offsetClock.Offset = offsetClock.CurrentTime + frameData[frameIndex].DisplayTime; 147 + offsetClock.Offset = frameData[frameIndex].DisplayStartTime - offsetClock.Source.CurrentTime; 148 148 currentFrameCache.Invalidate(); 149 149 } 150 150 ··· 166 166 { 167 167 var lastFrame = frameData.LastOrDefault(); 168 168 169 - frame.DisplayTime = lastFrame.DisplayTime + lastFrame.Duration; 169 + frame.DisplayStartTime = lastFrame.DisplayEndTime; 170 170 Duration += frame.Duration; 171 171 172 172 frameData.Add(frame); ··· 221 221 { 222 222 base.Update(); 223 223 224 - if (!IsPlaying || frameData.Count <= 0) return; 224 + if (!IsPlaying) 225 + offsetClock.Offset -= Time.Elapsed; 225 226 226 - while (CurrentFrameIndex < frameData.Count && PlaybackPosition > frameData[CurrentFrameIndex].DisplayTime + frameData[CurrentFrameIndex].Duration) 227 - { 228 - CurrentFrameIndex++; 229 - currentFrameCache.Invalidate(); 230 - } 227 + if (frameData.Count == 0) return; 231 228 232 - while (CurrentFrameIndex > 0 && PlaybackPosition < frameData[CurrentFrameIndex].DisplayTime) 229 + switch (PlaybackPosition.CompareTo(frameData[CurrentFrameIndex].DisplayStartTime)) 233 230 { 234 - CurrentFrameIndex--; 235 - currentFrameCache.Invalidate(); 231 + case -1: 232 + while (CurrentFrameIndex > 0 && PlaybackPosition < frameData[CurrentFrameIndex].DisplayStartTime) 233 + { 234 + CurrentFrameIndex--; 235 + currentFrameCache.Invalidate(); 236 + } 237 + 238 + break; 239 + 240 + case 1: 241 + while (CurrentFrameIndex < frameData.Count - 1 && PlaybackPosition >= frameData[CurrentFrameIndex].DisplayEndTime) 242 + { 243 + CurrentFrameIndex++; 244 + currentFrameCache.Invalidate(); 245 + } 246 + 247 + break; 236 248 } 237 249 238 250 if (!currentFrameCache.IsValid)
+6 -1
osu.Framework/Graphics/Animations/FrameData.cs
··· 22 22 /// <summary> 23 23 /// The time at which this frame is displayed in the containing animation. 24 24 /// </summary> 25 - internal double DisplayTime { get; set; } 25 + internal double DisplayStartTime { get; set; } 26 + 27 + /// <summary> 28 + /// The time at which this frame is no longer displayed. 29 + /// </summary> 30 + internal double DisplayEndTime => DisplayStartTime + Duration; 26 31 } 27 32 }