1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-06 08:22:56 +08:00

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); 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": case "L":
{ {
var startTime = Parsing.ParseDouble(split[1]); var startTime = Parsing.ParseDouble(split[1]);
var loopCount = Parsing.ParseInt(split[2]); var repeatCount = Parsing.ParseInt(split[2]);
timelineGroup = storyboardSprite?.AddLoop(startTime, loopCount); timelineGroup = storyboardSprite?.AddLoop(startTime, Math.Max(0, repeatCount - 1));
break; break;
} }

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace osu.Game.Storyboards namespace osu.Game.Storyboards
@ -8,20 +9,31 @@ namespace osu.Game.Storyboards
public class CommandLoop : CommandTimelineGroup public class CommandLoop : CommandTimelineGroup
{ {
public double LoopStartTime; 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 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; LoopStartTime = startTime;
LoopCount = loopCount; TotalIterations = repeatCount + 1;
} }
public override IEnumerable<CommandTimeline<T>.TypedCommand> GetCommands<T>(CommandTimelineSelector<T> timelineSelector, double offset = 0) 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; var loopOffset = LoopStartTime + loop * CommandsDuration;
foreach (var command in base.GetCommands(timelineSelector, offset + loopOffset)) foreach (var command in base.GetCommands(timelineSelector, offset + loopOffset))
@ -30,6 +42,6 @@ namespace osu.Game.Storyboards
} }
public override string ToString() 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. // 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. // 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using JetBrains.Annotations; using JetBrains.Annotations;
using osu.Framework.Graphics;
using osu.Game.Storyboards.Drawables;
using osuTK;
namespace osu.Game.Storyboards namespace osu.Game.Storyboards
{ {
@ -78,9 +78,9 @@ namespace osu.Game.Storyboards
InitialPosition = initialPosition; 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); loops.Add(loop);
return loop; return loop;
} }