From 817bae3219871b7541569984522c11cb8425ed20 Mon Sep 17 00:00:00 2001 From: MrTheMake Date: Thu, 18 May 2017 01:54:17 +0200 Subject: [PATCH 01/32] Fixes the auto mod not hitting stacks of notes on certain maps. --- osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs index f8365bf9ab..db99ddf0d4 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs @@ -92,24 +92,24 @@ namespace osu.Game.Rulesets.Osu.Replays if (h.StartTime - h.HitWindowFor(OsuScoreResult.Miss) > endTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50) { if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), prev.EndPosition.X, prev.EndPosition.Y, ReplayButtonState.None)); - if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Miss), h.Position.X, h.Position.Y, ReplayButtonState.None)); + if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Miss), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None)); } else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50) > endTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50) { if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), prev.EndPosition.X, prev.EndPosition.Y, ReplayButtonState.None)); - if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50), h.Position.X, h.Position.Y, ReplayButtonState.None)); + if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None)); } else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100) > endTime + h.HitWindowFor(OsuScoreResult.Hit100) + 50) { if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit100), prev.EndPosition.X, prev.EndPosition.Y, ReplayButtonState.None)); - if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100), h.Position.X, h.Position.Y, ReplayButtonState.None)); + if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None)); } } private void addHitObjectReplay(OsuHitObject h) { // Default values for circles/sliders - Vector2 startPosition = h.Position; + Vector2 startPosition = h.StackedPosition; EasingTypes easing = preferredEasing; float spinnerDirection = -1; @@ -238,7 +238,7 @@ namespace osu.Game.Rulesets.Osu.Replays // TODO: Why do we delay 1 ms if the object is a spinner? There already is KEY_UP_DELAY from hEndTime. double hEndTime = ((h as IHasEndTime)?.EndTime ?? h.StartTime) + KEY_UP_DELAY; int endDelay = h is Spinner ? 1 : 0; - ReplayFrame endFrame = new ReplayFrame(hEndTime + endDelay, h.EndPosition.X, h.EndPosition.Y, ReplayButtonState.None); + ReplayFrame endFrame = new ReplayFrame(hEndTime + endDelay, h.StackedEndPosition.X, h.StackedEndPosition.Y, ReplayButtonState.None); // Decrement because we want the previous frame, not the next one int index = FindInsertionIndex(startFrame) - 1; From 91859042e8f5f167291b1ade81f3a0a07128d97f Mon Sep 17 00:00:00 2001 From: MrTheMake Date: Thu, 18 May 2017 02:15:16 +0200 Subject: [PATCH 02/32] Fix not all cases of the auto replay generator fix being covered. --- osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs index db99ddf0d4..22246af5fd 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs @@ -91,17 +91,17 @@ namespace osu.Game.Rulesets.Osu.Replays // Make the cursor stay at a hitObject as long as possible (mainly for autopilot). if (h.StartTime - h.HitWindowFor(OsuScoreResult.Miss) > endTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50) { - if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), prev.EndPosition.X, prev.EndPosition.Y, ReplayButtonState.None)); + if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), prev.StackedPosition.X, prev.StackedPosition.Y, ReplayButtonState.None)); if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Miss), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None)); } else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50) > endTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50) { - if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), prev.EndPosition.X, prev.EndPosition.Y, ReplayButtonState.None)); + if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), prev.StackedPosition.X, prev.StackedPosition.Y, ReplayButtonState.None)); if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None)); } else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100) > endTime + h.HitWindowFor(OsuScoreResult.Hit100) + 50) { - if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit100), prev.EndPosition.X, prev.EndPosition.Y, ReplayButtonState.None)); + if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit100), prev.StackedPosition.X, prev.StackedPosition.Y, ReplayButtonState.None)); if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None)); } } From 58ebb24032350bf2ef79aef08ae9a5dd44833efc Mon Sep 17 00:00:00 2001 From: MrTheMake Date: Thu, 18 May 2017 02:17:49 +0200 Subject: [PATCH 03/32] Use the end position of a previous note stack rather than the start position. --- osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs index 22246af5fd..5ede3f56f5 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs @@ -91,17 +91,17 @@ namespace osu.Game.Rulesets.Osu.Replays // Make the cursor stay at a hitObject as long as possible (mainly for autopilot). if (h.StartTime - h.HitWindowFor(OsuScoreResult.Miss) > endTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50) { - if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), prev.StackedPosition.X, prev.StackedPosition.Y, ReplayButtonState.None)); + if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), prev.StackedEndPosition.X, prev.StackedEndPosition.Y, ReplayButtonState.None)); if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Miss), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None)); } else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50) > endTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50) { - if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), prev.StackedPosition.X, prev.StackedPosition.Y, ReplayButtonState.None)); + if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), prev.StackedEndPosition.X, prev.StackedEndPosition.Y, ReplayButtonState.None)); if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None)); } else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100) > endTime + h.HitWindowFor(OsuScoreResult.Hit100) + 50) { - if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit100), prev.StackedPosition.X, prev.StackedPosition.Y, ReplayButtonState.None)); + if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit100), prev.StackedEndPosition.X, prev.StackedEndPosition.Y, ReplayButtonState.None)); if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None)); } } From f112546085499eccd445624ad45f8ab8a064135b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 May 2017 19:39:40 +0900 Subject: [PATCH 04/32] Don't load a mode when opening TestCaseHitObjects --- osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs b/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs index dceb7a9cff..8c913ae95e 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs @@ -129,8 +129,6 @@ namespace osu.Desktop.VisualTests.Tests }; Add(clockAdjustContainer); - - load(mode); } private int depth; From 9cd41c0e87894a2c70db18ef539211eb5cfbe60d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 May 2017 19:40:20 +0900 Subject: [PATCH 05/32] Spinner design refresh --- .../Objects/Drawables/DrawableSpinner.cs | 138 ++++++++++++------ .../Objects/Drawables/Pieces/CirclePiece.cs | 1 - .../Drawables/Pieces/SpinnerBackground.cs | 46 +++++- .../Objects/Drawables/Pieces/SpinnerDisc.cs | 138 +++++------------- .../Objects/Drawables/Pieces/SpinnerTicks.cs | 71 +++++++++ .../osu.Game.Rulesets.Osu.csproj | 5 +- 6 files changed, 249 insertions(+), 150 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerTicks.cs diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index a8ff231cc7..642f088445 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -1,15 +1,16 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.MathUtils; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using OpenTK; using OpenTK.Graphics; -using osu.Game.Rulesets.Osu.UI; +using osu.Game.Graphics; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Allocation; +using osu.Game.Screens.Ranking; namespace osu.Game.Rulesets.Osu.Objects.Drawables { @@ -18,9 +19,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private readonly Spinner spinner; private readonly SpinnerDisc disc; + private readonly SpinnerTicks ticks; + + private readonly Container mainContainer; + private readonly SpinnerBackground background; private readonly Container circleContainer; - private readonly DrawableHitCircle circle; + private readonly CirclePiece circle; + + private readonly TextAwesome symbol; + + private Color4 normalColour; + private Color4 completeColour; public DrawableSpinner(Spinner s) : base(s) { @@ -29,57 +39,84 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Origin = Anchor.Centre; Position = s.Position; - //take up full playfield. - Size = new Vector2(OsuPlayfield.BASE_SIZE.X); + RelativeSizeAxes = Axes.Both; + + // we are slightly bigger than our parent, to clip the top and bottom of the circle + Height = 1.2f; spinner = s; Children = new Drawable[] { - background = new SpinnerBackground - { - Alpha = 0, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - DiscColour = Color4.Black - }, - disc = new SpinnerDisc - { - Alpha = 0, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - DiscColour = AccentColour - }, circleContainer = new Container { AutoSizeAxes = Axes.Both, Anchor = Anchor.Centre, Origin = Anchor.Centre, - Children = new [] + Children = new Drawable[] { - circle = new DrawableHitCircle(s) + circle = new CirclePiece { - Interactive = false, Position = Vector2.Zero, Anchor = Anchor.Centre, + }, + new RingPiece(), + symbol = new TextAwesome + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + UseFullGlyphHeight = true, + TextSize = 48, + Icon = FontAwesome.fa_asterisk, + Shadow = false, } } - } + }, + mainContainer = new AspectContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Y, + Children = new Drawable[] + { + background = new SpinnerBackground + { + Alpha = 0.2f, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + disc = new SpinnerDisc(spinner) + { + Scale = Vector2.Zero, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + circleContainer.CreateProxy(), + ticks = new SpinnerTicks + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + } + }, }; - - background.Scale = scaleToCircle; - disc.Scale = scaleToCircle; } + public float Progress => MathHelper.Clamp(disc.RotationAbsolute / 360 / spinner.SpinsRequired, 0, 1); + protected override void CheckJudgement(bool userTriggered) { if (Time.Current < HitObject.StartTime) return; - disc.ScaleTo(Interpolation.ValueAt(Math.Sqrt(Progress), scaleToCircle, Vector2.One, 0, 1), 100); - - if (Progress >= 1) + if (Progress >= 1 && !disc.Complete) + { disc.Complete = true; + disc.FadeAccent(completeColour, 500); + background.FadeAccent(completeColour, 500); + circle.FadeColour(completeColour, 500); + } + if (!userTriggered && Time.Current >= spinner.EndTime) { if (Progress >= 1) @@ -106,26 +143,45 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } } - private Vector2 scaleToCircle => circle.Scale * circle.DrawWidth / DrawWidth * 0.95f; + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + normalColour = colours.BlueDark; + completeColour = colours.YellowLight.Opacity(0.8f); - public float Progress => MathHelper.Clamp(disc.RotationAbsolute / 360 / spinner.SpinsRequired, 0, 1); + disc.AccentColour = normalColour; + background.AccentColour = normalColour; + circle.Colour = normalColour; + } + + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + circle.Rotation = disc.Rotation; + ticks.Rotation = disc.Rotation; + + float relativeCircleScale = spinner.Scale * circle.DrawHeight / mainContainer.DrawHeight; + disc.ScaleTo(relativeCircleScale + (1 - relativeCircleScale) * Progress, 200, EasingTypes.OutQuint); + + symbol.RotateTo(disc.Rotation / 2, 500, EasingTypes.OutQuint); + } protected override void UpdatePreemptState() { base.UpdatePreemptState(); - circleContainer.ScaleTo(1, 400, EasingTypes.OutElastic); + circleContainer.ScaleTo(spinner.Scale * 0.3f); + circleContainer.ScaleTo(spinner.Scale, TIME_PREEMPT / 1.4f, EasingTypes.OutQuint); - background.Delay(TIME_PREEMPT - 500); + disc.RotateTo(-720); + symbol.RotateTo(-720); - background.ScaleTo(scaleToCircle * 1.2f, 400, EasingTypes.OutQuint); - background.FadeIn(200); + mainContainer.ScaleTo(0); + mainContainer.ScaleTo(spinner.Scale * circle.DrawHeight / DrawHeight * 1.4f, TIME_PREEMPT - 150, EasingTypes.OutQuint); - background.Delay(400); - background.ScaleTo(1, 250, EasingTypes.OutQuint); - - disc.Delay(TIME_PREEMPT - 50); - disc.FadeIn(200); + mainContainer.Delay(TIME_PREEMPT - 150); + mainContainer.ScaleTo(1, 500, EasingTypes.OutQuint); } protected override void UpdateCurrentState(ArmedState state) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs index 9a90c07517..3004dafda7 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs @@ -16,7 +16,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { private readonly Sprite disc; - public Func Hit; public CirclePiece() diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs index 72024bbe99..b3ce075575 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs @@ -1,10 +1,54 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; + namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { - public class SpinnerBackground : SpinnerDisc + public class SpinnerBackground : CircularContainer, IHasAccentColour { public override bool HandleInput => false; + + protected Sprite Disc; + + public Color4 AccentColour + { + get { return Disc.Colour; } + set { Disc.Colour = value; } + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + EdgeEffect = new EdgeEffect + { + Type = EdgeEffectType.Glow, + Radius = 14, + Colour = colours.BlueLight.Opacity(0.3f), + }; + } + + public SpinnerBackground() + { + RelativeSizeAxes = Axes.Both; + Masking = true; + + Children = new Drawable[] + { + Disc = new Box + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Alpha = 1, + }, + }; + } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs index 71adba74c7..2d50754b4f 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs @@ -2,13 +2,8 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Transforms; using osu.Framework.Input; using osu.Game.Graphics; @@ -17,104 +12,31 @@ using OpenTK.Graphics; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { - public class SpinnerDisc : CircularContainer + public class SpinnerDisc : CircularContainer, IHasAccentColour { - protected Sprite Disc; + private readonly Spinner spinner; - public SRGBColour DiscColour + public Color4 AccentColour { - get { return Disc.Colour; } - set { Disc.Colour = value; } + get { return background.AccentColour; } + set { background.AccentColour = value; } } - private Color4 completeColour; + private readonly SpinnerBackground background; - [BackgroundDependencyLoader] - private void load(OsuColour colours) + private const float idle_alpha = 0.1f; + private const float tracking_alpha = 0.3f; + + public SpinnerDisc(Spinner s) { - completeColour = colours.YellowLight.Opacity(0.8f); - Masking = true; - } + spinner = s; - private class SpinnerBorder : Container - { - public SpinnerBorder() - { - Origin = Anchor.Centre; - Anchor = Anchor.Centre; - RelativeSizeAxes = Axes.Both; - - layout(); - } - - private int lastLayoutDotCount; - private void layout() - { - int count = (int)(MathHelper.Pi * ScreenSpaceDrawQuad.Width / 9); - - if (count == lastLayoutDotCount) return; - - lastLayoutDotCount = count; - - while (Children.Count() < count) - { - Add(new CircularContainer - { - Colour = Color4.White, - RelativePositionAxes = Axes.Both, - Masking = true, - Origin = Anchor.Centre, - Size = new Vector2(1 / ScreenSpaceDrawQuad.Width * 2000), - Children = new[] - { - new Box - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - } - } - }); - } - - var size = new Vector2(1 / ScreenSpaceDrawQuad.Width * 2000); - - int i = 0; - foreach (var d in Children) - { - d.Size = size; - d.Position = new Vector2( - 0.5f + (float)Math.Sin((float)i / count * 2 * MathHelper.Pi) / 2, - 0.5f + (float)Math.Cos((float)i / count * 2 * MathHelper.Pi) / 2 - ); - - i++; - } - } - - protected override void Update() - { - base.Update(); - layout(); - } - } - - public SpinnerDisc() - { AlwaysReceiveInput = true; - RelativeSizeAxes = Axes.Both; Children = new Drawable[] { - Disc = new Box - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Alpha = 0.2f, - }, - new SpinnerBorder() + background = new SpinnerBackground { Alpha = idle_alpha }, }; } @@ -125,10 +47,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces set { if (value == tracking) return; - tracking = value; - Disc.FadeTo(tracking ? 0.5f : 0.2f, 100); + background.FadeTo(tracking ? tracking_alpha : idle_alpha, 100); } } @@ -139,31 +60,28 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces set { if (value == complete) return; - complete = value; - Disc.FadeColour(completeColour, 200); - updateCompleteTick(); } } protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) { - Tracking = true; + Tracking |= state.Mouse.HasMainButtonPressed; return base.OnMouseDown(state, args); } protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) { - Tracking = false; + Tracking &= state.Mouse.HasMainButtonPressed; return base.OnMouseUp(state, args); } protected override bool OnMouseMove(InputState state) { Tracking |= state.Mouse.HasMainButtonPressed; - mousePosition = state.Mouse.Position; + mousePosition = Parent.ToLocalSpace(state.Mouse.NativeState.Position); return base.OnMouseMove(state); } @@ -177,13 +95,24 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces private bool updateCompleteTick() => completeTick != (completeTick = (int)(RotationAbsolute / 360)); + private bool rotationTransferred; + protected override void Update() { base.Update(); var thisAngle = -(float)MathHelper.RadiansToDegrees(Math.Atan2(mousePosition.X - DrawSize.X / 2, mousePosition.Y - DrawSize.Y / 2)); - if (tracking) + + bool validAndTracking = tracking && spinner.StartTime <= Time.Current && spinner.EndTime > Time.Current; + + if (validAndTracking) { + if (!rotationTransferred) + { + currentRotation = Rotation * 2; + rotationTransferred = true; + } + if (thisAngle - lastAngle > 180) lastAngle += 360; else if (lastAngle - thisAngle > 180) @@ -192,17 +121,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces currentRotation += thisAngle - lastAngle; RotationAbsolute += Math.Abs(thisAngle - lastAngle); } + lastAngle = thisAngle; if (Complete && updateCompleteTick()) { - Disc.Flush(flushType: typeof(TransformAlpha)); - Disc.FadeTo(0.75f, 30, EasingTypes.OutExpo); - Disc.Delay(30); - Disc.FadeTo(0.5f, 250, EasingTypes.OutQuint); + background.Flush(flushType: typeof(TransformAlpha)); + background.FadeTo(0.75f, 60, EasingTypes.OutExpo); + background.Delay(60); + background.FadeTo(0.5f, 250, EasingTypes.OutQuint); } - RotateTo(currentRotation, 100, EasingTypes.OutExpo); + RotateTo(currentRotation / 2, validAndTracking ? 500 : 1500, EasingTypes.OutExpo); } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerTicks.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerTicks.cs new file mode 100644 index 0000000000..d08051eed6 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerTicks.cs @@ -0,0 +1,71 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces +{ + public class SpinnerTicks : Container + { + private Color4 glowColour; + + public SpinnerTicks() + { + Origin = Anchor.Centre; + Anchor = Anchor.Centre; + RelativeSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + glowColour = colours.BlueDarker.Opacity(0.4f); + layout(); + } + + private void layout() + { + const int count = 18; + + for (int i = 0; i < count; i++) + { + Add(new Container + { + Colour = Color4.Black, + Alpha = 0.4f, + EdgeEffect = new EdgeEffect + { + Type = EdgeEffectType.Glow, + Radius = 20, + Colour = glowColour, + }, + RelativePositionAxes = Axes.Both, + Masking = true, + CornerRadius = 5, + Size = new Vector2(60, 10), + Origin = Anchor.Centre, + Position = new Vector2( + 0.5f + (float)Math.Sin((float)i / count * 2 * MathHelper.Pi) / 2 * 0.9f, + 0.5f + (float)Math.Cos((float)i / count * 2 * MathHelper.Pi) / 2 * 0.9f + ), + Rotation = -(float)i / count * 360 + 90, + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + } + } + }); + } + } + } +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 8974b1bcbd..b91bdc6a78 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -64,6 +64,7 @@ + @@ -103,9 +104,7 @@ - - - +