diff --git a/osu.Game.Tests/Visual/Editing/TestSceneTapTimingControl.cs b/osu.Game.Tests/Visual/Editing/TestSceneTapTimingControl.cs index c2889b2549..9b2618df5b 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneTapTimingControl.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneTapTimingControl.cs @@ -36,10 +36,37 @@ namespace osu.Game.Tests.Visual.Editing public TestSceneTapTimingControl() { - editorBeatmap = new EditorBeatmap(CreateBeatmap(new OsuRuleset().RulesetInfo)); + var playableBeatmap = CreateBeatmap(new OsuRuleset().RulesetInfo); + + // Ensure time doesn't end while testing + playableBeatmap.BeatmapInfo.Length = 1200000; + + editorBeatmap = new EditorBeatmap(playableBeatmap); + selectedGroup.Value = editorBeatmap.ControlPointInfo.Groups.First(); } + protected override void LoadComplete() + { + base.LoadComplete(); + + Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap); + Beatmap.Disabled = true; + + Children = new Drawable[] + { + new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Y, + Width = 400, + Scale = new Vector2(1.5f), + Child = control = new TapTimingControl(), + } + }; + } + [Test] public void TestTapThenReset() { @@ -65,24 +92,19 @@ namespace osu.Game.Tests.Visual.Editing [Test] public void TestBasic() { - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap); - Beatmap.Disabled = true; - - Child = new Container + AddStep("set low bpm", () => { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.Y, - Width = 400, - Scale = new Vector2(1.5f), - Child = control = new TapTimingControl(), - }; + editorBeatmap.ControlPointInfo.TimingPoints.First().BeatLength = 1000; + }); + + AddStep("click tap button", () => + { + control.ChildrenOfType() + .First(b => b.Text == "Tap to beat") + .TriggerClick(); + }); + + AddSliderStep("BPM", 30, 400, 60, bpm => editorBeatmap.ControlPointInfo.TimingPoints.First().BeatLength = 60000f / bpm); } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs index 3867a4bcec..e23982ddc7 100644 --- a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs +++ b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs @@ -3,15 +3,19 @@ using System; using osu.Framework.Allocation; +using osu.Framework.Audio.Track; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Utils; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Overlays; using osuTK; +using osuTK.Graphics; namespace osu.Game.Screens.Edit.Timing { @@ -103,9 +107,13 @@ namespace osu.Game.Screens.Edit.Timing private Container swing; private Box weight; private OsuSpriteText bpm; + private Box stick; + + [Resolved] + private OverlayColourProvider overlayColourProvider { get; set; } [BackgroundDependencyLoader] - private void load(OverlayColourProvider overlayColourProvider) + private void load() { Margin = new MarginPadding(10); AutoSizeAxes = Axes.Both; @@ -117,21 +125,13 @@ namespace osu.Game.Screens.Edit.Timing Anchor = Anchor.Centre, Origin = Anchor.Centre, Size = new Vector2(80, 120), - Colour = overlayColourProvider.Background1, - }, - new Circle - { - Y = -25, - Anchor = Anchor.BottomCentre, - Origin = Anchor.Centre, - Colour = overlayColourProvider.Content2, - Size = new Vector2(10) + Colour = overlayColourProvider.Background2, }, bpm = new OsuSpriteText { Colour = overlayColourProvider.Content1, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, }, swing = new Container { @@ -142,10 +142,10 @@ namespace osu.Game.Screens.Edit.Timing Origin = Anchor.BottomCentre, Children = new Drawable[] { - new Box + stick = new Box { - Colour = overlayColourProvider.Content2, RelativeSizeAxes = Axes.Y, + Colour = overlayColourProvider.Colour2, Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, Width = 4, @@ -154,35 +154,84 @@ namespace osu.Game.Screens.Edit.Timing { Anchor = Anchor.TopCentre, Origin = Anchor.Centre, - Colour = overlayColourProvider.Content2, + Colour = overlayColourProvider.Colour1, Size = new Vector2(15), RelativePositionAxes = Axes.Y, - Y = -0.4f, + Y = 0.4f, }, } }, + new Circle + { + Y = -25, + Anchor = Anchor.BottomCentre, + Origin = Anchor.Centre, + Colour = overlayColourProvider.Colour0, + Size = new Vector2(10) + }, }; } - protected override void LoadComplete() - { - base.LoadComplete(); - swing - .RotateTo(20, 500, Easing.InOutQuad) - .Then() - .RotateTo(-20, 500, Easing.InOutQuad) - .Loop(); - } + private double beatLength; + + private TimingControlPoint timingPoint; + + private float bpmRatio; + private bool isSwinging; protected override void Update() { base.Update(); - if (CurrentTimingPoint == null) + timingPoint = Beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(BeatSyncClock.CurrentTime); + + if (beatLength != timingPoint.BeatLength) + { + beatLength = timingPoint.BeatLength; + bpm.Text = $"{timingPoint.BPM:F0}"; + + EarlyActivationMilliseconds = timingPoint.BeatLength / 2; + + bpmRatio = (float)Interpolation.ApplyEasing(Easing.OutQuad, Math.Clamp((timingPoint.BPM - 30) / 480, 0, 1)); + + weight.MoveToY((float)Interpolation.Lerp(0, 0.9f, bpmRatio), 600, Easing.OutQuint); + } + + if (BeatSyncClock?.IsRunning != true && isSwinging) + { + swing.ClearTransforms(true); + + using (swing.BeginDelayedSequence(350)) + { + swing.RotateTo(0, 1000, Easing.OutQuint); + stick.FadeColour(overlayColourProvider.Colour2, 1000, Easing.OutQuint); + } + + isSwinging = false; + } + } + + protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) + { + base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); + + float angle = (float)Interpolation.Lerp(25, 4, bpmRatio); + + if (!IsBeatSyncedWithTrack) return; - weight.Y = Math.Clamp((float)CurrentTimingPoint.BPM / 480, 0, 0.95f); - bpm.Text = $"{CurrentTimingPoint.BPM:F0}"; + isSwinging = true; + + float currentAngle = swing.Rotation; + float targetAngle = currentAngle > 0 ? -angle : angle; + + swing.RotateTo(targetAngle, beatLength, Easing.InOutQuad); + + if (currentAngle != 0 && Math.Abs(currentAngle - targetAngle) > angle * 1.8f && isSwinging) + { + using (stick.BeginDelayedSequence(beatLength / 2)) + stick.FlashColour(overlayColourProvider.Content1, beatLength, Easing.OutQuint); + } } } } diff --git a/osu.Game/Screens/Edit/Timing/TimingSection.cs b/osu.Game/Screens/Edit/Timing/TimingSection.cs index be719d0cb6..a5abd96d72 100644 --- a/osu.Game/Screens/Edit/Timing/TimingSection.cs +++ b/osu.Game/Screens/Edit/Timing/TimingSection.cs @@ -97,6 +97,5 @@ namespace osu.Game.Screens.Edit.Timing } private static double beatLengthToBpm(double beatLength) => 60000 / beatLength; - } }