diff --git a/osu.Android.props b/osu.Android.props index 0563e5319d..ff04c7f120 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs index 65bed071cd..8cb7f3f4b6 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs @@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Osu.Tests if (auto && !userTriggered && Time.Current > Spinner.StartTime + Spinner.Duration / 2 && Progress < 1) { // force completion only once to not break human interaction - Disc.RotationAbsolute = Spinner.SpinsRequired * 360; + Disc.CumulativeRotation = Spinner.SpinsRequired * 360; auto = false; } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs index ea006ec607..6b1394d799 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs @@ -14,6 +14,12 @@ using osu.Game.Rulesets.Osu.Objects.Drawables; using osuTK; using System.Collections.Generic; using System.Linq; +using osu.Framework.Graphics.Sprites; +using osu.Game.Replays; +using osu.Game.Rulesets.Osu.Replays; +using osu.Game.Rulesets.Osu.UI; +using osu.Game.Rulesets.Replays; +using osu.Game.Scoring; using osu.Game.Storyboards; using static osu.Game.Tests.Visual.OsuTestScene.ClockBackedTestWorkingBeatmap; @@ -36,6 +42,7 @@ namespace osu.Game.Rulesets.Osu.Tests } private DrawableSpinner drawableSpinner; + private SpriteIcon spinnerSymbol => drawableSpinner.ChildrenOfType().Single(); [SetUpSteps] public override void SetUpSteps() @@ -50,25 +57,78 @@ namespace osu.Game.Rulesets.Osu.Tests public void TestSpinnerRewindingRotation() { addSeekStep(5000); - AddAssert("is rotation absolute not almost 0", () => !Precision.AlmostEquals(drawableSpinner.Disc.RotationAbsolute, 0, 100)); + AddAssert("is disc rotation not almost 0", () => !Precision.AlmostEquals(drawableSpinner.Disc.Rotation, 0, 100)); + AddAssert("is disc rotation absolute not almost 0", () => !Precision.AlmostEquals(drawableSpinner.Disc.CumulativeRotation, 0, 100)); addSeekStep(0); - AddAssert("is rotation absolute almost 0", () => Precision.AlmostEquals(drawableSpinner.Disc.RotationAbsolute, 0, 100)); + AddAssert("is disc rotation almost 0", () => Precision.AlmostEquals(drawableSpinner.Disc.Rotation, 0, 100)); + AddAssert("is disc rotation absolute almost 0", () => Precision.AlmostEquals(drawableSpinner.Disc.CumulativeRotation, 0, 100)); } [Test] public void TestSpinnerMiddleRewindingRotation() { - double estimatedRotation = 0; + double finalAbsoluteDiscRotation = 0, finalRelativeDiscRotation = 0, finalSpinnerSymbolRotation = 0; addSeekStep(5000); - AddStep("retrieve rotation", () => estimatedRotation = drawableSpinner.Disc.RotationAbsolute); + AddStep("retrieve disc relative rotation", () => finalRelativeDiscRotation = drawableSpinner.Disc.Rotation); + AddStep("retrieve disc absolute rotation", () => finalAbsoluteDiscRotation = drawableSpinner.Disc.CumulativeRotation); + AddStep("retrieve spinner symbol rotation", () => finalSpinnerSymbolRotation = spinnerSymbol.Rotation); addSeekStep(2500); + AddUntilStep("disc rotation rewound", + // we want to make sure that the rotation at time 2500 is in the same direction as at time 5000, but about half-way in. + () => Precision.AlmostEquals(drawableSpinner.Disc.Rotation, finalRelativeDiscRotation / 2, 100)); + AddUntilStep("symbol rotation rewound", + () => Precision.AlmostEquals(spinnerSymbol.Rotation, finalSpinnerSymbolRotation / 2, 100)); + addSeekStep(5000); - AddAssert("is rotation absolute almost same", () => Precision.AlmostEquals(drawableSpinner.Disc.RotationAbsolute, estimatedRotation, 100)); + AddAssert("is disc rotation almost same", + () => Precision.AlmostEquals(drawableSpinner.Disc.Rotation, finalRelativeDiscRotation, 100)); + AddAssert("is symbol rotation almost same", + () => Precision.AlmostEquals(spinnerSymbol.Rotation, finalSpinnerSymbolRotation, 100)); + AddAssert("is disc rotation absolute almost same", + () => Precision.AlmostEquals(drawableSpinner.Disc.CumulativeRotation, finalAbsoluteDiscRotation, 100)); } + [Test] + public void TestRotationDirection([Values(true, false)] bool clockwise) + { + if (clockwise) + { + AddStep("flip replay", () => + { + var drawableRuleset = this.ChildrenOfType().Single(); + var score = drawableRuleset.ReplayScore; + var scoreWithFlippedReplay = new Score + { + ScoreInfo = score.ScoreInfo, + Replay = flipReplay(score.Replay) + }; + drawableRuleset.SetReplayScore(scoreWithFlippedReplay); + }); + } + + addSeekStep(5000); + + AddAssert("disc spin direction correct", () => clockwise ? drawableSpinner.Disc.Rotation > 0 : drawableSpinner.Disc.Rotation < 0); + AddAssert("spinner symbol direction correct", () => clockwise ? spinnerSymbol.Rotation > 0 : spinnerSymbol.Rotation < 0); + } + + private Replay flipReplay(Replay scoreReplay) => new Replay + { + Frames = scoreReplay + .Frames + .Cast() + .Select(replayFrame => + { + var flippedPosition = new Vector2(OsuPlayfield.BASE_SIZE.X - replayFrame.Position.X, replayFrame.Position.Y); + return new OsuReplayFrame(replayFrame.Time, flippedPosition, replayFrame.Actions.ToArray()); + }) + .Cast() + .ToList() + }; + [Test] public void TestSpinPerMinuteOnRewind() { diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index 4d37622be5..be6766509c 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -138,7 +138,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables positionBindable.BindTo(HitObject.PositionBindable); } - public float Progress => Math.Clamp(Disc.RotationAbsolute / 360 / Spinner.SpinsRequired, 0, 1); + public float Progress => Math.Clamp(Disc.CumulativeRotation / 360 / Spinner.SpinsRequired, 0, 1); protected override void CheckForResult(bool userTriggered, double timeOffset) { @@ -191,13 +191,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables circle.Rotation = Disc.Rotation; Ticks.Rotation = Disc.Rotation; - SpmCounter.SetRotation(Disc.RotationAbsolute); + SpmCounter.SetRotation(Disc.CumulativeRotation); float relativeCircleScale = Spinner.Scale * circle.DrawHeight / mainContainer.DrawHeight; float targetScale = relativeCircleScale + (1 - relativeCircleScale) * Progress; Disc.Scale = new Vector2((float)Interpolation.Lerp(Disc.Scale.X, targetScale, Math.Clamp(Math.Abs(Time.Elapsed) / 100, 0, 1))); - symbol.Rotation = (float)Interpolation.Lerp(symbol.Rotation, Disc.RotationAbsolute / 2, Math.Clamp(Math.Abs(Time.Elapsed) / 40, 0, 1)); + symbol.Rotation = (float)Interpolation.Lerp(symbol.Rotation, Disc.Rotation / 2, Math.Clamp(Math.Abs(Time.Elapsed) / 40, 0, 1)); } protected override void UpdateInitialTransforms() @@ -207,9 +207,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables circleContainer.ScaleTo(Spinner.Scale * 0.3f); circleContainer.ScaleTo(Spinner.Scale, HitObject.TimePreempt / 1.4f, Easing.OutQuint); - Disc.RotateTo(-720); - symbol.RotateTo(-720); - mainContainer .ScaleTo(0) .ScaleTo(Spinner.Scale * circle.DrawHeight / DrawHeight * 1.4f, HitObject.TimePreempt - 150, Easing.OutQuint) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs index d4ef039b79..35819cd05e 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs @@ -73,6 +73,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces } } + /// + /// The total rotation performed on the spinner disc, disregarding the spin direction. + /// + /// + /// This value is always non-negative and is monotonically increasing with time + /// (i.e. will only increase if time is passing forward, but can decrease during rewind). + /// + /// + /// If the spinner is spun 360 degrees clockwise and then 360 degrees counter-clockwise, + /// this property will return the value of 720 (as opposed to 0 for ). + /// + public float CumulativeRotation; + /// /// Whether currently in the correct time range to allow spinning. /// @@ -88,10 +101,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces private float lastAngle; private float currentRotation; - public float RotationAbsolute; private int completeTick; - private bool updateCompleteTick() => completeTick != (completeTick = (int)(RotationAbsolute / 360)); + private bool updateCompleteTick() => completeTick != (completeTick = (int)(CumulativeRotation / 360)); private bool rotationTransferred; @@ -149,7 +161,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces } currentRotation += angle; - RotationAbsolute += Math.Abs(angle) * Math.Sign(Clock.ElapsedFrameTime); + CumulativeRotation += Math.Abs(angle) * Math.Sign(Clock.ElapsedFrameTime); } } } diff --git a/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs b/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs index d47d37806e..3106d1143e 100644 --- a/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs +++ b/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs @@ -183,6 +183,7 @@ namespace osu.Game.Beatmaps public void Dispose() { cacheDownloadRequest?.Dispose(); + updateScheduler?.Dispose(); } [Serializable] diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index 1b748cb672..de08b79f57 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -28,9 +28,6 @@ namespace osu.Game.Overlays.Toolbar private const double transition_time = 500; - private const float alpha_hovering = 0.8f; - private const float alpha_normal = 0.6f; - private readonly Bindable overlayActivationMode = new Bindable(OverlayActivation.All); // Toolbar components like RulesetSelector should receive keyboard input events even when the toolbar is hidden. @@ -103,7 +100,6 @@ namespace osu.Game.Overlays.Toolbar public class ToolbarBackground : Container { - private readonly Box solidBackground; private readonly Box gradientBackground; public ToolbarBackground() @@ -111,11 +107,10 @@ namespace osu.Game.Overlays.Toolbar RelativeSizeAxes = Axes.Both; Children = new Drawable[] { - solidBackground = new Box + new Box { RelativeSizeAxes = Axes.Both, Colour = OsuColour.Gray(0.1f), - Alpha = alpha_normal, }, gradientBackground = new Box { @@ -131,14 +126,12 @@ namespace osu.Game.Overlays.Toolbar protected override bool OnHover(HoverEvent e) { - solidBackground.FadeTo(alpha_hovering, transition_time, Easing.OutQuint); gradientBackground.FadeIn(transition_time, Easing.OutQuint); return true; } protected override void OnHoverLost(HoverLostEvent e) { - solidBackground.FadeTo(alpha_normal, transition_time, Easing.OutQuint); gradientBackground.FadeOut(transition_time, Easing.OutQuint); } } @@ -146,7 +139,7 @@ namespace osu.Game.Overlays.Toolbar protected override void PopIn() { this.MoveToY(0, transition_time, Easing.OutQuint); - this.FadeIn(transition_time / 2, Easing.OutQuint); + this.FadeIn(transition_time / 4, Easing.OutQuint); } protected override void PopOut() @@ -154,7 +147,7 @@ namespace osu.Game.Overlays.Toolbar userButton.StateContainer?.Hide(); this.MoveToY(-DrawSize.Y, transition_time, Easing.OutQuint); - this.FadeOut(transition_time); + this.FadeOut(transition_time, Easing.InQuint); } } } diff --git a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs index d4b6a3b79f..9c2ed26b52 100644 --- a/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/Multi/Lounge/LoungeSubScreen.cs @@ -34,7 +34,7 @@ namespace osu.Game.Screens.Multi.Lounge public LoungeSubScreen() { - SearchContainer searchContainer; + RoomsContainer roomsContainer; InternalChildren = new Drawable[] { @@ -55,14 +55,9 @@ namespace osu.Game.Screens.Multi.Lounge RelativeSizeAxes = Axes.Both, ScrollbarOverlapsContent = false, Padding = new MarginPadding(10), - Child = searchContainer = new SearchContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Child = new RoomsContainer { JoinRequested = joinRequested } - }, + Child = roomsContainer = new RoomsContainer { JoinRequested = joinRequested } }, - loadingLayer = new LoadingLayer(searchContainer), + loadingLayer = new LoadingLayer(roomsContainer), } }, new RoomInspector diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 4e6de77e86..e4753e7ee9 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -24,7 +24,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index c31e28638f..91fa003604 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -80,7 +80,7 @@ - +