-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
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
Add Gameplay Accuracy Heatmap #31158
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
@@ -0,0 +1,76 @@ | ||||
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence. | ||||
// See the LICENCE file in the repository root for full licence text. | ||||
|
||||
using osu.Framework.Graphics.Containers; | ||||
using osu.Game.Skinning; | ||||
using osu.Game.Rulesets.Osu.Statistics; | ||||
using osu.Game.Scoring; | ||||
using osu.Framework.Bindables; | ||||
using osu.Game.Beatmaps; | ||||
using osu.Framework.Allocation; | ||||
using osu.Framework.Graphics; | ||||
using osu.Game.Rulesets.Scoring; | ||||
using System.Collections.Generic; | ||||
using osu.Game.Rulesets.Osu.Objects; | ||||
using osu.Game.Rulesets.Objects.Legacy; | ||||
using osu.Game.Screens.Play; | ||||
using osu.Game.Rulesets.Judgements; | ||||
using osu.Game.Rulesets.Osu.Judgements; | ||||
|
||||
namespace osu.Game.Rulesets.Osu.Skinning | ||||
{ | ||||
public partial class GameplayAccuracyHeatmap : Container, ISerialisableDrawable | ||||
{ | ||||
[Resolved] | ||||
private IBindable<WorkingBeatmap> beatmap { get; set; } = null!; | ||||
|
||||
[Resolved] | ||||
private ScoreProcessor scoreProcessor { get; set; } = null!; | ||||
|
||||
[Resolved(canBeNull: true)] | ||||
private GameplayClockContainer? gameplayClockContainer { get; set; } | ||||
|
||||
public bool UsesFixedAnchor { get; set; } | ||||
|
||||
private float radius; | ||||
private AccuracyHeatmap heatmap = null!; | ||||
|
||||
protected override void LoadComplete() | ||||
{ | ||||
base.LoadComplete(); | ||||
|
||||
AutoSizeAxes = Axes.Both; | ||||
|
||||
radius = OsuHitObject.OBJECT_RADIUS * LegacyRulesetExtensions.CalculateScaleFromCircleSize(beatmap.Value.Beatmap.Difficulty.CircleSize, true); | ||||
|
||||
initHeatmap(); | ||||
|
||||
if (gameplayClockContainer != null) | ||||
gameplayClockContainer.OnSeek += initHeatmap; | ||||
|
||||
scoreProcessor.NewJudgement += updateHeatmap; | ||||
} | ||||
|
||||
private void initHeatmap() | ||||
{ | ||||
ScoreInfo scoreInfo = new ScoreInfo { BeatmapInfo = beatmap.Value.BeatmapInfo, HitEvents = (List<HitEvent>)scoreProcessor.HitEvents }; | ||||
Child = new Container | ||||
{ | ||||
Height = 200, | ||||
Width = 200, | ||||
Child = heatmap = new AccuracyHeatmap(scoreInfo, beatmap.Value.Beatmap, false) | ||||
{ | ||||
RelativeSizeAxes = Axes.Both | ||||
} | ||||
}; | ||||
} | ||||
|
||||
private void updateHeatmap(JudgementResult j) | ||||
{ | ||||
if (j is not OsuHitCircleJudgementResult circleJudgementResult || circleJudgementResult.CursorPositionAtHit == null) | ||||
return; | ||||
|
||||
heatmap.AddPoint(((OsuHitObject)j.HitObject).StackedEndPosition, ((OsuHitObject)j.HitObject).StackedEndPosition, circleJudgementResult.CursorPositionAtHit.Value, radius); | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why is this using
|
||||
} | ||||
} | ||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -39,22 +39,25 @@ public partial class AccuracyHeatmap : CompositeDrawable | |
private const float rotation = 45; | ||
|
||
private BufferedContainer bufferedGrid = null!; | ||
private GridContainer pointGrid = null!; | ||
private GridContainer? pointGrid; | ||
|
||
private readonly ScoreInfo score; | ||
private readonly IBeatmap playableBeatmap; | ||
|
||
private const float line_thickness = 2; | ||
|
||
private readonly bool showsLabel; | ||
|
||
/// <summary> | ||
/// The highest count of any point currently being displayed. | ||
/// </summary> | ||
protected float PeakValue { get; private set; } | ||
|
||
public AccuracyHeatmap(ScoreInfo score, IBeatmap playableBeatmap) | ||
public AccuracyHeatmap(ScoreInfo score, IBeatmap playableBeatmap, bool showsLabel = true) | ||
{ | ||
this.score = score; | ||
this.playableBeatmap = playableBeatmap; | ||
this.showsLabel = showsLabel; | ||
} | ||
|
||
[BackgroundDependencyLoader] | ||
|
@@ -123,6 +126,7 @@ private void load() | |
new OsuSpriteText | ||
{ | ||
Text = "Overshoot", | ||
Alpha = showsLabel ? 1 : 0, | ||
Font = OsuFont.GetFont(size: 12), | ||
Anchor = Anchor.Centre, | ||
Origin = Anchor.BottomLeft, | ||
|
@@ -134,6 +138,7 @@ private void load() | |
new OsuSpriteText | ||
{ | ||
Text = "Undershoot", | ||
Alpha = showsLabel ? 1 : 0, | ||
Font = OsuFont.GetFont(size: 12), | ||
Anchor = Anchor.Centre, | ||
Origin = Anchor.TopRight, | ||
|
@@ -227,9 +232,9 @@ private void load() | |
} | ||
} | ||
|
||
protected void AddPoint(Vector2 start, Vector2 end, Vector2 hitPoint, float radius) | ||
public void AddPoint(Vector2 start, Vector2 end, Vector2 hitPoint, float radius) | ||
{ | ||
if (pointGrid.Content.Count == 0) | ||
if (pointGrid == null || pointGrid.Content.Count == 0) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what is with the null check here? sounds to me like you're calling |
||
return; | ||
|
||
double angle1 = Math.Atan2(end.Y - hitPoint.Y, hitPoint.X - end.X); // Angle between the end point and the hit point. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
drawable adding event subscriptions to other components that are not cleaned up on disposal => this will leak delegates and hold references to removed instances of this component forever