mirror of
https://github.com/ppy/osu.git
synced 2025-03-13 19:47:30 +08:00
Implement aim UR
This commit is contained in:
parent
1cde18b895
commit
6d863e5a46
@ -320,16 +320,21 @@ namespace osu.Game.Rulesets.Osu
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 250
|
||||
}, true),
|
||||
new StatisticItem("Timing Statistics", () => new SimpleStatisticTable(2, new SimpleStatisticItem[]
|
||||
{
|
||||
new AverageHitError(timedHitEvents),
|
||||
new UnstableRate(timedHitEvents)
|
||||
}), true),
|
||||
new StatisticItem("Accuracy Heatmap", () => new AccuracyHeatmap(score, playableBeatmap)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 250
|
||||
}, true),
|
||||
new StatisticItem("Statistics", () => new SimpleStatisticTable(2, new SimpleStatisticItem[]
|
||||
new StatisticItem("Aim Statistics", () => new SimpleStatisticTable(2, new SimpleStatisticItem[]
|
||||
{
|
||||
new AverageHitError(timedHitEvents),
|
||||
new UnstableRate(timedHitEvents)
|
||||
}), true)
|
||||
new AverageAimError(timedHitEvents),
|
||||
new AimError(timedHitEvents)
|
||||
}), true),
|
||||
};
|
||||
}
|
||||
|
||||
|
67
osu.Game.Rulesets.Osu/Statistics/AimError.cs
Normal file
67
osu.Game.Rulesets.Osu/Statistics/AimError.cs
Normal file
@ -0,0 +1,67 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Screens.Ranking.Statistics;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Statistics
|
||||
{
|
||||
/// <summary>
|
||||
/// Displays the unstable rate statistic for a given play.
|
||||
/// </summary>
|
||||
public partial class AimError : SimpleStatisticItem<double?>
|
||||
{
|
||||
private readonly List<Vector2> hitPoints = new List<Vector2>();
|
||||
|
||||
/// <summary>
|
||||
/// Creates and computes an <see cref="AimError"/> statistic.
|
||||
/// </summary>
|
||||
public AimError(IEnumerable<HitEvent> hitEvents)
|
||||
: base("Aim Error")
|
||||
{
|
||||
Value = calculateAimError(hitEvents);
|
||||
}
|
||||
|
||||
private double? calculateAimError(IEnumerable<HitEvent> hitEvents)
|
||||
{
|
||||
IEnumerable<HitEvent> rawHitPositions = hitEvents.Where(affectsAimError);
|
||||
|
||||
if (!rawHitPositions.Any())
|
||||
return null;
|
||||
|
||||
foreach (var e in hitEvents.Where(e => e.HitObject is HitCircle && !(e.HitObject is SliderTailCircle)))
|
||||
{
|
||||
if (e.LastHitObject == null || e.Position == null)
|
||||
continue;
|
||||
|
||||
addAngleAdjustedPoint(((OsuHitObject)e.LastHitObject).StackedEndPosition, ((OsuHitObject)e.HitObject).StackedEndPosition, e.Position.Value);
|
||||
}
|
||||
|
||||
Vector2 averagePosition = new Vector2(hitPoints.Sum(x => x[0]), hitPoints.Sum(x => x[1])) / hitEvents.Where(affectsAimError).Count();
|
||||
|
||||
return Math.Sqrt(hitPoints.Average(x => (x - averagePosition).LengthSquared)) * 10;
|
||||
}
|
||||
|
||||
private void addAngleAdjustedPoint(Vector2 start, Vector2 end, Vector2 hitPoint)
|
||||
{
|
||||
double angle1 = Math.Atan2(end.Y - hitPoint.Y, hitPoint.X - end.X); // Angle between the end point and the hit point.
|
||||
double angle2 = Math.Atan2(end.Y - start.Y, start.X - end.X); // Angle between the end point and the start point.
|
||||
double finalAngle = angle2 - angle1; // Angle between start, end, and hit points.
|
||||
|
||||
double distanceFromCenter = (hitPoint - end).Length;
|
||||
|
||||
Vector2 angleAdjustedPoint = new Vector2((float)(Math.Sin(finalAngle) * distanceFromCenter), (float)(Math.Cos(finalAngle) * distanceFromCenter));
|
||||
|
||||
hitPoints.Add(angleAdjustedPoint);
|
||||
}
|
||||
|
||||
private bool affectsAimError(HitEvent hitEvent) => hitEvent.HitObject is HitCircle && !(hitEvent.HitObject is SliderTailCircle) && hitEvent.Result.IsHit();
|
||||
|
||||
protected override string DisplayValue(double? value) => value == null ? "(not available)" : value.Value.ToString(@"N2");
|
||||
}
|
||||
}
|
68
osu.Game.Rulesets.Osu/Statistics/AverageAimError.cs
Normal file
68
osu.Game.Rulesets.Osu/Statistics/AverageAimError.cs
Normal file
@ -0,0 +1,68 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Screens.Ranking.Statistics;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Statistics
|
||||
{
|
||||
/// <summary>
|
||||
/// Displays the unstable rate statistic for a given play.
|
||||
/// </summary>
|
||||
public partial class AverageAimError : SimpleStatisticItem<double?>
|
||||
{
|
||||
private readonly List<Vector2> hitPoints = new List<Vector2>();
|
||||
|
||||
/// <summary>
|
||||
/// Creates and computes an <see cref="AverageHitError"/> statistic.
|
||||
/// </summary>
|
||||
/// <param name="hitEvents">Sequence of <see cref="HitEvent"/>s to calculate the unstable rate based on.</param>
|
||||
public AverageAimError(IEnumerable<HitEvent> hitEvents)
|
||||
: base("Average Hit Error")
|
||||
{
|
||||
Value = calculateAverageAimError(hitEvents);
|
||||
}
|
||||
|
||||
private double? calculateAverageAimError(IEnumerable<HitEvent> hitEvents)
|
||||
{
|
||||
IEnumerable<HitEvent> rawHitPositions = hitEvents.Where(affectsAimError);
|
||||
|
||||
if (!rawHitPositions.Any())
|
||||
return null;
|
||||
|
||||
foreach (var e in rawHitPositions)
|
||||
{
|
||||
if (e.LastHitObject == null || e.Position == null)
|
||||
continue;
|
||||
|
||||
addAngleAdjustedPoint(((OsuHitObject)e.LastHitObject).StackedEndPosition, ((OsuHitObject)e.HitObject).StackedEndPosition, e.Position.Value);
|
||||
}
|
||||
|
||||
Vector2 averagePosition = new Vector2(hitPoints.Sum(x => x[0]), hitPoints.Sum(x => x[1])) / rawHitPositions.Count();
|
||||
|
||||
return averagePosition.Length;
|
||||
}
|
||||
|
||||
private void addAngleAdjustedPoint(Vector2 start, Vector2 end, Vector2 hitPoint)
|
||||
{
|
||||
double angle1 = Math.Atan2(end.Y - hitPoint.Y, hitPoint.X - end.X); // Angle between the end point and the hit point.
|
||||
double angle2 = Math.Atan2(end.Y - start.Y, start.X - end.X); // Angle between the end point and the start point.
|
||||
double finalAngle = angle2 - angle1; // Angle between start, end, and hit points.
|
||||
|
||||
double distanceFromCenter = (hitPoint - end).Length;
|
||||
|
||||
Vector2 angleAdjustedPoint = new Vector2((float)(Math.Sin(finalAngle) * distanceFromCenter), (float)(Math.Cos(finalAngle) * distanceFromCenter));
|
||||
|
||||
hitPoints.Add(angleAdjustedPoint);
|
||||
}
|
||||
|
||||
private bool affectsAimError(HitEvent hitEvent) => hitEvent.HitObject is HitCircle && !(hitEvent.HitObject is SliderTailCircle) && hitEvent.Result.IsHit();
|
||||
|
||||
protected override string DisplayValue(double? value) => value == null ? "(not available)" : $"{Math.Abs(value.Value):N2} osu!px from center";
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user