Merge pull request #14885 from Wieku/storyboard-fixes

Force storyboard's loop commands to be run at least once
This commit is contained in:
Dean Herbert 2021-10-03 05:19:12 +09:00 committed by GitHub
commit df50695021
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 67 additions and 13 deletions

View File

@ -149,5 +149,32 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual(AnimationLoopType.LoopForever, ((StoryboardAnimation)foreground.Elements[5]).LoopType);
}
}
[Test]
public void TestDecodeLoopCount()
{
// all loop sequences in loop-count.osb have a total duration of 2000ms (fade in 0->1000ms, fade out 1000->2000ms).
const double loop_duration = 2000;
var decoder = new LegacyStoryboardDecoder();
using (var resStream = TestResources.OpenResource("loop-count.osb"))
using (var stream = new LineBufferedReader(resStream))
{
var storyboard = decoder.Decode(stream);
StoryboardLayer background = storyboard.Layers.Single(l => l.Depth == 3);
// stable ensures that any loop command executes at least once, even if the loop count specified in the .osb is zero or negative.
StoryboardSprite zeroTimes = background.Elements.OfType<StoryboardSprite>().Single(s => s.Path == "zero-times.png");
Assert.That(zeroTimes.EndTime, Is.EqualTo(1000 + loop_duration));
StoryboardSprite oneTime = background.Elements.OfType<StoryboardSprite>().Single(s => s.Path == "one-time.png");
Assert.That(oneTime.EndTime, Is.EqualTo(4000 + loop_duration));
StoryboardSprite manyTimes = background.Elements.OfType<StoryboardSprite>().Single(s => s.Path == "many-times.png");
Assert.That(manyTimes.EndTime, Is.EqualTo(9000 + 40 * loop_duration));
}
}
}
}

View File

@ -0,0 +1,15 @@
osu file format v14
[Events]
Sprite,Background,TopCentre,"zero-times.png",320,240
L,1000,0
F,0,0,1000,0,1
F,0,1000,2000,1,0
Sprite,Background,TopCentre,"one-time.png",320,240
L,4000,1
F,0,0,1000,0,1
F,0,1000,2000,1,0
Sprite,Background,TopCentre,"many-times.png",320,240
L,9000,40
F,0,0,1000,0,1
F,0,1000,2000,1,0

View File

@ -176,8 +176,8 @@ namespace osu.Game.Beatmaps.Formats
case "L":
{
var startTime = Parsing.ParseDouble(split[1]);
var loopCount = Parsing.ParseInt(split[2]);
timelineGroup = storyboardSprite?.AddLoop(startTime, loopCount);
var repeatCount = Parsing.ParseInt(split[2]);
timelineGroup = storyboardSprite?.AddLoop(startTime, Math.Max(0, repeatCount - 1));
break;
}

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
namespace osu.Game.Storyboards
@ -8,20 +9,31 @@ namespace osu.Game.Storyboards
public class CommandLoop : CommandTimelineGroup
{
public double LoopStartTime;
public int LoopCount;
/// <summary>
/// The total number of times this loop is played back. Always greater than zero.
/// </summary>
public readonly int TotalIterations;
public override double StartTime => LoopStartTime + CommandsStartTime;
public override double EndTime => StartTime + CommandsDuration * LoopCount;
public override double EndTime => StartTime + CommandsDuration * TotalIterations;
public CommandLoop(double startTime, int loopCount)
/// <summary>
/// Construct a new command loop.
/// </summary>
/// <param name="startTime">The start time of the loop.</param>
/// <param name="repeatCount">The number of times the loop should repeat. Should be greater than zero. Zero means a single playback.</param>
public CommandLoop(double startTime, int repeatCount)
{
if (repeatCount < 0) throw new ArgumentException("Repeat count must be zero or above.", nameof(repeatCount));
LoopStartTime = startTime;
LoopCount = loopCount;
TotalIterations = repeatCount + 1;
}
public override IEnumerable<CommandTimeline<T>.TypedCommand> GetCommands<T>(CommandTimelineSelector<T> timelineSelector, double offset = 0)
{
for (var loop = 0; loop < LoopCount; loop++)
for (var loop = 0; loop < TotalIterations; loop++)
{
var loopOffset = LoopStartTime + loop * CommandsDuration;
foreach (var command in base.GetCommands(timelineSelector, offset + loopOffset))
@ -30,6 +42,6 @@ namespace osu.Game.Storyboards
}
public override string ToString()
=> $"{LoopStartTime} x{LoopCount}";
=> $"{LoopStartTime} x{TotalIterations}";
}
}

View File

@ -1,13 +1,13 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osuTK;
using osu.Framework.Graphics;
using osu.Game.Storyboards.Drawables;
using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using osu.Framework.Graphics;
using osu.Game.Storyboards.Drawables;
using osuTK;
namespace osu.Game.Storyboards
{
@ -78,9 +78,9 @@ namespace osu.Game.Storyboards
InitialPosition = initialPosition;
}
public CommandLoop AddLoop(double startTime, int loopCount)
public CommandLoop AddLoop(double startTime, int repeatCount)
{
var loop = new CommandLoop(startTime, loopCount);
var loop = new CommandLoop(startTime, repeatCount);
loops.Add(loop);
return loop;
}