Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid closure allocations when applying hit object results #26694

Merged
merged 9 commits into from
Feb 6, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (timeOffset >= 0)
// todo: implement judgement logic
ApplyResult(r => r.Type = HitResult.Perfect);
ApplyResult(static (r, hitObject) => r.Type = HitResult.Perfect);
}

protected override void UpdateHitStateTransforms(ArmedState state)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,12 @@ public override IEnumerable<HitSampleInfo> GetSamples() => new[]
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (timeOffset >= 0)
ApplyResult(r => r.Type = IsHovered ? HitResult.Perfect : HitResult.Miss);
{
ApplyResult(static (r, hitObject) =>
{
r.Type = hitObject.IsHovered ? HitResult.Perfect : HitResult.Miss;
});
}
}

protected override double InitialLifetimeOffset => time_preempt;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (timeOffset >= 0)
// todo: implement judgement logic
ApplyResult(r => r.Type = HitResult.Perfect);
ApplyResult(static (r, hitObject) => r.Type = HitResult.Perfect);
}

protected override void UpdateHitStateTransforms(ArmedState state)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,13 @@ public override IEnumerable<HitSampleInfo> GetSamples() => new[]
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (timeOffset >= 0)
ApplyResult(r => r.Type = currentLane.Value == HitObject.Lane ? HitResult.Perfect : HitResult.Miss);
{
ApplyResult(static (r, hitObject) =>
{
var pippidonHitObject = (DrawablePippidonHitObject)hitObject;
r.Type = pippidonHitObject.currentLane.Value == pippidonHitObject.HitObject.Lane ? HitResult.Perfect : HitResult.Miss;
});
}
}

protected override void UpdateHitStateTransforms(ArmedState state)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,13 @@ protected override void CheckForResult(bool userTriggered, double timeOffset)
if (CheckPosition == null) return;

if (timeOffset >= 0 && Result != null)
ApplyResult(r => r.Type = CheckPosition.Invoke(HitObject) ? r.Judgement.MaxResult : r.Judgement.MinResult);
{
ApplyResult(static (r, hitObject) =>
{
var catchHitObject = (DrawableCatchHitObject)hitObject;
r.Type = catchHitObject.CheckPosition!.Invoke(catchHitObject.HitObject) ? r.Judgement.MaxResult : r.Judgement.MinResult;
});
}
}

