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

Merge branch 'master' into use-bindable-break-time-in-player

This commit is contained in:
Salman Ahmed 2019-08-08 05:36:45 +03:00 committed by GitHub
commit 3f9f9e7bc9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 212 additions and 66 deletions

View File

@ -1,8 +1,11 @@
// 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;
using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Timing;
using osu.Game.Beatmaps.Timing; using osu.Game.Beatmaps.Timing;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
@ -11,78 +14,172 @@ namespace osu.Game.Tests.Visual.Gameplay
[TestFixture] [TestFixture]
public class TestSceneBreakOverlay : OsuTestScene public class TestSceneBreakOverlay : OsuTestScene
{ {
private readonly BreakOverlay breakOverlay; public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(BreakOverlay),
};
private readonly TestBreakOverlay breakOverlay;
private readonly IReadOnlyList<BreakPeriod> testBreaks = new List<BreakPeriod>
{
new BreakPeriod
{
StartTime = 1000,
EndTime = 5000,
},
new BreakPeriod
{
StartTime = 6000,
EndTime = 13500,
},
};
public TestSceneBreakOverlay() public TestSceneBreakOverlay()
{ {
Child = breakOverlay = new BreakOverlay(true); Add(breakOverlay = new TestBreakOverlay(true));
AddStep("2s break", () => startBreak(2000));
AddStep("5s break", () => startBreak(5000));
AddStep("10s break", () => startBreak(10000));
AddStep("15s break", () => startBreak(15000));
AddStep("2s, 2s", startMultipleBreaks);
AddStep("0.5s, 0.7s, 1s, 2s", startAnotherMultipleBreaks);
} }
private void startBreak(double duration) [Test]
public void TestShowBreaks()
{ {
breakOverlay.Breaks = new List<BreakPeriod> setClock(false);
addShowBreakStep(2);
addShowBreakStep(5);
addShowBreakStep(15);
}
[Test]
public void TestNoEffectsBreak()
{
var shortBreak = new BreakPeriod { EndTime = 500 };
setClock(true);
loadBreaksStep("short break", new[] { shortBreak });
addBreakSeeks(shortBreak, false);
}
[Test]
public void TestMultipleBreaks()
{
setClock(true);
loadBreaksStep("multiple breaks", testBreaks);
foreach (var b in testBreaks)
addBreakSeeks(b, false);
}
[Test]
public void TestRewindBreaks()
{
setClock(true);
loadBreaksStep("multiple breaks", testBreaks);
foreach (var b in testBreaks.Reverse())
addBreakSeeks(b, true);
}
[Test]
public void TestSkipBreaks()
{
setClock(true);
loadBreaksStep("multiple breaks", testBreaks);
seekAndAssertBreak("seek to break start", testBreaks[1].StartTime, true);
AddAssert("is skipped to break #2", () => breakOverlay.CurrentBreakIndex == 1);
seekAndAssertBreak("seek to break middle", testBreaks[1].StartTime + testBreaks[1].Duration / 2, true);
seekAndAssertBreak("seek to break end", testBreaks[1].EndTime, false);
seekAndAssertBreak("seek to break after end", testBreaks[1].EndTime + 500, false);
}
private void addShowBreakStep(double seconds)
{
AddStep($"show '{seconds}s' break", () => breakOverlay.Breaks = new List<BreakPeriod>
{ {
new BreakPeriod new BreakPeriod
{ {
StartTime = Clock.CurrentTime, StartTime = Clock.CurrentTime,
EndTime = Clock.CurrentTime + duration, EndTime = Clock.CurrentTime + seconds * 1000,
} }
}; });
} }
private void startMultipleBreaks() private void setClock(bool useManual)
{ {
double currentTime = Clock.CurrentTime; AddStep($"set {(useManual ? "manual" : "realtime")} clock", () => breakOverlay.SwitchClock(useManual));
breakOverlay.Breaks = new List<BreakPeriod>
{
new BreakPeriod
{
StartTime = currentTime,
EndTime = currentTime + 2000,
},
new BreakPeriod
{
StartTime = currentTime + 4000,
EndTime = currentTime + 6000,
}
};
} }
private void startAnotherMultipleBreaks() private void loadBreaksStep(string breakDescription, IReadOnlyList<BreakPeriod> breaks)
{ {
double currentTime = Clock.CurrentTime; AddStep($"load {breakDescription}", () => breakOverlay.Breaks = breaks);
seekAndAssertBreak("seek back to 0", 0, false);
}
breakOverlay.Breaks = new List<BreakPeriod> private void addBreakSeeks(BreakPeriod b, bool isReversed)
{
if (isReversed)
{ {
new BreakPeriod // Duration is less than 650 - too short to appear seekAndAssertBreak("seek to break after end", b.EndTime + 500, false);
{ seekAndAssertBreak("seek to break end", b.EndTime, false);
StartTime = currentTime, seekAndAssertBreak("seek to break middle", b.StartTime + b.Duration / 2, b.HasEffect);
EndTime = currentTime + 500, seekAndAssertBreak("seek to break start", b.StartTime, b.HasEffect);
}, }
new BreakPeriod else
{ {
StartTime = currentTime + 1500, seekAndAssertBreak("seek to break start", b.StartTime, b.HasEffect);
EndTime = currentTime + 2200, seekAndAssertBreak("seek to break middle", b.StartTime + b.Duration / 2, b.HasEffect);
}, seekAndAssertBreak("seek to break end", b.EndTime, false);
new BreakPeriod seekAndAssertBreak("seek to break after end", b.EndTime + 500, false);
{ }
StartTime = currentTime + 3200, }
EndTime = currentTime + 4200,
}, private void seekAndAssertBreak(string seekStepDescription, double time, bool shouldBeBreak)
new BreakPeriod {
{ AddStep(seekStepDescription, () => breakOverlay.ManualClockTime = time);
StartTime = currentTime + 5200, AddAssert($"is{(!shouldBeBreak ? " not" : string.Empty)} break time", () =>
EndTime = currentTime + 7200, {
} breakOverlay.ProgressTime();
}; return breakOverlay.IsBreakTime.Value == shouldBeBreak;
});
}
private class TestBreakOverlay : BreakOverlay
{
private readonly FramedClock framedManualClock;
private readonly ManualClock manualClock;
private IFrameBasedClock originalClock;
public new int CurrentBreakIndex => base.CurrentBreakIndex;
public double ManualClockTime
{
get => manualClock.CurrentTime;
set => manualClock.CurrentTime = value;
}
public TestBreakOverlay(bool letterboxing)
: base(letterboxing)
{
framedManualClock = new FramedClock(manualClock = new ManualClock());
ProcessCustomClock = false;
}
public void ProgressTime()
{
framedManualClock.ProcessFrame();
Update();
}
public void SwitchClock(bool setManual) => Clock = setManual ? framedManualClock : originalClock;
protected override void LoadComplete()
{
base.LoadComplete();
originalClock = Clock;
}
} }
} }
} }

