mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 11:42:56 +08:00
Merge pull request #11990 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:
commit
d8db29de35
@ -46,11 +46,12 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[TestCase(0, 0)]
|
||||
[TestCase(-1000, -1000)]
|
||||
[TestCase(-10000, -10000)]
|
||||
public void TestStoryboardProducesCorrectStartTime(double firstStoryboardEvent, double expectedStartTime)
|
||||
public void TestStoryboardProducesCorrectStartTimeSimpleAlpha(double firstStoryboardEvent, double expectedStartTime)
|
||||
{
|
||||
var storyboard = new Storyboard();
|
||||
|
||||
var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero);
|
||||
|
||||
sprite.TimelineGroup.Alpha.Add(Easing.None, firstStoryboardEvent, firstStoryboardEvent + 500, 0, 1);
|
||||
|
||||
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)
|
||||
{
|
||||
AddStep("create player", () =>
|
||||
|
@ -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]
|
||||
public double CommandsStartTime
|
||||
{
|
||||
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;
|
||||
|
||||
for (int i = 0; i < timelines.Length; i++)
|
||||
|
@ -24,13 +24,46 @@ namespace osu.Game.Storyboards
|
||||
|
||||
public readonly CommandTimelineGroup TimelineGroup = new CommandTimelineGroup();
|
||||
|
||||
public double StartTime => Math.Min(
|
||||
TimelineGroup.HasCommands ? TimelineGroup.CommandsStartTime : double.MaxValue,
|
||||
loops.Any(l => l.HasCommands) ? loops.Where(l => l.HasCommands).Min(l => l.StartTime) : double.MaxValue);
|
||||
public double StartTime
|
||||
{
|
||||
get
|
||||
{
|
||||
// check for presence affecting commands as an initial pass.
|
||||
double earliestStartTime = TimelineGroup.EarliestDisplayedTime ?? double.MaxValue;
|
||||
|
||||
public double EndTime => Math.Max(
|
||||
TimelineGroup.HasCommands ? TimelineGroup.CommandsEndTime : double.MinValue,
|
||||
loops.Any(l => l.HasCommands) ? loops.Where(l => l.HasCommands).Max(l => l.EndTime) : double.MinValue);
|
||||
foreach (var l in loops)
|
||||
{
|
||||
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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user