From 3e7208c8a48db353182920e25b42ed799039121f Mon Sep 17 00:00:00 2001 From: Damnae Date: Sun, 12 Feb 2017 20:38:05 +0100 Subject: [PATCH 01/12] Add slider ticks. --- .../Objects/Drawables/DrawableSlider.cs | 34 ++++++- .../Objects/Drawables/DrawableSliderTick.cs | 92 +++++++++++++++++++ .../Objects/Drawables/SliderTicksRenderer.cs | 74 +++++++++++++++ osu.Game.Modes.Osu/Objects/Slider.cs | 54 ++++++++++- osu.Game.Modes.Osu/Objects/SliderTick.cs | 9 ++ osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj | 3 + 6 files changed, 260 insertions(+), 6 deletions(-) create mode 100644 osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs create mode 100644 osu.Game.Modes.Osu/Objects/Drawables/SliderTicksRenderer.cs create mode 100644 osu.Game.Modes.Osu/Objects/SliderTick.cs diff --git a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSlider.cs index 611daad64264..981da050a33b 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSlider.cs @@ -1,12 +1,13 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Collections.Generic; +using OpenTK; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Osu.Objects.Drawables.Pieces; -using OpenTK; -using osu.Framework.Input; +using System.Collections.Generic; +using System.Linq; namespace osu.Game.Modes.Osu.Objects.Drawables { @@ -22,6 +23,7 @@ class DrawableSlider : DrawableOsuHitObject SliderBall ball; SliderBouncer bouncer1, bouncer2; + SliderTicksRenderer ticks; public DrawableSlider(Slider s) : base(s) { @@ -34,6 +36,13 @@ public DrawableSlider(Slider s) : base(s) Position = s.StackedPosition, PathWidth = s.Scale * 64, }, + ticks = new SliderTicksRenderer + { + Position = s.StackedPosition, + StartTime = s.StartTime, + RepeatDuration = s.Curve.Length / s.Velocity, + Ticks = s.Ticks, + }, bouncer1 = new SliderBouncer(s, false) { Position = s.Curve.PositionAt(1), @@ -96,6 +105,7 @@ protected override void Update() initialCircle.Position = slider.Curve.PositionAt(progress); components.ForEach(c => c.UpdateProgress(progress, repeat)); + ticks.ShouldHit = ball.Tracking; } protected override void CheckJudgement(bool userTriggered) @@ -105,8 +115,22 @@ protected override void CheckJudgement(bool userTriggered) if (!userTriggered && Time.Current >= HitObject.EndTime) { - j.Score = sc.Score; - j.Result = sc.Result; + var ticksCount = ticks.Children.Count() + 1; + var ticksHit = ticks.Children.Count(t => t.Judgement.Result == HitResult.Hit); + if (sc.Result == HitResult.Hit) + ticksHit++; + + var hitFraction = (double)ticksHit / ticksCount; + if (hitFraction == 1 && sc.Score == OsuScoreResult.Hit300) + j.Score = OsuScoreResult.Hit300; + else if (hitFraction >= 0.5 && sc.Score >= OsuScoreResult.Hit100) + j.Score = OsuScoreResult.Hit100; + else if (hitFraction > 0) + j.Score = OsuScoreResult.Hit50; + else + j.Score = OsuScoreResult.Miss; + + j.Result = j.Score != OsuScoreResult.Miss ? HitResult.Hit : HitResult.Miss; } } diff --git a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs new file mode 100644 index 000000000000..cd5f33a4e6b7 --- /dev/null +++ b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs @@ -0,0 +1,92 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Transformations; +using osu.Game.Modes.Objects.Drawables; +using System; + +namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces +{ + public class DrawableSliderTick : DrawableOsuHitObject + { + private SliderTick sliderTick; + + public double FadeInTime; + public double FadeOutTime; + + public bool ShouldHit; + + public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick) + { + this.sliderTick = sliderTick; + + Size = new Vector2(16) * sliderTick.Scale; + + Masking = true; + CornerRadius = Size.X / 2; + + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + BorderThickness = 2; + BorderColour = Color4.White; + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = sliderTick.Colour, + Alpha = 0.3f, + } + }; + } + + protected override void CheckJudgement(bool userTriggered) + { + if (Judgement.TimeOffset >= 0) + Judgement.Result = ShouldHit ? HitResult.Hit : HitResult.Miss; + } + + protected override void UpdatePreemptState() + { + var animIn = Math.Min(150, sliderTick.StartTime - FadeInTime); + + ScaleTo(0.5f); + ScaleTo(1.2f, animIn); + FadeIn(animIn); + + Delay(animIn); + ScaleTo(1, 150, EasingTypes.Out); + + Delay(-animIn); + } + + protected override void UpdateState(ArmedState state) + { + if (!IsLoaded) return; + + base.UpdateState(state); + + switch (state) + { + case ArmedState.Idle: + Delay(FadeOutTime - sliderTick.StartTime); + FadeOut(); + break; + case ArmedState.Miss: + FadeTo(0.6f); + Delay(FadeOutTime - sliderTick.StartTime); + FadeOut(); + break; + case ArmedState.Hit: + FadeOut(); + break; + } + } + } +} \ No newline at end of file diff --git a/osu.Game.Modes.Osu/Objects/Drawables/SliderTicksRenderer.cs b/osu.Game.Modes.Osu/Objects/Drawables/SliderTicksRenderer.cs new file mode 100644 index 000000000000..82fa99d00a27 --- /dev/null +++ b/osu.Game.Modes.Osu/Objects/Drawables/SliderTicksRenderer.cs @@ -0,0 +1,74 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics.Containers; +using System.Collections.Generic; + +namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces +{ + public class SliderTicksRenderer : Container + { + private double startTime; + public double StartTime + { + get { return startTime; } + set + { + startTime = value; + update(); + } + } + + private double repeatDuration; + public double RepeatDuration + { + get { return repeatDuration; } + set + { + repeatDuration = value; + update(); + } + } + + private IEnumerable ticks; + public IEnumerable Ticks + { + get { return ticks; } + set + { + ticks = value; + update(); + } + } + + public bool ShouldHit + { + set + { + foreach (var tick in Children) + tick.ShouldHit = value; + } + } + + private void update() + { + Clear(); + if (ticks == null || repeatDuration == 0) + return; + + foreach (var tick in ticks) + { + var repeatStartTime = startTime + tick.RepeatIndex * repeatDuration; + var fadeInTime = repeatStartTime + (tick.StartTime - repeatStartTime) / 2 - (tick.RepeatIndex == 0 ? DrawableOsuHitObject.TIME_FADEIN : DrawableOsuHitObject.TIME_FADEIN / 2); + var fadeOutTime = repeatStartTime + repeatDuration; + + Add(new DrawableSliderTick(tick) + { + FadeInTime = fadeInTime, + FadeOutTime = fadeOutTime, + Position = tick.Position, + }); + } + } + } +} \ No newline at end of file diff --git a/osu.Game.Modes.Osu/Objects/Slider.cs b/osu.Game.Modes.Osu/Objects/Slider.cs index 85ee83a7f854..6f97ccf654b5 100644 --- a/osu.Game.Modes.Osu/Objects/Slider.cs +++ b/osu.Game.Modes.Osu/Objects/Slider.cs @@ -3,6 +3,9 @@ using osu.Game.Beatmaps; using OpenTK; +using System.Collections.Generic; +using System; +using osu.Game.Beatmaps.Samples; namespace osu.Game.Modes.Osu.Objects { @@ -25,17 +28,66 @@ public override int StackHeight } public double Velocity; + public double TickDistance; public override void SetDefaultsFromBeatmap(Beatmap beatmap) { base.SetDefaultsFromBeatmap(beatmap); - Velocity = 100 / beatmap.BeatLengthAt(StartTime, true) * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier; + var baseDifficulty = beatmap.BeatmapInfo.BaseDifficulty; + + var startBeatLength = beatmap.BeatLengthAt(StartTime); + var multipliedStartBeatLength = beatmap.BeatLengthAt(StartTime, true); + + Velocity = 100 / multipliedStartBeatLength * baseDifficulty.SliderMultiplier; + TickDistance = (100 * baseDifficulty.SliderMultiplier) / baseDifficulty.SliderTickRate / (multipliedStartBeatLength / startBeatLength); } public int RepeatCount; public SliderCurve Curve; + + public IEnumerable Ticks + { + get + { + var length = Curve.Length; + var tickDistance = Math.Min(TickDistance, length); + var repeatDuration = length / Velocity; + + var minDistanceFromEnd = Velocity * 0.01; + + for (var repeat = 0; repeat < RepeatCount; repeat++) + { + var repeatStartTime = StartTime + repeat * repeatDuration; + var reversed = repeat % 2 == 1; + + for (var d = tickDistance; d <= length; d += tickDistance) + { + if (d > length - minDistanceFromEnd) + break; + + var distanceProgress = d / length; + var timeProgress = reversed ? 1 - distanceProgress : distanceProgress; + + yield return new SliderTick + { + RepeatIndex = repeat, + StartTime = repeatStartTime + timeProgress * repeatDuration, + Position = Curve.PositionAt(distanceProgress) - StackedPosition, + StackHeight = StackHeight, + Scale = Scale, + Colour = Colour, + Sample = new HitSampleInfo + { + Type = SampleType.None, + Set = SampleSet.Soft, + }, + }; + } + } + } + } } public enum CurveTypes diff --git a/osu.Game.Modes.Osu/Objects/SliderTick.cs b/osu.Game.Modes.Osu/Objects/SliderTick.cs new file mode 100644 index 000000000000..de8f3f4b6f12 --- /dev/null +++ b/osu.Game.Modes.Osu/Objects/SliderTick.cs @@ -0,0 +1,9 @@ +using OpenTK; + +namespace osu.Game.Modes.Osu.Objects +{ + public class SliderTick : OsuHitObject + { + public int RepeatIndex { get; set; } + } +} diff --git a/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj b/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj index e0b9f5c90477..19f0df55b8e7 100644 --- a/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj +++ b/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj @@ -47,6 +47,7 @@ + @@ -57,6 +58,7 @@ + @@ -64,6 +66,7 @@ + From 9f890139d4331f5c51560344f85c2d77521c72f1 Mon Sep 17 00:00:00 2001 From: Damnae Date: Tue, 14 Feb 2017 15:16:18 +0100 Subject: [PATCH 02/12] Cache drawable ticks. --- .../Objects/Drawables/SliderTicksRenderer.cs | 54 +++++++++++++------ 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/osu.Game.Modes.Osu/Objects/Drawables/SliderTicksRenderer.cs b/osu.Game.Modes.Osu/Objects/Drawables/SliderTicksRenderer.cs index 82fa99d00a27..03ab28e49546 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/SliderTicksRenderer.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/SliderTicksRenderer.cs @@ -1,6 +1,8 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Allocation; +using osu.Framework.Caching; using osu.Framework.Graphics.Containers; using System.Collections.Generic; @@ -8,6 +10,8 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces { public class SliderTicksRenderer : Container { + private Cached drawableTicks = new Cached(); + private double startTime; public double StartTime { @@ -15,7 +19,7 @@ public double StartTime set { startTime = value; - update(); + drawableTicks.Invalidate(); } } @@ -26,7 +30,7 @@ public double RepeatDuration set { repeatDuration = value; - update(); + drawableTicks.Invalidate(); } } @@ -37,7 +41,7 @@ public IEnumerable Ticks set { ticks = value; - update(); + drawableTicks.Invalidate(); } } @@ -50,25 +54,43 @@ public bool ShouldHit } } - private void update() + protected override void Update() { - Clear(); - if (ticks == null || repeatDuration == 0) + base.Update(); + updateDrawableTicks(); + } + + [BackgroundDependencyLoader] + private void load() + { + updateDrawableTicks(); + } + + private void updateDrawableTicks() + { + if (drawableTicks.EnsureValid()) return; - foreach (var tick in ticks) + drawableTicks.Refresh(delegate { - var repeatStartTime = startTime + tick.RepeatIndex * repeatDuration; - var fadeInTime = repeatStartTime + (tick.StartTime - repeatStartTime) / 2 - (tick.RepeatIndex == 0 ? DrawableOsuHitObject.TIME_FADEIN : DrawableOsuHitObject.TIME_FADEIN / 2); - var fadeOutTime = repeatStartTime + repeatDuration; + Clear(); + if (ticks == null || repeatDuration == 0) + return; - Add(new DrawableSliderTick(tick) + foreach (var tick in ticks) { - FadeInTime = fadeInTime, - FadeOutTime = fadeOutTime, - Position = tick.Position, - }); - } + var repeatStartTime = startTime + tick.RepeatIndex * repeatDuration; + var fadeInTime = repeatStartTime + (tick.StartTime - repeatStartTime) / 2 - (tick.RepeatIndex == 0 ? DrawableOsuHitObject.TIME_FADEIN : DrawableOsuHitObject.TIME_FADEIN / 2); + var fadeOutTime = repeatStartTime + repeatDuration; + + Add(new DrawableSliderTick(tick) + { + FadeInTime = fadeInTime, + FadeOutTime = fadeOutTime, + Position = tick.Position, + }); + } + }); } } } \ No newline at end of file From ace7a1d0096f22dd680e3569c1209a87be909146 Mon Sep 17 00:00:00 2001 From: Damnae Date: Tue, 14 Feb 2017 16:12:35 +0100 Subject: [PATCH 03/12] Fix slider ticks expiring before scoring happens. --- osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs index cd5f33a4e6b7..cb32410303e6 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs @@ -20,6 +20,8 @@ public class DrawableSliderTick : DrawableOsuHitObject public bool ShouldHit; + public override bool RemoveWhenNotAlive => false; + public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick) { this.sliderTick = sliderTick; From b7fca88b4fb5504af28946ca92b48045310ea46e Mon Sep 17 00:00:00 2001 From: Damnae Date: Wed, 15 Feb 2017 18:55:49 +0100 Subject: [PATCH 04/12] Clearer slider Velocity and TickDistance calculations. --- osu.Game.Modes.Osu/Objects/Slider.cs | 11 +++++---- osu.Game/Beatmaps/Beatmap.cs | 35 ++++++++++++++++------------ 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/osu.Game.Modes.Osu/Objects/Slider.cs b/osu.Game.Modes.Osu/Objects/Slider.cs index 30ad1c01804c..f64d443bccb4 100644 --- a/osu.Game.Modes.Osu/Objects/Slider.cs +++ b/osu.Game.Modes.Osu/Objects/Slider.cs @@ -4,6 +4,7 @@ using OpenTK; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Samples; +using osu.Game.Beatmaps.Timing; using System; using System.Collections.Generic; @@ -53,11 +54,13 @@ public override void SetDefaultsFromBeatmap(Beatmap beatmap) var baseDifficulty = beatmap.BeatmapInfo.BaseDifficulty; - var startBeatLength = beatmap.BeatLengthAt(StartTime); - var multipliedStartBeatLength = beatmap.BeatLengthAt(StartTime, true); + ControlPoint overridePoint; + ControlPoint timingPoint = beatmap.TimingPointAt(StartTime, out overridePoint); + var velocityAdjustment = overridePoint?.VelocityAdjustment ?? 1; + var baseVelocity = 100 * baseDifficulty.SliderMultiplier; - Velocity = 100 / multipliedStartBeatLength * baseDifficulty.SliderMultiplier; - TickDistance = (100 * baseDifficulty.SliderMultiplier) / baseDifficulty.SliderTickRate / (multipliedStartBeatLength / startBeatLength); + Velocity = baseVelocity / (timingPoint.BeatLength * velocityAdjustment); + TickDistance = baseVelocity / (baseDifficulty.SliderTickRate * velocityAdjustment); } public int RepeatCount = 1; diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index 0914d1a2ad98..d0ce290bad74 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -26,26 +26,31 @@ public double BPMAt(double time) return 60000 / BeatLengthAt(time); } - public double BeatLengthAt(double time, bool applyMultipliers = false) + public double BeatLengthAt(double time) { - int point = 0; - int samplePoint = 0; + ControlPoint overridePoint; + ControlPoint timingPoint = TimingPointAt(time, out overridePoint); + return timingPoint.BeatLength; + } + + public ControlPoint TimingPointAt(double time, out ControlPoint overridePoint) + { + overridePoint = null; - for (int i = 0; i < ControlPoints.Count; i++) - if (ControlPoints[i].Time <= time) + ControlPoint timingPoint = null; + foreach (var controlPoint in ControlPoints) + if (controlPoint.Time <= time) { - if (ControlPoints[i].TimingChange) - point = i; - else - samplePoint = i; + if (controlPoint.TimingChange) + { + timingPoint = controlPoint; + overridePoint = null; + } + else overridePoint = controlPoint; } + else break; - double mult = 1; - - if (applyMultipliers && samplePoint > point) - mult = ControlPoints[samplePoint].VelocityAdjustment; - - return ControlPoints[point].BeatLength * mult; + return timingPoint; } } } From f1535363f41ead41e7173be7f7d57a9a349d1e15 Mon Sep 17 00:00:00 2001 From: Damnae Date: Wed, 15 Feb 2017 19:04:56 +0100 Subject: [PATCH 05/12] Rename SliderTicksRenderer to SliderTicksLayer. --- .../Drawables/{SliderTicksRenderer.cs => SliderTicksLayer.cs} | 0 osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename osu.Game.Modes.Osu/Objects/Drawables/{SliderTicksRenderer.cs => SliderTicksLayer.cs} (100%) diff --git a/osu.Game.Modes.Osu/Objects/Drawables/SliderTicksRenderer.cs b/osu.Game.Modes.Osu/Objects/Drawables/SliderTicksLayer.cs similarity index 100% rename from osu.Game.Modes.Osu/Objects/Drawables/SliderTicksRenderer.cs rename to osu.Game.Modes.Osu/Objects/Drawables/SliderTicksLayer.cs diff --git a/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj b/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj index d236497ff48e..fd6a45c0654b 100644 --- a/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj +++ b/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj @@ -47,7 +47,7 @@ - + From 6c9fe544b24ecfc1b181a1d4fe583744e81ae970 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Feb 2017 13:20:30 +0900 Subject: [PATCH 06/12] Handle case where TickDistance is zero (don't draw ticks). --- osu.Game.Modes.Osu/Objects/Slider.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Modes.Osu/Objects/Slider.cs b/osu.Game.Modes.Osu/Objects/Slider.cs index f64d443bccb4..72fdd70a7099 100644 --- a/osu.Game.Modes.Osu/Objects/Slider.cs +++ b/osu.Game.Modes.Osu/Objects/Slider.cs @@ -71,6 +71,8 @@ public IEnumerable Ticks { get { + if (TickDistance == 0) yield break; + var length = Curve.Length; var tickDistance = Math.Min(TickDistance, length); var repeatDuration = length / Velocity; From c90f5e247dbc24184ebe0372faabc52e95eb0746 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Feb 2017 13:20:40 +0900 Subject: [PATCH 07/12] Add a TickDistance to TestCaseHitObjects. --- osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs b/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs index 1e3d8df6c69f..6f406307c9d8 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs @@ -75,6 +75,7 @@ private void load(HitObjectType mode) Length = 400, Position = new Vector2(-200, 0), Velocity = 1, + TickDistance = 100, })); break; case HitObjectType.Spinner: From 97b782128eba2077e43a780a88e4051b13231c45 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Feb 2017 13:33:59 +0900 Subject: [PATCH 08/12] Adjust tick appear/disappear animations. --- .../Objects/Drawables/DrawableSliderTick.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs index cb32410303e6..cc6890c9874c 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs @@ -81,12 +81,12 @@ protected override void UpdateState(ArmedState state) FadeOut(); break; case ArmedState.Miss: - FadeTo(0.6f); - Delay(FadeOutTime - sliderTick.StartTime); - FadeOut(); + FadeOut(160); + FadeColour(Color4.Red, 80); break; case ArmedState.Hit: - FadeOut(); + FadeOut(120, EasingTypes.OutQuint); + ScaleTo(Scale * 1.5f, 120, EasingTypes.OutQuint); break; } } From 19eb58e724cfe3231b1521c96aee82d1ecb8d2e7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Feb 2017 13:37:08 +0900 Subject: [PATCH 09/12] Fix namespace. --- .../Objects/Drawables/DrawableSliderTick.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs index cc6890c9874c..9139bce248fa 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs @@ -1,15 +1,15 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using OpenTK; -using OpenTK.Graphics; +using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Transformations; using osu.Game.Modes.Objects.Drawables; -using System; +using OpenTK; +using OpenTK.Graphics; -namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces +namespace osu.Game.Modes.Osu.Objects.Drawables { public class DrawableSliderTick : DrawableOsuHitObject { From 2640c2ac43dd4e69162f2c0a64c7bf2e002815ac Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Feb 2017 13:39:01 +0900 Subject: [PATCH 10/12] Add tick sample. --- .../Objects/Drawables/DrawableSliderTick.cs | 20 +++++++++++++++++++ .../Objects/Drawables/DrawableHitObject.cs | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs index 9139bce248fa..b3479c0fae52 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs @@ -2,9 +2,13 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Transformations; +using osu.Game.Beatmaps.Samples; using osu.Game.Modes.Objects.Drawables; using OpenTK; using OpenTK.Graphics; @@ -48,6 +52,22 @@ public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick) }; } + private AudioSample sample; + + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + string sampleSet = (HitObject.Sample?.Set ?? SampleSet.Normal).ToString().ToLower(); + + sample = audio.Sample.Get($@"Gameplay/{sampleSet}-slidertick"); + } + + protected override void PlaySample() + { + sample?.Play(); + } + + protected override void CheckJudgement(bool userTriggered) { if (Judgement.TimeOffset >= 0) diff --git a/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs index 4df49ca01fdd..4c707c5e62ab 100644 --- a/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs @@ -66,7 +66,7 @@ private void load(AudioManager audio) sample = audio.Sample.Get($@"Gameplay/{sampleSet}-hit{hitType}"); } - protected void PlaySample() + protected virtual void PlaySample() { sample?.Play(); } From 8bf3902cbd0b2a7ffada2df90a3df4afff6cac5a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Feb 2017 17:02:36 +0900 Subject: [PATCH 11/12] Add the concept of nested DrawableHitObjects. - Applies to Slider Ticks and start circle. repeat/endpoints still need addressing. - Removed SliderTicksLayer abstraction for now. --- .../Tests/TestCaseHitObjects.cs | 2 +- .../Objects/Drawables/DrawableOsuHitObject.cs | 2 + .../Objects/Drawables/DrawableSlider.cs | 36 +++++-- .../Objects/Drawables/DrawableSliderTick.cs | 10 +- .../Objects/Drawables/SliderTicksLayer.cs | 96 ------------------- osu.Game.Modes.Osu/Objects/Slider.cs | 2 +- osu.Game.Modes.Osu/OsuScoreProcessor.cs | 4 + osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj | 1 - .../Objects/Drawables/DrawableHitObject.cs | 22 ++++- 9 files changed, 60 insertions(+), 115 deletions(-) delete mode 100644 osu.Game.Modes.Osu/Objects/Drawables/SliderTicksLayer.cs diff --git a/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs b/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs index 6f406307c9d8..f82ea1c98c36 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs @@ -37,7 +37,7 @@ public TestCaseHitObjects() playbackSpeed.ValueChanged += delegate { rateAdjustClock.Rate = playbackSpeed.Value; }; } - HitObjectType mode = HitObjectType.Spinner; + HitObjectType mode = HitObjectType.Slider; BindableNumber playbackSpeed = new BindableDouble(0.5) { MinValue = 0, MaxValue = 1 }; private Container playfieldContainer; diff --git a/osu.Game.Modes.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Modes.Osu/Objects/Drawables/DrawableOsuHitObject.cs index ef153848d46c..d6907474f373 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -73,5 +73,7 @@ public enum OsuScoreResult Hit100, [Description(@"300")] Hit300, + [Description(@"10")] + SliderTick } } diff --git a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSlider.cs index 01ddd7a790b3..745c696feec7 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSlider.cs @@ -7,6 +7,7 @@ using osu.Game.Modes.Osu.Objects.Drawables.Pieces; using System.Collections.Generic; using System.Linq; +using osu.Framework.Graphics.Containers; namespace osu.Game.Modes.Osu.Objects.Drawables { @@ -18,11 +19,12 @@ public class DrawableSlider : DrawableOsuHitObject, IDrawableHitObjectWithProxie private List components = new List(); + private Container ticks; + SliderBody body; SliderBall ball; SliderBouncer bouncer1, bouncer2; - SliderTicksRenderer ticks; public DrawableSlider(Slider s) : base(s) { @@ -35,13 +37,7 @@ public DrawableSlider(Slider s) : base(s) Position = s.StackedPosition, PathWidth = s.Scale * 64, }, - ticks = new SliderTicksRenderer - { - Position = s.StackedPosition, - StartTime = s.StartTime, - RepeatDuration = s.Curve.Length / s.Velocity, - Ticks = s.Ticks, - }, + ticks = new Container(), bouncer1 = new SliderBouncer(s, false) { Position = s.Curve.PositionAt(1), @@ -72,6 +68,26 @@ public DrawableSlider(Slider s) : base(s) components.Add(ball); components.Add(bouncer1); components.Add(bouncer2); + + AddNested(initialCircle); + + var repeatDuration = s.Curve.Length / s.Velocity; + foreach (var tick in s.Ticks) + { + var repeatStartTime = s.StartTime + tick.RepeatIndex * repeatDuration; + var fadeInTime = repeatStartTime + (tick.StartTime - repeatStartTime) / 2 - (tick.RepeatIndex == 0 ? TIME_FADEIN : TIME_FADEIN / 2); + var fadeOutTime = repeatStartTime + repeatDuration; + + var drawableTick = new DrawableSliderTick(tick) + { + FadeInTime = fadeInTime, + FadeOutTime = fadeOutTime, + Position = tick.Position, + }; + + ticks.Add(drawableTick); + AddNested(drawableTick); + } } // Since the DrawableSlider itself is just a container without a size we need to @@ -105,8 +121,8 @@ protected override void Update() if (initialCircle.Judgement?.Result != HitResult.Hit) initialCircle.Position = slider.Curve.PositionAt(progress); - components.ForEach(c => c.UpdateProgress(progress, repeat)); - ticks.ShouldHit = ball.Tracking; + foreach (var c in components) c.UpdateProgress(progress, repeat); + foreach (var t in ticks.Children) t.Tracking = ball.Tracking; } protected override void CheckJudgement(bool userTriggered) diff --git a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs index b3479c0fae52..c1c17f1c53ff 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs @@ -22,7 +22,7 @@ public class DrawableSliderTick : DrawableOsuHitObject public double FadeInTime; public double FadeOutTime; - public bool ShouldHit; + public bool Tracking; public override bool RemoveWhenNotAlive => false; @@ -35,7 +35,6 @@ public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick) Masking = true; CornerRadius = Size.X / 2; - Anchor = Anchor.Centre; Origin = Anchor.Centre; BorderThickness = 2; @@ -70,8 +69,13 @@ protected override void PlaySample() protected override void CheckJudgement(bool userTriggered) { + var j = Judgement as OsuJudgementInfo; + if (Judgement.TimeOffset >= 0) - Judgement.Result = ShouldHit ? HitResult.Hit : HitResult.Miss; + { + j.Result = Tracking ? HitResult.Hit : HitResult.Miss; + j.Score = Tracking ? OsuScoreResult.SliderTick : OsuScoreResult.Miss; + } } protected override void UpdatePreemptState() diff --git a/osu.Game.Modes.Osu/Objects/Drawables/SliderTicksLayer.cs b/osu.Game.Modes.Osu/Objects/Drawables/SliderTicksLayer.cs deleted file mode 100644 index 03ab28e49546..000000000000 --- a/osu.Game.Modes.Osu/Objects/Drawables/SliderTicksLayer.cs +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Framework.Caching; -using osu.Framework.Graphics.Containers; -using System.Collections.Generic; - -namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces -{ - public class SliderTicksRenderer : Container - { - private Cached drawableTicks = new Cached(); - - private double startTime; - public double StartTime - { - get { return startTime; } - set - { - startTime = value; - drawableTicks.Invalidate(); - } - } - - private double repeatDuration; - public double RepeatDuration - { - get { return repeatDuration; } - set - { - repeatDuration = value; - drawableTicks.Invalidate(); - } - } - - private IEnumerable ticks; - public IEnumerable Ticks - { - get { return ticks; } - set - { - ticks = value; - drawableTicks.Invalidate(); - } - } - - public bool ShouldHit - { - set - { - foreach (var tick in Children) - tick.ShouldHit = value; - } - } - - protected override void Update() - { - base.Update(); - updateDrawableTicks(); - } - - [BackgroundDependencyLoader] - private void load() - { - updateDrawableTicks(); - } - - private void updateDrawableTicks() - { - if (drawableTicks.EnsureValid()) - return; - - drawableTicks.Refresh(delegate - { - Clear(); - if (ticks == null || repeatDuration == 0) - return; - - foreach (var tick in ticks) - { - var repeatStartTime = startTime + tick.RepeatIndex * repeatDuration; - var fadeInTime = repeatStartTime + (tick.StartTime - repeatStartTime) / 2 - (tick.RepeatIndex == 0 ? DrawableOsuHitObject.TIME_FADEIN : DrawableOsuHitObject.TIME_FADEIN / 2); - var fadeOutTime = repeatStartTime + repeatDuration; - - Add(new DrawableSliderTick(tick) - { - FadeInTime = fadeInTime, - FadeOutTime = fadeOutTime, - Position = tick.Position, - }); - } - }); - } - } -} \ No newline at end of file diff --git a/osu.Game.Modes.Osu/Objects/Slider.cs b/osu.Game.Modes.Osu/Objects/Slider.cs index 72fdd70a7099..c3d819024083 100644 --- a/osu.Game.Modes.Osu/Objects/Slider.cs +++ b/osu.Game.Modes.Osu/Objects/Slider.cs @@ -96,7 +96,7 @@ public IEnumerable Ticks { RepeatIndex = repeat, StartTime = repeatStartTime + timeProgress * repeatDuration, - Position = Curve.PositionAt(distanceProgress) - StackedPosition, + Position = Curve.PositionAt(distanceProgress), StackHeight = StackHeight, Scale = Scale, Colour = Colour, diff --git a/osu.Game.Modes.Osu/OsuScoreProcessor.cs b/osu.Game.Modes.Osu/OsuScoreProcessor.cs index 8a9f7d6b2e24..224a669746dc 100644 --- a/osu.Game.Modes.Osu/OsuScoreProcessor.cs +++ b/osu.Game.Modes.Osu/OsuScoreProcessor.cs @@ -53,6 +53,10 @@ protected override void UpdateCalculations(JudgementInfo judgement) score += 300; maxScore += 300; break; + case OsuScoreResult.SliderTick: + score += 10; + maxScore += 10; + break; } } diff --git a/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj b/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj index f269921cacfa..1b1ded3d6194 100644 --- a/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj +++ b/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj @@ -47,7 +47,6 @@ - diff --git a/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs index 4c707c5e62ab..94369d51aa8d 100644 --- a/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using osu.Framework; @@ -23,8 +24,6 @@ public abstract class DrawableHitObject : Container, IStateful public bool Interactive = true; - public Container ChildObjects; - public JudgementInfo Judgement; public abstract JudgementInfo CreateJudgementInfo(); @@ -85,6 +84,19 @@ protected override void LoadComplete() Expire(true); } + private List nestedHitObjects; + + protected IEnumerable NestedHitObjects => nestedHitObjects; + + protected void AddNested(DrawableHitObject h) + { + if (nestedHitObjects == null) + nestedHitObjects = new List(); + + h.OnJudgement += (d, j) => { OnJudgement?.Invoke(d, j); } ; + nestedHitObjects.Add(h); + } + /// /// Process a hit of this hitobject. Carries out judgement. /// @@ -119,7 +131,11 @@ protected bool UpdateJudgement(bool userTriggered) protected virtual void CheckJudgement(bool userTriggered) { - //todo: consider making abstract. + if (NestedHitObjects != null) + { + foreach (var d in NestedHitObjects) + d.CheckJudgement(userTriggered); + } } protected override void UpdateAfterChildren() From b55d85a5c51b9ef25b996e4638c48e7b229c7e40 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Feb 2017 17:33:13 +0900 Subject: [PATCH 12/12] Add MaxScore to correctly account for the maximum attainable score of DrawableHitObjects. --- .../Objects/Drawables/DrawableHitCircle.cs | 10 +++--- .../Objects/Drawables/DrawableOsuHitObject.cs | 33 +++++++++++++++++-- .../Objects/Drawables/DrawableSliderTick.cs | 2 ++ osu.Game.Modes.Osu/OsuScoreProcessor.cs | 24 ++------------ 4 files changed, 40 insertions(+), 29 deletions(-) diff --git a/osu.Game.Modes.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Modes.Osu/Objects/Drawables/DrawableHitCircle.cs index 208bf15328e9..0e085d63b872 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -84,18 +84,18 @@ protected override void CheckJudgement(bool userTriggered) double hitOffset = Math.Abs(Judgement.TimeOffset); + OsuJudgementInfo osuJudgement = Judgement as OsuJudgementInfo; + if (hitOffset < hit50) { Judgement.Result = HitResult.Hit; - OsuJudgementInfo osuInfo = Judgement as OsuJudgementInfo; - if (hitOffset < hit300) - osuInfo.Score = OsuScoreResult.Hit300; + osuJudgement.Score = OsuScoreResult.Hit300; else if (hitOffset < hit100) - osuInfo.Score = OsuScoreResult.Hit100; + osuJudgement.Score = OsuScoreResult.Hit100; else if (hitOffset < hit50) - osuInfo.Score = OsuScoreResult.Hit50; + osuJudgement.Score = OsuScoreResult.Hit50; } else Judgement.Result = HitResult.Miss; diff --git a/osu.Game.Modes.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Modes.Osu/Objects/Drawables/DrawableOsuHitObject.cs index d6907474f373..c7cc7cfdd75a 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -4,7 +4,6 @@ using System.ComponentModel; using osu.Game.Modes.Objects; using osu.Game.Modes.Objects.Drawables; -using osu.Framework.Graphics; namespace osu.Game.Modes.Osu.Objects.Drawables { @@ -19,7 +18,7 @@ public DrawableOsuHitObject(OsuHitObject hitObject) { } - public override JudgementInfo CreateJudgementInfo() => new OsuJudgementInfo(); + public override JudgementInfo CreateJudgementInfo() => new OsuJudgementInfo { MaxScore = OsuScoreResult.Hit300 }; protected override void UpdateState(ArmedState state) { @@ -49,7 +48,37 @@ protected virtual void UpdateInitialState() public class OsuJudgementInfo : PositionalJudgementInfo { + /// + /// The score the user achieved. + /// public OsuScoreResult Score; + + /// + /// The score which would be achievable on a perfect hit. + /// + public OsuScoreResult MaxScore = OsuScoreResult.Hit300; + + public int ScoreValue => scoreToInt(Score); + + public int MaxScoreValue => scoreToInt(MaxScore); + + private int scoreToInt(OsuScoreResult result) + { + switch (result) + { + default: + return 0; + case OsuScoreResult.Hit50: + return 50; + case OsuScoreResult.Hit100: + return 100; + case OsuScoreResult.Hit300: + return 300; + case OsuScoreResult.SliderTick: + return 10; + } + } + public ComboResult Combo; } diff --git a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs index c1c17f1c53ff..73808715838d 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs @@ -26,6 +26,8 @@ public class DrawableSliderTick : DrawableOsuHitObject public override bool RemoveWhenNotAlive => false; + public override JudgementInfo CreateJudgementInfo() => new OsuJudgementInfo { MaxScore = OsuScoreResult.SliderTick }; + public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick) { this.sliderTick = sliderTick; diff --git a/osu.Game.Modes.Osu/OsuScoreProcessor.cs b/osu.Game.Modes.Osu/OsuScoreProcessor.cs index 224a669746dc..949325955832 100644 --- a/osu.Game.Modes.Osu/OsuScoreProcessor.cs +++ b/osu.Game.Modes.Osu/OsuScoreProcessor.cs @@ -36,28 +36,8 @@ protected override void UpdateCalculations(JudgementInfo judgement) foreach (OsuJudgementInfo j in Judgements) { - switch (j.Score) - { - case OsuScoreResult.Miss: - maxScore += 300; - break; - case OsuScoreResult.Hit50: - score += 50; - maxScore += 300; - break; - case OsuScoreResult.Hit100: - score += 100; - maxScore += 300; - break; - case OsuScoreResult.Hit300: - score += 300; - maxScore += 300; - break; - case OsuScoreResult.SliderTick: - score += 10; - maxScore += 10; - break; - } + score += j.ScoreValue; + maxScore += j.MaxScoreValue; } TotalScore.Value = score;