1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-15 03:22:55 +08:00

Merge pull request from peppy/fix-storyboard-alpha-start-time-woes

Fix storyboard commands occurring before the earliest point of visibility delaying gameplay
This commit is contained in:
Dan Balasescu 2021-03-12 00:07:23 +09:00 committed by GitHub
commit d8db29de35
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 97 additions and 7 deletions
osu.Game.Tests/Visual/Gameplay
osu.Game/Storyboards

View File

@ -46,11 +46,12 @@ namespace osu.Game.Tests.Visual.Gameplay
[TestCase(0, 0)] [TestCase(0, 0)]
[TestCase(-1000, -1000)] [TestCase(-1000, -1000)]
[TestCase(-10000, -10000)] [TestCase(-10000, -10000)]
public void TestStoryboardProducesCorrectStartTime(double firstStoryboardEvent, double expectedStartTime) public void TestStoryboardProducesCorrectStartTimeSimpleAlpha(double firstStoryboardEvent, double expectedStartTime)
{ {
var storyboard = new Storyboard(); var storyboard = new Storyboard();
var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero); var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero);
sprite.TimelineGroup.Alpha.Add(Easing.None, firstStoryboardEvent, firstStoryboardEvent + 500, 0, 1); sprite.TimelineGroup.Alpha.Add(Easing.None, firstStoryboardEvent, firstStoryboardEvent + 500, 0, 1);
storyboard.GetLayer("Background").Add(sprite); storyboard.GetLayer("Background").Add(sprite);
@ -64,6 +65,43 @@ namespace osu.Game.Tests.Visual.Gameplay
}); });
} }
[TestCase(1000, 0, false)]
[TestCase(0, 0, false)]
[TestCase(-1000, -1000, false)]
[TestCase(-10000, -10000, false)]
[TestCase(1000, 0, true)]
[TestCase(0, 0, true)]
[TestCase(-1000, -1000, true)]
[TestCase(-10000, -10000, true)]
public void TestStoryboardProducesCorrectStartTimeFadeInAfterOtherEvents(double firstStoryboardEvent, double expectedStartTime, bool addEventToLoop)
{
var storyboard = new Storyboard();
var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero);
// these should be ignored as we have an alpha visibility blocker proceeding this command.
sprite.TimelineGroup.Scale.Add(Easing.None, -20000, -18000, 0, 1);
var loopGroup = sprite.AddLoop(-20000, 50);
loopGroup.Scale.Add(Easing.None, -20000, -18000, 0, 1);
var target = addEventToLoop ? loopGroup : sprite.TimelineGroup;
target.Alpha.Add(Easing.None, firstStoryboardEvent, firstStoryboardEvent + 500, 0, 1);
// these should be ignored due to being in the future.
sprite.TimelineGroup.Alpha.Add(Easing.None, 18000, 20000, 0, 1);
loopGroup.Alpha.Add(Easing.None, 18000, 20000, 0, 1);
storyboard.GetLayer("Background").Add(sprite);
loadPlayerWithBeatmap(new TestBeatmap(new OsuRuleset().RulesetInfo), storyboard);
AddAssert($"first frame is {expectedStartTime}", () =>
{
Debug.Assert(player.FirstFrameClockTime != null);
return Precision.AlmostEquals(player.FirstFrameClockTime.Value, expectedStartTime, lenience_ms);
});
}
private void loadPlayerWithBeatmap(IBeatmap beatmap, Storyboard storyboard = null) private void loadPlayerWithBeatmap(IBeatmap beatmap, Storyboard storyboard = null)
{ {
AddStep("create player", () => AddStep("create player", () =>

View File

@ -45,11 +45,30 @@ namespace osu.Game.Storyboards
}; };
} }
/// <summary>
/// Returns the earliest visible time. Will be null unless this group's first <see cref="Alpha"/> command has a start value of zero.
/// </summary>
public double? EarliestDisplayedTime
{
get
{
var first = Alpha.Commands.FirstOrDefault();
return first?.StartValue == 0 ? first.StartTime : (double?)null;
}
}
[JsonIgnore] [JsonIgnore]
public double CommandsStartTime public double CommandsStartTime
{ {
get get
{ {
// if the first alpha command starts at zero it should be given priority over anything else.
// this is due to it creating a state where the target is not present before that time, causing any other events to not be visible.
var earliestDisplay = EarliestDisplayedTime;
if (earliestDisplay != null)
return earliestDisplay.Value;
double min = double.MaxValue; double min = double.MaxValue;
for (int i = 0; i < timelines.Length; i++) for (int i = 0; i < timelines.Length; i++)

View File

@ -24,13 +24,46 @@ namespace osu.Game.Storyboards
public readonly CommandTimelineGroup TimelineGroup = new CommandTimelineGroup(); public readonly CommandTimelineGroup TimelineGroup = new CommandTimelineGroup();
public double StartTime => Math.Min( public double StartTime
TimelineGroup.HasCommands ? TimelineGroup.CommandsStartTime : double.MaxValue, {
loops.Any(l => l.HasCommands) ? loops.Where(l => l.HasCommands).Min(l => l.StartTime) : double.MaxValue); get
{
// check for presence affecting commands as an initial pass.
double earliestStartTime = TimelineGroup.EarliestDisplayedTime ?? double.MaxValue;
public double EndTime => Math.Max( foreach (var l in loops)
TimelineGroup.HasCommands ? TimelineGroup.CommandsEndTime : double.MinValue, {
loops.Any(l => l.HasCommands) ? loops.Where(l => l.HasCommands).Max(l => l.EndTime) : double.MinValue); if (!(l.EarliestDisplayedTime is double lEarliest))
continue;
earliestStartTime = Math.Min(earliestStartTime, lEarliest);
}
if (earliestStartTime < double.MaxValue)
return earliestStartTime;
// if an alpha-affecting command was not found, use the earliest of any command.
earliestStartTime = TimelineGroup.StartTime;
foreach (var l in loops)
earliestStartTime = Math.Min(earliestStartTime, l.StartTime);
return earliestStartTime;
}
}
public double EndTime
{
get
{
double latestEndTime = TimelineGroup.EndTime;
foreach (var l in loops)
latestEndTime = Math.Max(latestEndTime, l.EndTime);
return latestEndTime;
}
}
public bool HasCommands => TimelineGroup.HasCommands || loops.Any(l => l.HasCommands); public bool HasCommands => TimelineGroup.HasCommands || loops.Any(l => l.HasCommands);