From 5850d6a57807aee271aae79532964bc85d345e1a Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 6 Feb 2024 20:06:51 +0900 Subject: [PATCH 1/8] Show near-misses on the results-screen heatmap --- osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs | 7 +- .../Objects/Drawables/DrawableHitCircle.cs | 74 +++++++++++-------- .../Statistics/AccuracyHeatmap.cs | 60 +++++++++------ 3 files changed, 81 insertions(+), 60 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs b/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs index 10d7af5e58..0e3f972d41 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs @@ -95,12 +95,7 @@ namespace osu.Game.Rulesets.Osu.Mods /// private static void blockInputToObjectsUnderSliderHead(DrawableSliderHead slider) { - var oldHitAction = slider.HitArea.Hit; - slider.HitArea.Hit = () => - { - oldHitAction?.Invoke(); - return !slider.DrawableSlider.AllJudged; - }; + slider.HitArea.CanBeHit = () => !slider.DrawableSlider.AllJudged; } private void applyEarlyFading(DrawableHitCircle circle) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index b1c9bef6c4..3727e78d01 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -10,7 +10,6 @@ using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Game.Graphics.Containers; @@ -43,7 +42,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Drawable IHasApproachCircle.ApproachCircle => ApproachCircle; private Container scaleContainer; - private InputManager inputManager; public DrawableHitCircle() : this(null) @@ -73,14 +71,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { HitArea = new HitReceptor { - Hit = () => - { - if (AllJudged) - return false; - - UpdateResult(true); - return true; - }, + CanBeHit = () => !AllJudged, + Hit = () => UpdateResult(true) }, shakeContainer = new ShakeContainer { @@ -114,13 +106,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables ScaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue)); } - protected override void LoadComplete() - { - base.LoadComplete(); - - inputManager = GetContainingInputManager(); - } - public override double LifetimeStart { get => base.LifetimeStart; @@ -155,7 +140,15 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables if (!userTriggered) { if (!HitObject.HitWindows.CanBeHit(timeOffset)) - ApplyMinResult(); + { + ApplyResult((r, position) => + { + var circleResult = (OsuHitCircleJudgementResult)r; + + circleResult.Type = r.Judgement.MinResult; + circleResult.CursorPositionAtHit = position; + }, computeHitPosition()); + } return; } @@ -169,22 +162,21 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables if (result == HitResult.None || clickAction != ClickAction.Hit) return; - Vector2? hitPosition = null; - - // Todo: This should also consider misses, but they're a little more interesting to handle, since we don't necessarily know the position at the time of a miss. - if (result.IsHit()) - { - var localMousePosition = ToLocalSpace(inputManager.CurrentState.Mouse.Position); - hitPosition = HitObject.StackedPosition + (localMousePosition - DrawSize / 2); - } - ApplyResult<(HitResult result, Vector2? position)>((r, state) => { var circleResult = (OsuHitCircleJudgementResult)r; circleResult.Type = state.result; circleResult.CursorPositionAtHit = state.position; - }, (result, hitPosition)); + }, (result, computeHitPosition())); + } + + private Vector2? computeHitPosition() + { + if (HitArea.ClosestPressPosition is Vector2 screenSpaceHitPosition) + return HitObject.StackedPosition + (ToLocalSpace(screenSpaceHitPosition) - DrawSize / 2); + + return null; } /// @@ -227,6 +219,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables break; case ArmedState.Idle: + HitArea.ClosestPressPosition = null; HitArea.HitAction = null; break; @@ -247,9 +240,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables // IsHovered is used public override bool HandlePositionalInput => true; - public Func Hit; + public Func CanBeHit; + public Action Hit; public OsuAction? HitAction; + public Vector2? ClosestPressPosition; public HitReceptor() { @@ -264,12 +259,31 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public bool OnPressed(KeyBindingPressEvent e) { + if (!(CanBeHit?.Invoke() ?? false)) + return false; + switch (e.Action) { case OsuAction.LeftButton: case OsuAction.RightButton: - if (IsHovered && (Hit?.Invoke() ?? false)) + // Only update closest press position while the object hasn't been hit yet. + if (HitAction == null) { + if (ClosestPressPosition is Vector2 curClosest) + { + float oldDist = Vector2.DistanceSquared(curClosest, ScreenSpaceDrawQuad.Centre); + float newDist = Vector2.DistanceSquared(e.ScreenSpaceMousePosition, ScreenSpaceDrawQuad.Centre); + + if (newDist < oldDist) + ClosestPressPosition = e.ScreenSpaceMousePosition; + } + else + ClosestPressPosition = e.ScreenSpaceMousePosition; + } + + if (IsHovered) + { + Hit?.Invoke(); HitAction ??= e.Action; return true; } diff --git a/osu.Game.Rulesets.Osu/Statistics/AccuracyHeatmap.cs b/osu.Game.Rulesets.Osu/Statistics/AccuracyHeatmap.cs index f9d4a3b325..813f3b2e7a 100644 --- a/osu.Game.Rulesets.Osu/Statistics/AccuracyHeatmap.cs +++ b/osu.Game.Rulesets.Osu/Statistics/AccuracyHeatmap.cs @@ -197,7 +197,9 @@ namespace osu.Game.Rulesets.Osu.Statistics var point = new HitPoint(pointType, this) { - BaseColour = pointType == HitPointType.Hit ? new Color4(102, 255, 204, 255) : new Color4(255, 102, 102, 255) + BaseColour = pointType == HitPointType.Hit + ? new Color4(102, 255, 204, 255) + : new Color4(255, 102, 102, 255) }; points[r][c] = point; @@ -250,12 +252,15 @@ namespace osu.Game.Rulesets.Osu.Statistics var rotatedCoordinate = -1 * new Vector2((float)Math.Cos(rotatedAngle), (float)Math.Sin(rotatedAngle)); Vector2 localCentre = new Vector2(points_per_dimension - 1) / 2; - float localRadius = localCentre.X * inner_portion * normalisedDistance; // The radius inside the inner portion which of the heatmap which the closest point lies. + float localRadius = localCentre.X * inner_portion * normalisedDistance; Vector2 localPoint = localCentre + localRadius * rotatedCoordinate; // Find the most relevant hit point. - int r = Math.Clamp((int)Math.Round(localPoint.Y), 0, points_per_dimension - 1); - int c = Math.Clamp((int)Math.Round(localPoint.X), 0, points_per_dimension - 1); + int r = (int)Math.Round(localPoint.Y); + int c = (int)Math.Round(localPoint.X); + + if (r < 0 || r >= points_per_dimension || c < 0 || c >= points_per_dimension) + return; PeakValue = Math.Max(PeakValue, ((HitPoint)pointGrid.Content[r][c]).Increment()); @@ -298,28 +303,35 @@ namespace osu.Game.Rulesets.Osu.Statistics { base.Update(); - // the point at which alpha is saturated and we begin to adjust colour lightness. - const float lighten_cutoff = 0.95f; - - // the amount of lightness to attribute regardless of relative value to peak point. - const float non_relative_portion = 0.2f; - - float amount = 0; - - // give some amount of alpha regardless of relative count - amount += non_relative_portion * Math.Min(1, count / 10f); - - // add relative portion - amount += (1 - non_relative_portion) * (count / heatmap.PeakValue); - - // apply easing - amount = (float)Interpolation.ApplyEasing(Easing.OutQuint, Math.Min(1, amount)); - - Debug.Assert(amount <= 1); - - Alpha = Math.Min(amount / lighten_cutoff, 1); if (pointType == HitPointType.Hit) + { + // the point at which alpha is saturated and we begin to adjust colour lightness. + const float lighten_cutoff = 0.95f; + + // the amount of lightness to attribute regardless of relative value to peak point. + const float non_relative_portion = 0.2f; + + float amount = 0; + + // give some amount of alpha regardless of relative count + amount += non_relative_portion * Math.Min(1, count / 10f); + + // add relative portion + amount += (1 - non_relative_portion) * (count / heatmap.PeakValue); + + // apply easing + amount = (float)Interpolation.ApplyEasing(Easing.OutQuint, Math.Min(1, amount)); + + Debug.Assert(amount <= 1); + + Alpha = Math.Min(amount / lighten_cutoff, 1); Colour = BaseColour.Lighten(Math.Max(0, amount - lighten_cutoff)); + } + else + { + Alpha = 0.8f; + Colour = BaseColour; + } } } From 99d716f8fc62aaaf09ca65fa7da6f8f876bd6b8a Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 6 Feb 2024 22:23:30 +0900 Subject: [PATCH 2/8] Make miss points into crosses --- .../Statistics/AccuracyHeatmap.cs | 123 ++++++++++-------- 1 file changed, 68 insertions(+), 55 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Statistics/AccuracyHeatmap.cs b/osu.Game.Rulesets.Osu/Statistics/AccuracyHeatmap.cs index 813f3b2e7a..2120b929a7 100644 --- a/osu.Game.Rulesets.Osu/Statistics/AccuracyHeatmap.cs +++ b/osu.Game.Rulesets.Osu/Statistics/AccuracyHeatmap.cs @@ -9,6 +9,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Graphics; @@ -191,18 +192,22 @@ namespace osu.Game.Rulesets.Osu.Statistics for (int c = 0; c < points_per_dimension; c++) { - HitPointType pointType = Vector2.Distance(new Vector2(c + 0.5f, r + 0.5f), centre) <= innerRadius - ? HitPointType.Hit - : HitPointType.Miss; + bool isHit = Vector2.Distance(new Vector2(c + 0.5f, r + 0.5f), centre) <= innerRadius; - var point = new HitPoint(pointType, this) + if (isHit) { - BaseColour = pointType == HitPointType.Hit - ? new Color4(102, 255, 204, 255) - : new Color4(255, 102, 102, 255) - }; - - points[r][c] = point; + points[r][c] = new HitPoint(this) + { + BaseColour = new Color4(102, 255, 204, 255) + }; + } + else + { + points[r][c] = new MissPoint + { + BaseColour = new Color4(255, 102, 102, 255) + }; + } } } @@ -262,33 +267,21 @@ namespace osu.Game.Rulesets.Osu.Statistics if (r < 0 || r >= points_per_dimension || c < 0 || c >= points_per_dimension) return; - PeakValue = Math.Max(PeakValue, ((HitPoint)pointGrid.Content[r][c]).Increment()); + PeakValue = Math.Max(PeakValue, ((GridPoint)pointGrid.Content[r][c]).Increment()); bufferedGrid.ForceRedraw(); } - private partial class HitPoint : Circle + private abstract partial class GridPoint : CompositeDrawable { /// /// The base colour which will be lightened/darkened depending on the value of this . /// public Color4 BaseColour; - private readonly HitPointType pointType; - private readonly AccuracyHeatmap heatmap; + public override bool IsPresent => Count > 0; - public override bool IsPresent => count > 0; - - public HitPoint(HitPointType pointType, AccuracyHeatmap heatmap) - { - this.pointType = pointType; - this.heatmap = heatmap; - - RelativeSizeAxes = Axes.Both; - Alpha = 1; - } - - private int count; + protected int Count { get; private set; } /// /// Increment the value of this point by one. @@ -296,49 +289,69 @@ namespace osu.Game.Rulesets.Osu.Statistics /// The value after incrementing. public int Increment() { - return ++count; + return ++Count; + } + } + + private partial class MissPoint : GridPoint + { + public MissPoint() + { + RelativeSizeAxes = Axes.Both; + + InternalChild = new SpriteIcon + { + RelativeSizeAxes = Axes.Both, + Icon = FontAwesome.Solid.Times + }; + } + + protected override void Update() + { + Alpha = 0.8f; + Colour = BaseColour; + } + } + + private partial class HitPoint : GridPoint + { + private readonly AccuracyHeatmap heatmap; + + public HitPoint(AccuracyHeatmap heatmap) + { + this.heatmap = heatmap; + + RelativeSizeAxes = Axes.Both; + + InternalChild = new Circle { RelativeSizeAxes = Axes.Both }; } protected override void Update() { base.Update(); - if (pointType == HitPointType.Hit) - { - // the point at which alpha is saturated and we begin to adjust colour lightness. - const float lighten_cutoff = 0.95f; + // the point at which alpha is saturated and we begin to adjust colour lightness. + const float lighten_cutoff = 0.95f; - // the amount of lightness to attribute regardless of relative value to peak point. - const float non_relative_portion = 0.2f; + // the amount of lightness to attribute regardless of relative value to peak point. + const float non_relative_portion = 0.2f; - float amount = 0; + float amount = 0; - // give some amount of alpha regardless of relative count - amount += non_relative_portion * Math.Min(1, count / 10f); + // give some amount of alpha regardless of relative count + amount += non_relative_portion * Math.Min(1, Count / 10f); - // add relative portion - amount += (1 - non_relative_portion) * (count / heatmap.PeakValue); + // add relative portion + amount += (1 - non_relative_portion) * (Count / heatmap.PeakValue); - // apply easing - amount = (float)Interpolation.ApplyEasing(Easing.OutQuint, Math.Min(1, amount)); + // apply easing + amount = (float)Interpolation.ApplyEasing(Easing.OutQuint, Math.Min(1, amount)); - Debug.Assert(amount <= 1); + Debug.Assert(amount <= 1); - Alpha = Math.Min(amount / lighten_cutoff, 1); - Colour = BaseColour.Lighten(Math.Max(0, amount - lighten_cutoff)); - } - else - { - Alpha = 0.8f; - Colour = BaseColour; - } + Alpha = Math.Min(amount / lighten_cutoff, 1); + Colour = BaseColour.Lighten(Math.Max(0, amount - lighten_cutoff)); } } - - private enum HitPointType - { - Hit, - Miss - } } } From 44b1515cc5395486963615d238f2f77c16b5888c Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 7 Feb 2024 03:28:40 +0900 Subject: [PATCH 3/8] Remove LangVersion redefintions --- osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj | 1 - osu.Game/osu.Game.csproj | 1 - 2 files changed, 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 518ab362ca..7817d55f57 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -4,7 +4,6 @@ Library true click the circles. to the beat. - 10 diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 935b759e4d..9bcdebc347 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -3,7 +3,6 @@ net8.0 Library true - 10 osu! From 9b8f2064867472009e3c5c621c1fbf5823ad7592 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 7 Feb 2024 03:38:07 +0900 Subject: [PATCH 4/8] Enable NRT for DrawableHitCircle to clean up --- .../Objects/Drawables/DrawableHitCircle.cs | 46 +++++++++---------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index 3727e78d01..b950ef4bbb 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -1,12 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Collections.Generic; using System.Diagnostics; -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -27,34 +24,30 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { public partial class DrawableHitCircle : DrawableOsuHitObject, IHasApproachCircle { - public OsuAction? HitAction => HitArea?.HitAction; + public OsuAction? HitAction => HitArea.HitAction; protected virtual OsuSkinComponents CirclePieceComponent => OsuSkinComponents.HitCircle; - public SkinnableDrawable ApproachCircle { get; private set; } - public HitReceptor HitArea { get; private set; } - public SkinnableDrawable CirclePiece { get; private set; } + public SkinnableDrawable ApproachCircle { get; private set; } = null!; + public HitReceptor HitArea { get; private set; } = null!; + public SkinnableDrawable CirclePiece { get; private set; } = null!; - protected override IEnumerable DimmablePieces => new[] - { - CirclePiece, - }; + protected override IEnumerable DimmablePieces => new[] { CirclePiece }; Drawable IHasApproachCircle.ApproachCircle => ApproachCircle; - private Container scaleContainer; + private Container scaleContainer = null!; + private ShakeContainer shakeContainer = null!; public DrawableHitCircle() : this(null) { } - public DrawableHitCircle([CanBeNull] HitCircle h = null) + public DrawableHitCircle(HitCircle? h = null) : base(h) { } - private ShakeContainer shakeContainer; - [BackgroundDependencyLoader] private void load() { @@ -219,8 +212,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables break; case ArmedState.Idle: - HitArea.ClosestPressPosition = null; - HitArea.HitAction = null; + HitArea.Reset(); break; case ArmedState.Miss: @@ -240,11 +232,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables // IsHovered is used public override bool HandlePositionalInput => true; - public Func CanBeHit; - public Action Hit; + public required Func CanBeHit { get; set; } + public required Action Hit { get; set; } - public OsuAction? HitAction; - public Vector2? ClosestPressPosition; + public OsuAction? HitAction { get; private set; } + public Vector2? ClosestPressPosition { get; private set; } public HitReceptor() { @@ -259,7 +251,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public bool OnPressed(KeyBindingPressEvent e) { - if (!(CanBeHit?.Invoke() ?? false)) + if (CanBeHit()) return false; switch (e.Action) @@ -283,7 +275,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables if (IsHovered) { - Hit?.Invoke(); + Hit(); HitAction ??= e.Action; return true; } @@ -297,13 +289,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public void OnReleased(KeyBindingReleaseEvent e) { } + + public void Reset() + { + HitAction = null; + ClosestPressPosition = null; + } } private partial class ProxyableSkinnableDrawable : SkinnableDrawable { public override bool RemoveWhenNotAlive => false; - public ProxyableSkinnableDrawable(ISkinComponentLookup lookup, Func defaultImplementation = null, ConfineMode confineMode = ConfineMode.NoScaling) + public ProxyableSkinnableDrawable(ISkinComponentLookup lookup, Func? defaultImplementation = null, ConfineMode confineMode = ConfineMode.NoScaling) : base(lookup, defaultImplementation, confineMode) { } From 38f7913b31bbc64f99882668b7d8cb89de4d51fd Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 7 Feb 2024 03:50:56 +0900 Subject: [PATCH 5/8] Fix inverted condition --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index b950ef4bbb..c9f2983b1e 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -251,7 +251,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public bool OnPressed(KeyBindingPressEvent e) { - if (CanBeHit()) + if (!CanBeHit()) return false; switch (e.Action) From 2fc06f16b50c0112522b3c2f164de6aa2aa36608 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 7 Feb 2024 03:51:28 +0900 Subject: [PATCH 6/8] Fix inspections --- osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs index c624fbbe73..9d79cb0db4 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs @@ -91,11 +91,11 @@ namespace osu.Game.Rulesets.Osu.Tests var skinnable = firstObject.ApproachCircle; - if (skin == null && skinnable?.Drawable is DefaultApproachCircle) + if (skin == null && skinnable.Drawable is DefaultApproachCircle) // check for default skin provider return true; - var text = skinnable?.Drawable as SpriteText; + var text = skinnable.Drawable as SpriteText; return text?.Text == skin; }); From e2867986c591f042b63c6da36e1f27d40cb89341 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 7 Feb 2024 03:47:36 +0900 Subject: [PATCH 7/8] Add xmldocs --- .../Objects/Drawables/DrawableHitCircle.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index c9f2983b1e..f7237d4c03 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -232,10 +232,24 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables // IsHovered is used public override bool HandlePositionalInput => true; + /// + /// Whether the hitobject can still be hit at the current point in time. + /// public required Func CanBeHit { get; set; } + + /// + /// An action that's invoked to perform the hit. + /// public required Action Hit { get; set; } + /// + /// The with which the hit was attempted. + /// public OsuAction? HitAction { get; private set; } + + /// + /// The closest position to the hit receptor at the point where the hit was attempted. + /// public Vector2? ClosestPressPosition { get; private set; } public HitReceptor() @@ -290,6 +304,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { } + /// + /// Resets to a fresh state. + /// public void Reset() { HitAction = null; From 1f13124b3834b846281c5d63acc65275c356258e Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 7 Feb 2024 03:52:51 +0900 Subject: [PATCH 8/8] Always process position as long as it's hittable For example... If a hitobject is pressed but the result is a shake, this will stop processing hits. --- .../Objects/Drawables/DrawableHitCircle.cs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index f7237d4c03..c3ce6acce9 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -272,20 +272,16 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { case OsuAction.LeftButton: case OsuAction.RightButton: - // Only update closest press position while the object hasn't been hit yet. - if (HitAction == null) + if (ClosestPressPosition is Vector2 curClosest) { - if (ClosestPressPosition is Vector2 curClosest) - { - float oldDist = Vector2.DistanceSquared(curClosest, ScreenSpaceDrawQuad.Centre); - float newDist = Vector2.DistanceSquared(e.ScreenSpaceMousePosition, ScreenSpaceDrawQuad.Centre); + float oldDist = Vector2.DistanceSquared(curClosest, ScreenSpaceDrawQuad.Centre); + float newDist = Vector2.DistanceSquared(e.ScreenSpaceMousePosition, ScreenSpaceDrawQuad.Centre); - if (newDist < oldDist) - ClosestPressPosition = e.ScreenSpaceMousePosition; - } - else + if (newDist < oldDist) ClosestPressPosition = e.ScreenSpaceMousePosition; } + else + ClosestPressPosition = e.ScreenSpaceMousePosition; if (IsHovered) {