1
0
mirror of https://github.com/ppy/osu.git synced 2024-09-21 18:07:23 +08:00

Use the cursor position to test nested object validity

This commit is contained in:
Dan Balasescu 2023-12-15 17:05:14 +09:00
parent 3b8a73bf2c
commit 12210017e4
No known key found for this signature in database
2 changed files with 46 additions and 10 deletions

View File

@ -184,7 +184,7 @@ namespace osu.Game.Rulesets.Osu.Tests
/// <summary>
/// If the head circle is hit late and the mouse is in range of the follow circle,
/// then all the repeats that the mouse has passed through should be hit.
/// then all the repeats that the follow circle has passed through should be hit.
/// </summary>
[Test]
public void TestHitLateInRangeHitsRepeat()
@ -212,8 +212,8 @@ namespace osu.Game.Rulesets.Osu.Tests
/// <summary>
/// If the head circle is hit and the mouse is in range of the follow circle,
/// then only the ticks that were in range of the follow circle at the head should be hit.
/// If any hitobject was outside the follow range, ALL hitobjects after that point should be missed.
/// then only the ticks that are in range of the cursor position should be hit.
/// If any hitobject does not meet this criteria, ALL hitobjects after that one should be missed.
/// </summary>
[Test]
public void TestHitLateInRangeDoesNotHitAfterAnyOutOfRange()
@ -257,7 +257,7 @@ namespace osu.Game.Rulesets.Osu.Tests
assertTickJudgement(11, HitResult.LargeTickMiss);
assertTickJudgement(12, HitResult.LargeTickMiss);
// And the tail should be hit because of its leniency.
// This particular test actually starts tracking the slider just before the end, so the tail should be hit because of its leniency.
assertTailJudgement(HitResult.LargeTickHit);
assertSliderJudgement(HitResult.IgnoreHit);
@ -265,10 +265,10 @@ namespace osu.Game.Rulesets.Osu.Tests
/// <summary>
/// If the head circle is hit and the mouse is in range of the follow circle,
/// then a tick outside the range of the follow circle from the head should not be hit.
/// then a tick not within the follow radius from the cursor position should not be hit.
/// </summary>
[Test]
public void TestHitLateInRangeDoesNotHitOutOfRange()
public void TestHitLateInRangeDoesNotHitOutOfRangeTick()
{
performTest(new List<ReplayFrame>
{
@ -293,6 +293,36 @@ namespace osu.Game.Rulesets.Osu.Tests
assertSliderJudgement(HitResult.IgnoreHit);
}
/// <summary>
/// If the head circle is hit and the mouse is in range of the follow circle,
/// then a tick not within the follow radius from the cursor position should not be hit.
/// </summary>
[Test]
public void TestHitLateWithEdgeHit()
{
performTest(new List<ReplayFrame>
{
new OsuReplayFrame(time_slider_start + 150, slider_start_position - new Vector2(20), OsuAction.LeftButton),
new OsuReplayFrame(time_slider_end + 150, slider_start_position - new Vector2(20), OsuAction.LeftButton),
}, s =>
{
s.Path = new SliderPath(PathType.PERFECT_CURVE, new[]
{
Vector2.Zero,
new Vector2(50, 50),
new Vector2(20, 0),
});
s.TickDistanceMultiplier = 0.35f;
s.SliderVelocityMultiplier = 4;
});
assertHeadJudgement(HitResult.Meh);
assertTickJudgement(0, HitResult.LargeTickMiss);
assertTailJudgement(HitResult.IgnoreMiss);
assertSliderJudgement(HitResult.IgnoreHit);
}
private void assertHeadJudgement(HitResult result)
{
AddAssert(

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Input;
@ -75,8 +76,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
if (!isMouseInFollowArea(true))
return;
Debug.Assert(screenSpaceMousePosition != null);
Vector2 mousePositionInSlider = slider.ToLocalSpace(screenSpaceMousePosition.Value) - slider.OriginPosition;
// When the head is hit and the mouse is in the expanded follow area, force a hit on every nested hitobject
// from the start of the slider that is within follow-radius units from the head.
// from the start of the slider that is within the follow area.
bool forceMiss = false;
@ -94,9 +99,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
double objectProgress = Math.Clamp((nested.HitObject.StartTime - slider.HitObject.StartTime) / slider.HitObject.Duration, 0, 1);
Vector2 objectPosition = slider.HitObject.CurvePositionAt(objectProgress);
// When the first nested object that is further than follow-radius units away from the start of the slider is reached,
// forcefully miss all other nested objects that would otherwise be valid to be hit by this process.
if (forceMiss || objectPosition.LengthSquared > radius * radius)
// When the first nested object that is further outside the follow area is reached,
// forcefully miss all other nested objects that would otherwise be valid to be hit.
// This covers a case of a slider overlapping itself that requires tracking to a tick on an outer edge.
if (forceMiss || (objectPosition - mousePositionInSlider).LengthSquared > radius * radius)
{
nested.MissForcefully();
forceMiss = true;