protected override void UpdateHitStateTransforms(ArmedState state)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ protected override void CheckForResult(bool userTriggered, double timeOffset)
if (Tail.AllJudged)
{
if (Tail.IsHit)
ApplyResult(r => r.Type = r.Judgement.MaxResult);
ApplyResult(static (r, _) => r.Type = r.Judgement.MaxResult);
else
MissForcefully();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ public partial class DrawableHoldNoteBody : DrawableManiaHitObject<HoldNoteBody>

public override bool DisplayResult => false;

private bool hit;

public DrawableHoldNoteBody()
: this(null)
{
Expand All @@ -25,7 +27,12 @@ internal void TriggerResult(bool hit)
{
if (AllJudged) return;

ApplyResult(r => r.Type = hit ? r.Judgement.MaxResult : r.Judgement.MinResult);
this.hit = hit;
ApplyResult(static (r, hitObject) =>
{
var holdNoteBody = (DrawableHoldNoteBody)hitObject;
r.Type = holdNoteBody.hit ? r.Judgement.MaxResult : r.Judgement.MinResult;
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ protected override void UpdateHitStateTransforms(ArmedState state)
/// <summary>
/// Causes this <see cref="DrawableManiaHitObject"/> to get missed, disregarding all conditions in implementations of <see cref="DrawableHitObject.CheckForResult"/>.
/// </summary>
public virtual void MissForcefully() => ApplyResult(r => r.Type = r.Judgement.MinResult);
public virtual void MissForcefully() => ApplyResult(static (r, _) => r.Type = r.Judgement.MinResult);
}

public abstract partial class DrawableManiaHitObject<TObject> : DrawableManiaHitObject
Expand Down
16 changes: 11 additions & 5 deletions osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ public partial class DrawableNote : DrawableManiaHitObject<Note>, IKeyBindingHan

private Drawable headPiece;

private HitResult hitResult;

public DrawableNote()
: this(null)
{
Expand Down Expand Up @@ -89,18 +91,22 @@ protected override void CheckForResult(bool userTriggered, double timeOffset)
if (!userTriggered)
{
if (!HitObject.HitWindows.CanBeHit(timeOffset))
ApplyResult(r => r.Type = r.Judgement.MinResult);
ApplyResult(static (r, _) => r.Type = r.Judgement.MinResult);

return;
}

var result = HitObject.HitWindows.ResultFor(timeOffset);
if (result == HitResult.None)
hitResult = HitObject.HitWindows.ResultFor(timeOffset);
if (hitResult == HitResult.None)
return;

result = GetCappedResult(result);
hitResult = GetCappedResult(hitResult);

ApplyResult(r => r.Type = result);
ApplyResult(static (r, hitObject) =>
{
var note = (DrawableNote)hitObject;
r.Type = note.hitResult;
});
}

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ protected override void CheckForResult(bool userTriggered, double timeOffset)
if (auto && !userTriggered && timeOffset > hitOffset && CheckHittable?.Invoke(this, Time.Current, HitResult.Great) == ClickAction.Hit)
{
// force success
ApplyResult(r => r.Type = HitResult.Great);
ApplyResult(static (r, _) => r.Type = HitResult.Great);
}
else
base.CheckForResult(userTriggered, timeOffset);
Expand Down
2 changes: 1 addition & 1 deletion osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLateFade.cs
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ protected override void CheckForResult(bool userTriggered, double timeOffset)
if (shouldHit && !userTriggered && timeOffset >= 0)
{
// force success
ApplyResult(r => r.Type = HitResult.Great);
ApplyResult(static (r, _) => r.Type = HitResult.Great);
}
else
base.CheckForResult(userTriggered, timeOffset);
Expand Down
20 changes: 11 additions & 9 deletions osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public partial class DrawableHitCircle : DrawableOsuHitObject, IHasApproachCircl

private Container scaleContainer;
private InputManager inputManager;
private HitResult hitResult;

public DrawableHitCircle()
: this(null)
Expand Down Expand Up @@ -155,32 +156,33 @@ protected override void CheckForResult(bool userTriggered, double timeOffset)
if (!userTriggered)
{
if (!HitObject.HitWindows.CanBeHit(timeOffset))
ApplyResult(r => r.Type = r.Judgement.MinResult);
ApplyResult(static (r, _) => r.Type = r.Judgement.MinResult);

return;
}

var result = ResultFor(timeOffset);
var clickAction = CheckHittable?.Invoke(this, Time.Current, result);
hitResult = ResultFor(timeOffset);
var clickAction = CheckHittable?.Invoke(this, Time.Current, hitResult);

if (clickAction == ClickAction.Shake)
Shake();

if (result == HitResult.None || clickAction != ClickAction.Hit)
if (hitResult == HitResult.None || clickAction != ClickAction.Hit)
return;

ApplyResult(r =>
ApplyResult(static (r, hitObject) =>
{
var hitCircle = (DrawableHitCircle)hitObject;
var circleResult = (OsuHitCircleJudgementResult)r;

// 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())
if (hitCircle.hitResult.IsHit())
{
var localMousePosition = ToLocalSpace(inputManager.CurrentState.Mouse.Position);
circleResult.CursorPositionAtHit = HitObject.StackedPosition + (localMousePosition - DrawSize / 2);
var localMousePosition = hitCircle.ToLocalSpace(hitCircle.inputManager.CurrentState.Mouse.Position);
circleResult.CursorPositionAtHit = hitCircle.HitObject.StackedPosition + (localMousePosition - hitCircle.DrawSize / 2);
}

circleResult.Type = result;
circleResult.Type = hitCircle.hitResult;
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,12 @@ public virtual void Shake() { }
/// <summary>
/// Causes this <see cref="DrawableOsuHitObject"/> to get hit, disregarding all conditions in implementations of <see cref="DrawableHitObject.CheckForResult"/>.
/// </summary>
public void HitForcefully() => ApplyResult(r => r.Type = r.Judgement.MaxResult);
public void HitForcefully() => ApplyResult(static (r, _) => r.Type = r.Judgement.MaxResult);

/// <summary>
/// Causes this <see cref="DrawableOsuHitObject"/> to get missed, disregarding all conditions in implementations of <see cref="DrawableHitObject.CheckForResult"/>.
/// </summary>
public void MissForcefully() => ApplyResult(r => r.Type = r.Judgement.MinResult);
public void MissForcefully() => ApplyResult(static (r, _) => r.Type = r.Judgement.MinResult);

private RectangleF parentScreenSpaceRectangle => ((DrawableOsuHitObject)ParentHitObject)?.parentScreenSpaceRectangle ?? Parent!.ScreenSpaceDrawQuad.AABBFloat;

Expand Down
11 changes: 7 additions & 4 deletions osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -292,10 +292,10 @@ protected override void CheckForResult(bool userTriggered, double timeOffset)
if (HitObject.ClassicSliderBehaviour)
{
// Classic behaviour means a slider is judged proportionally to the number of nested hitobjects hit. This is the classic osu!stable scoring.
ApplyResult(r =>
ApplyResult(static (r, hitObject) =>
{
int totalTicks = NestedHitObjects.Count;
int hitTicks = NestedHitObjects.Count(h => h.IsHit);
int totalTicks = hitObject.NestedHitObjects.Count;
int hitTicks = hitObject.NestedHitObjects.Count(h => h.IsHit);

if (hitTicks == totalTicks)
r.Type = HitResult.Great;
Expand All @@ -312,7 +312,10 @@ protected override void CheckForResult(bool userTriggered, double timeOffset)
{
// If only the nested hitobjects are judged, then the slider's own judgement is ignored for scoring purposes.
// But the slider needs to still be judged with a reasonable hit/miss result for visual purposes (hit/miss transforms, etc).
ApplyResult(r => r.Type = NestedHitObjects.Any(h => h.Result.IsHit) ? r.Judgement.MaxResult : r.Judgement.MinResult);
ApplyResult(static (r, hitObject) =>
{
r.Type = hitObject.NestedHitObjects.Any(h => h.Result.IsHit) ? r.Judgement.MaxResult : r.Judgement.MinResult;
});
}
}

Expand Down
11 changes: 6 additions & 5 deletions osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -258,15 +258,16 @@ protected override void CheckForResult(bool userTriggered, double timeOffset)
foreach (var tick in ticks.Where(t => !t.Result.HasResult))
tick.TriggerResult(false);

ApplyResult(r =>
ApplyResult(static (r, hitObject) =>
{
if (Progress >= 1)
var spinner = (DrawableSpinner)hitObject;
if (spinner.Progress >= 1)
r.Type = HitResult.Great;
else if (Progress > .9)
else if (spinner.Progress > .9)
r.Type = HitResult.Ok;
else if (Progress > .75)
else if (spinner.Progress > .75)
r.Type = HitResult.Meh;
else if (Time.Current >= HitObject.EndTime)
else if (spinner.Time.Current >= spinner.HitObject.EndTime)
r.Type = r.Judgement.MinResult;
});
}
Expand Down
12 changes: 11 additions & 1 deletion osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinnerTick.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ public partial class DrawableSpinnerTick : DrawableOsuHitObject
{
public override bool DisplayResult => false;

private bool hit;

public DrawableSpinnerTick()
: this(null)
{
Expand All @@ -35,6 +37,14 @@ protected override void OnApply()
/// Apply a judgement result.
/// </summary>
/// <param name="hit">Whether this tick was reached.</param>
internal void TriggerResult(bool hit) => ApplyResult(r => r.Type = hit ? r.Judgement.MaxResult : r.Judgement.MinResult);
internal void TriggerResult(bool hit)
{
this.hit = hit;
ApplyResult(static (r, hitObject) =>
{
var spinnerTick = (DrawableSpinnerTick)hitObject;
r.Type = spinnerTick.hit ? r.Judgement.MaxResult : r.Judgement.MinResult;
});
}
}
}
7 changes: 5 additions & 2 deletions osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ protected override void CheckForResult(bool userTriggered, double timeOffset)
if (timeOffset < 0)
return;

ApplyResult(r => r.Type = r.Judgement.MaxResult);
ApplyResult(static (r, _) => r.Type = r.Judgement.MaxResult);
}

protected override void UpdateHitStateTransforms(ArmedState state)
Expand Down Expand Up @@ -192,7 +192,10 @@ protected override void CheckForResult(bool userTriggered, double timeOffset)
if (!ParentHitObject.Judged)
return;

ApplyResult(r => r.Type = ParentHitObject.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult);
ApplyResult(static (r, hitObject) =>
{
r.Type = hitObject.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult;
});
}

public override bool OnPressed(KeyBindingPressEvent<TaikoAction> e) => false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,22 +49,22 @@ protected override void CheckForResult(bool userTriggered, double timeOffset)
if (!userTriggered)
{
if (timeOffset > HitObject.HitWindow)
ApplyResult(r => r.Type = r.Judgement.MinResult);
ApplyResult(static (r, _) => r.Type = r.Judgement.MinResult);
return;
}

if (Math.Abs(timeOffset) > HitObject.HitWindow)
return;

ApplyResult(r => r.Type = r.Judgement.MaxResult);
ApplyResult(static (r, _) => r.Type = r.Judgement.MaxResult);
}

public override void OnKilled()
{
base.OnKilled();

if (Time.Current > HitObject.GetEndTime() && !Judged)
ApplyResult(r => r.Type = r.Judgement.MinResult);
ApplyResult(static (r, _) => r.Type = r.Judgement.MinResult);
}

protected override void UpdateHitStateTransforms(ArmedState state)
Expand Down Expand Up @@ -105,7 +105,11 @@ protected override void CheckForResult(bool userTriggered, double timeOffset)
if (!ParentHitObject.Judged)
return;

ApplyResult(r => r.Type = ParentHitObject.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult);
ApplyResult(static (r, hitObject) =>
{
var nestedHit = (StrongNestedHit)hitObject;
r.Type = nestedHit.ParentHitObject!.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult;
});
}

public override bool OnPressed(KeyBindingPressEvent<TaikoAction> e) => false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public DrawableFlyingHit(DrawableDrumRollTick drumRollTick)
protected override void LoadComplete()
{
base.LoadComplete();
ApplyResult(r => r.Type = r.Judgement.MaxResult);
ApplyResult(static (r, _) => r.Type = r.Judgement.MaxResult);
}

protected override void LoadSamples()
Expand Down
Loading