View File

@ -1,6 +1,8 @@
// 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 osu.Game.Screens.Play;
namespace osu.Game.Beatmaps.Timing namespace osu.Game.Beatmaps.Timing
{ {
public class BreakPeriod public class BreakPeriod
@ -35,6 +37,6 @@ namespace osu.Game.Beatmaps.Timing
/// </summary> /// </summary>
/// <param name="time">The time to check in milliseconds.</param> /// <param name="time">The time to check in milliseconds.</param>
/// <returns>Whether the time falls within this <see cref="BreakPeriod"/>.</returns> /// <returns>Whether the time falls within this <see cref="BreakPeriod"/>.</returns>
public bool Contains(double time) => time >= StartTime && time <= EndTime; public bool Contains(double time) => time >= StartTime && time <= EndTime - BreakOverlay.BREAK_FADE_DURATION;
} }
} }

View File

@ -2,7 +2,9 @@
// 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.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
@ -15,7 +17,11 @@ namespace osu.Game.Screens.Play
{ {
public class BreakOverlay : Container public class BreakOverlay : Container
{ {
private const double fade_duration = BreakPeriod.MIN_BREAK_DURATION / 2; /// <summary>
/// The duration of the break overlay fading.
/// </summary>
public const double BREAK_FADE_DURATION = BreakPeriod.MIN_BREAK_DURATION / 2;
private const float remaining_time_container_max_size = 0.3f; private const float remaining_time_container_max_size = 0.3f;
private const int vertical_margin = 25; private const int vertical_margin = 25;
@ -29,12 +35,27 @@ namespace osu.Game.Screens.Play
set set
{ {
breaks = value; breaks = value;
initializeBreaks();
// reset index in case the new breaks list is smaller than last one
isBreakTime.Value = false;
CurrentBreakIndex = 0;
if (IsLoaded)
initializeBreaks();
} }
} }
public override bool RemoveCompletedTransforms => false; public override bool RemoveCompletedTransforms => false;
/// <summary>
/// Whether the gameplay is currently in a break.
/// </summary>
public IBindable<bool> IsBreakTime => isBreakTime;
protected int CurrentBreakIndex;
private readonly BindableBool isBreakTime = new BindableBool();
private readonly Container remainingTimeAdjustmentBox; private readonly Container remainingTimeAdjustmentBox;
private readonly Container remainingTimeBox; private readonly Container remainingTimeBox;
private readonly RemainingTimeCounter remainingTimeCounter; private readonly RemainingTimeCounter remainingTimeCounter;
@ -109,10 +130,36 @@ namespace osu.Game.Screens.Play
initializeBreaks(); initializeBreaks();
} }
protected override void Update()
{
base.Update();
updateBreakTimeBindable();
}
private void updateBreakTimeBindable()
{
if (breaks?.Any() != true)
return;
var time = Clock.CurrentTime;
if (time > breaks[CurrentBreakIndex].EndTime)
{
while (time > breaks[CurrentBreakIndex].EndTime && CurrentBreakIndex < breaks.Count - 1)
CurrentBreakIndex++;
}
else
{
while (time < breaks[CurrentBreakIndex].StartTime && CurrentBreakIndex > 0)
CurrentBreakIndex--;
}
var currentBreak = breaks[CurrentBreakIndex];
isBreakTime.Value = currentBreak.HasEffect && currentBreak.Contains(time);
}
private void initializeBreaks() private void initializeBreaks()
{ {
if (!IsLoaded) return; // we need a clock.
FinishTransforms(true); FinishTransforms(true);
Scheduler.CancelDelayedTasks(); Scheduler.CancelDelayedTasks();
@ -125,25 +172,25 @@ namespace osu.Game.Screens.Play
using (BeginAbsoluteSequence(b.StartTime, true)) using (BeginAbsoluteSequence(b.StartTime, true))
{ {
fadeContainer.FadeIn(fade_duration); fadeContainer.FadeIn(BREAK_FADE_DURATION);
breakArrows.Show(fade_duration); breakArrows.Show(BREAK_FADE_DURATION);
remainingTimeAdjustmentBox remainingTimeAdjustmentBox
.ResizeWidthTo(remaining_time_container_max_size, fade_duration, Easing.OutQuint) .ResizeWidthTo(remaining_time_container_max_size, BREAK_FADE_DURATION, Easing.OutQuint)
.Delay(b.Duration - fade_duration) .Delay(b.Duration - BREAK_FADE_DURATION)
.ResizeWidthTo(0); .ResizeWidthTo(0);
remainingTimeBox remainingTimeBox
.ResizeWidthTo(0, b.Duration - fade_duration) .ResizeWidthTo(0, b.Duration - BREAK_FADE_DURATION)
.Then() .Then()
.ResizeWidthTo(1); .ResizeWidthTo(1);
remainingTimeCounter.CountTo(b.Duration).CountTo(0, b.Duration); remainingTimeCounter.CountTo(b.Duration).CountTo(0, b.Duration);
using (BeginDelayedSequence(b.Duration - fade_duration, true)) using (BeginDelayedSequence(b.Duration - BREAK_FADE_DURATION, true))
{ {
fadeContainer.FadeOut(fade_duration); fadeContainer.FadeOut(BREAK_FADE_DURATION);
breakArrows.Hide(fade_duration); breakArrows.Hide(BREAK_FADE_DURATION);
} }
} }
} }