diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMuted.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMuted.cs index c14dc78f38..e08d66fa31 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMuted.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModMuted.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods MuteComboCount = { Value = 0 }, }, PassCondition = () => Beatmap.Value.Track.AggregateVolume.Value == 0.0 && - Player.ChildrenOfType().SingleOrDefault()?.AggregateVolume.Value == 1.0, + Player.ChildrenOfType().SingleOrDefault()?.AggregateVolume.Value == 1.0, }); /// diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index 5b121f4673..c58c624f5c 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -339,7 +339,7 @@ namespace osu.Game.Rulesets.Osu.Mods public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { - drawableRuleset.Overlays.Add(new Metronome(drawableRuleset.Beatmap.HitObjects.First().StartTime)); + drawableRuleset.Overlays.Add(new MetronomeBeat(drawableRuleset.Beatmap.HitObjects.First().StartTime)); } #endregion diff --git a/osu.Game.Tests/Visual/Editing/TestSceneTapTimingControl.cs b/osu.Game.Tests/Visual/Editing/TestSceneTapTimingControl.cs index 9b2618df5b..de441995b5 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneTapTimingControl.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneTapTimingControl.cs @@ -73,7 +73,7 @@ namespace osu.Game.Tests.Visual.Editing AddStep("click tap button", () => { control.ChildrenOfType() - .First(b => b.Text == "Tap to beat") + .Last() .TriggerClick(); }); @@ -82,7 +82,7 @@ namespace osu.Game.Tests.Visual.Editing AddStep("click reset button", () => { control.ChildrenOfType() - .First(b => b.Text == "Reset") + .First() .TriggerClick(); }); @@ -100,7 +100,7 @@ namespace osu.Game.Tests.Visual.Editing AddStep("click tap button", () => { control.ChildrenOfType() - .First(b => b.Text == "Tap to beat") + .Last() .TriggerClick(); }); diff --git a/osu.Game/Rulesets/Mods/Metronome.cs b/osu.Game/Rulesets/Mods/MetronomeBeat.cs similarity index 95% rename from osu.Game/Rulesets/Mods/Metronome.cs rename to osu.Game/Rulesets/Mods/MetronomeBeat.cs index b85a341577..c7a8b02130 100644 --- a/osu.Game/Rulesets/Mods/Metronome.cs +++ b/osu.Game/Rulesets/Mods/MetronomeBeat.cs @@ -11,14 +11,14 @@ using osu.Game.Skinning; namespace osu.Game.Rulesets.Mods { - public class Metronome : BeatSyncedContainer, IAdjustableAudioComponent + public class MetronomeBeat : BeatSyncedContainer, IAdjustableAudioComponent { private readonly double firstHitTime; private readonly PausableSkinnableSound sample; /// Start time of the first hit object, used for providing a count down. - public Metronome(double firstHitTime) + public MetronomeBeat(double firstHitTime) { this.firstHitTime = firstHitTime; AllowMistimedEventFiring = false; diff --git a/osu.Game/Rulesets/Mods/ModMuted.cs b/osu.Game/Rulesets/Mods/ModMuted.cs index a7d3114f2b..84341faab7 100644 --- a/osu.Game/Rulesets/Mods/ModMuted.cs +++ b/osu.Game/Rulesets/Mods/ModMuted.cs @@ -79,11 +79,11 @@ namespace osu.Game.Rulesets.Mods { if (EnableMetronome.Value) { - Metronome metronome; + MetronomeBeat metronomeBeat; - drawableRuleset.Overlays.Add(metronome = new Metronome(drawableRuleset.Beatmap.HitObjects.First().StartTime)); + drawableRuleset.Overlays.Add(metronomeBeat = new MetronomeBeat(drawableRuleset.Beatmap.HitObjects.First().StartTime)); - metronome.AddAdjustment(AdjustableProperty.Volume, metronomeVolumeAdjust); + metronomeBeat.AddAdjustment(AdjustableProperty.Volume, metronomeVolumeAdjust); } if (AffectsHitSounds.Value) diff --git a/osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs b/osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs new file mode 100644 index 0000000000..fdd5bd1e4e --- /dev/null +++ b/osu.Game/Screens/Edit/Timing/MetronomeDisplay.cs @@ -0,0 +1,255 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Allocation; +using osu.Framework.Audio.Track; +using osu.Framework.Bindables; +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.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Overlays; +using osuTK; + +namespace osu.Game.Screens.Edit.Timing +{ + public class MetronomeDisplay : BeatSyncedContainer + { + private Container swing; + + private OsuSpriteText bpmText; + + private Drawable weight; + private Drawable stick; + + [Resolved] + private OverlayColourProvider overlayColourProvider { get; set; } + + [BackgroundDependencyLoader] + private void load() + { + const float taper = 25; + const float swing_vertical_offset = -23; + const float lower_cover_height = 32; + + var triangleSize = new Vector2(90, 120 + taper); + + Margin = new MarginPadding(10); + + AutoSizeAxes = Axes.Both; + + InternalChildren = new Drawable[] + { + new Container + { + Name = @"Taper adjust", + Masking = true, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Size = new Vector2(triangleSize.X, triangleSize.Y - taper), + Children = new Drawable[] + { + new Triangle + { + Name = @"Main body", + EdgeSmoothness = new Vector2(1), + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Size = triangleSize, + Colour = overlayColourProvider.Background3, + }, + }, + }, + new Circle + { + Name = "Centre marker", + Colour = overlayColourProvider.Background5, + RelativeSizeAxes = Axes.Y, + Width = 2, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Y = -(lower_cover_height + 3), + Height = 0.65f, + }, + swing = new Container + { + Name = @"Swing", + RelativeSizeAxes = Axes.Both, + Y = swing_vertical_offset, + Height = 0.80f, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Children = new[] + { + stick = new Circle + { + Name = @"Stick", + RelativeSizeAxes = Axes.Y, + Colour = overlayColourProvider.Colour2, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Width = 4, + }, + weight = new Container + { + Name = @"Weight", + Anchor = Anchor.TopCentre, + Origin = Anchor.Centre, + Colour = overlayColourProvider.Colour0, + Size = new Vector2(10), + Rotation = 180, + RelativePositionAxes = Axes.Y, + Y = 0.4f, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Shear = new Vector2(0.2f, 0), + EdgeSmoothness = new Vector2(1), + }, + new Box + { + RelativeSizeAxes = Axes.Both, + Shear = new Vector2(-0.2f, 0), + EdgeSmoothness = new Vector2(1), + }, + } + }, + } + }, + new Container + { + Name = @"Taper adjust", + Masking = true, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Size = new Vector2(triangleSize.X, triangleSize.Y - taper), + Children = new Drawable[] + { + new Circle + { + Name = @"Locking wedge", + Anchor = Anchor.TopCentre, + Origin = Anchor.Centre, + Colour = overlayColourProvider.Background1, + Size = new Vector2(8), + } + }, + }, + new Circle + { + Name = @"Swing connection point", + Y = swing_vertical_offset, + Anchor = Anchor.BottomCentre, + Origin = Anchor.Centre, + Colour = overlayColourProvider.Colour0, + Size = new Vector2(8) + }, + new Container + { + Name = @"Lower cover", + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + RelativeSizeAxes = Axes.X, + Masking = true, + Height = lower_cover_height, + Children = new Drawable[] + { + new Triangle + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Size = triangleSize, + Colour = overlayColourProvider.Background2, + EdgeSmoothness = new Vector2(1), + Alpha = 0.8f + }, + } + }, + bpmText = new OsuSpriteText + { + Name = @"BPM display", + Colour = overlayColourProvider.Content1, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Y = -3, + }, + }; + } + + private double beatLength; + + private TimingControlPoint timingPoint; + + private bool isSwinging; + + private readonly BindableInt interpolatedBpm = new BindableInt(); + + protected override void LoadComplete() + { + base.LoadComplete(); + + interpolatedBpm.BindValueChanged(bpm => bpmText.Text = bpm.NewValue.ToString()); + } + + protected override void Update() + { + base.Update(); + + timingPoint = Beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(BeatSyncClock.CurrentTime); + + if (beatLength != timingPoint.BeatLength) + { + beatLength = timingPoint.BeatLength; + + EarlyActivationMilliseconds = timingPoint.BeatLength / 2; + + float bpmRatio = (float)Interpolation.ApplyEasing(Easing.OutQuad, Math.Clamp((timingPoint.BPM - 30) / 480, 0, 1)); + + weight.MoveToY((float)Interpolation.Lerp(0.1f, 0.83f, bpmRatio), 600, Easing.OutQuint); + this.TransformBindableTo(interpolatedBpm, (int)timingPoint.BPM, 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); + + const float angle = 27.5f; + + if (!IsBeatSyncedWithTrack) + return; + + 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/TapTimingControl.cs b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs index a99468fbff..1b0f0a3f5e 100644 --- a/osu.Game/Screens/Edit/Timing/TapTimingControl.cs +++ b/osu.Game/Screens/Edit/Timing/TapTimingControl.cs @@ -1,21 +1,15 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Framework.Allocation; -using osu.Framework.Audio.Track; using osu.Framework.Bindables; 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; namespace osu.Game.Screens.Edit.Timing { @@ -24,6 +18,9 @@ namespace osu.Game.Screens.Edit.Timing [Resolved] private EditorClock editorClock { get; set; } + [Resolved] + private Bindable selectedGroup { get; set; } + [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider, OsuColour colours) { @@ -52,7 +49,11 @@ namespace osu.Game.Screens.Edit.Timing { new Drawable[] { - new Metronome() + new MetronomeDisplay + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } }, new Drawable[] { @@ -74,7 +75,7 @@ namespace osu.Game.Screens.Edit.Timing { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, - Text = "Tap to beat", + Text = "Play from start", RelativeSizeAxes = Axes.X, BackgroundColour = colourProvider.Background1, Width = 0.68f, @@ -90,252 +91,14 @@ namespace osu.Game.Screens.Edit.Timing private void tap() { - if (!editorClock.IsRunning) - { - editorClock.Seek(0); - editorClock.Start(); - } + editorClock.Seek(selectedGroup.Value.Time); + editorClock.Start(); } private void reset() { editorClock.Stop(); - } - - private class Metronome : BeatSyncedContainer - { - private Container swing; - - private OsuSpriteText bpmText; - - private Drawable weight; - private Drawable stick; - - [Resolved] - private OverlayColourProvider overlayColourProvider { get; set; } - - [BackgroundDependencyLoader] - private void load() - { - const float taper = 25; - const float swing_vertical_offset = -23; - const float lower_cover_height = 32; - - var triangleSize = new Vector2(90, 120 + taper); - - Margin = new MarginPadding(10); - - AutoSizeAxes = Axes.Both; - - InternalChildren = new Drawable[] - { - new Container - { - Name = @"Taper adjust", - Masking = true, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Size = new Vector2(triangleSize.X, triangleSize.Y - taper), - Children = new Drawable[] - { - new Triangle - { - Name = @"Main body", - EdgeSmoothness = new Vector2(1), - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Size = triangleSize, - Colour = overlayColourProvider.Background3, - }, - }, - }, - new Circle - { - Name = "Centre marker", - Colour = overlayColourProvider.Background5, - RelativeSizeAxes = Axes.Y, - Width = 2, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Y = -(lower_cover_height + 3), - Height = 0.65f, - }, - swing = new Container - { - Name = @"Swing", - RelativeSizeAxes = Axes.Both, - Y = swing_vertical_offset, - Height = 0.80f, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Children = new[] - { - stick = new Circle - { - Name = @"Stick", - RelativeSizeAxes = Axes.Y, - Colour = overlayColourProvider.Colour2, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Width = 4, - }, - weight = new Container - { - Name = @"Weight", - Anchor = Anchor.TopCentre, - Origin = Anchor.Centre, - Colour = overlayColourProvider.Colour0, - Size = new Vector2(10), - Rotation = 180, - RelativePositionAxes = Axes.Y, - Y = 0.4f, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Shear = new Vector2(0.2f, 0), - EdgeSmoothness = new Vector2(1), - }, - new Box - { - RelativeSizeAxes = Axes.Both, - Shear = new Vector2(-0.2f, 0), - EdgeSmoothness = new Vector2(1), - }, - } - }, - } - }, - new Container - { - Name = @"Taper adjust", - Masking = true, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Size = new Vector2(triangleSize.X, triangleSize.Y - taper), - Children = new Drawable[] - { - new Circle - { - Name = @"Locking wedge", - Anchor = Anchor.TopCentre, - Origin = Anchor.Centre, - Colour = overlayColourProvider.Background1, - Size = new Vector2(8), - } - }, - }, - new Circle - { - Name = @"Swing connection point", - Y = swing_vertical_offset, - Anchor = Anchor.BottomCentre, - Origin = Anchor.Centre, - Colour = overlayColourProvider.Colour0, - Size = new Vector2(8) - }, - new Container - { - Name = @"Lower cover", - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - RelativeSizeAxes = Axes.X, - Masking = true, - Height = lower_cover_height, - Children = new Drawable[] - { - new Triangle - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Size = triangleSize, - Colour = overlayColourProvider.Background2, - EdgeSmoothness = new Vector2(1), - Alpha = 0.8f - }, - } - }, - bpmText = new OsuSpriteText - { - Name = @"BPM display", - Colour = overlayColourProvider.Content1, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Y = -3, - }, - }; - } - - private double beatLength; - - private TimingControlPoint timingPoint; - - private bool isSwinging; - - private readonly BindableInt interpolatedBpm = new BindableInt(); - - protected override void LoadComplete() - { - base.LoadComplete(); - - interpolatedBpm.BindValueChanged(bpm => bpmText.Text = bpm.NewValue.ToString()); - } - - protected override void Update() - { - base.Update(); - - timingPoint = Beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(BeatSyncClock.CurrentTime); - - if (beatLength != timingPoint.BeatLength) - { - beatLength = timingPoint.BeatLength; - - EarlyActivationMilliseconds = timingPoint.BeatLength / 2; - - float bpmRatio = (float)Interpolation.ApplyEasing(Easing.OutQuad, Math.Clamp((timingPoint.BPM - 30) / 480, 0, 1)); - - weight.MoveToY((float)Interpolation.Lerp(0.1f, 0.83f, bpmRatio), 600, Easing.OutQuint); - this.TransformBindableTo(interpolatedBpm, (int)timingPoint.BPM, 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); - - const float angle = 27.5f; - - if (!IsBeatSyncedWithTrack) - return; - - 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); - } - } + editorClock.Seek(selectedGroup.Value.Time); } } }