mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 16:12:57 +08:00
updated to newly refactored aim
This commit is contained in:
parent
cec063206e
commit
200149c9d7
@ -22,11 +22,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
|
|||||||
public double JumpDistance { get; private set; }
|
public double JumpDistance { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Normalized Vector from the end position of the previous <see cref="OsuDifficultyHitObject"/> to the start position of this <see cref="OsuDifficultyHitObject"/>.
|
/// Minimum distance from the end position of the previous <see cref="OsuDifficultyHitObject"/> to the start position of this <see cref="OsuDifficultyHitObject"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Vector2 JumpVector { get; private set; }
|
public double MovementDistance { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>JumpTravel
|
||||||
/// Normalized distance between the start and end position of the previous <see cref="OsuDifficultyHitObject"/>.
|
/// Normalized distance between the start and end position of the previous <see cref="OsuDifficultyHitObject"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public double TravelDistance { get; private set; }
|
public double TravelDistance { get; private set; }
|
||||||
@ -37,6 +37,16 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public double? Angle { get; private set; }
|
public double? Angle { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Milliseconds elapsed since the end time of the Previous <see cref="OsuDifficultyHitObject"/>, with a minimum of 25ms.
|
||||||
|
/// </summary>
|
||||||
|
public double MovementTime { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Milliseconds elapsed since from the start time of the Previous <see cref="OsuDifficultyHitObject"/> to the end time of the same Previous <see cref="OsuDifficultyHitObject"/>, with a minimum of 25ms.
|
||||||
|
/// </summary>
|
||||||
|
public double TravelTime { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Milliseconds elapsed since the start time of the previous <see cref="OsuDifficultyHitObject"/>, with a minimum of 25ms.
|
/// Milliseconds elapsed since the start time of the previous <see cref="OsuDifficultyHitObject"/>, with a minimum of 25ms.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -51,13 +61,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
|
|||||||
this.lastLastObject = (OsuHitObject)lastLastObject;
|
this.lastLastObject = (OsuHitObject)lastLastObject;
|
||||||
this.lastObject = (OsuHitObject)lastObject;
|
this.lastObject = (OsuHitObject)lastObject;
|
||||||
|
|
||||||
setDistances();
|
|
||||||
|
|
||||||
// Capped to 25ms to prevent difficulty calculation breaking from simulatenous objects.
|
// Capped to 25ms to prevent difficulty calculation breaking from simulatenous objects.
|
||||||
StrainTime = Math.Max(DeltaTime, 25);
|
StrainTime = Math.Max(DeltaTime, 25);
|
||||||
|
|
||||||
|
setDistances(clockRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setDistances()
|
private void setDistances(double clockRate)
|
||||||
{
|
{
|
||||||
// We will scale distances by this factor, so we can assume a uniform CircleSize among beatmaps.
|
// We will scale distances by this factor, so we can assume a uniform CircleSize among beatmaps.
|
||||||
float scalingFactor = normalized_radius / (float)BaseObject.Radius;
|
float scalingFactor = normalized_radius / (float)BaseObject.Radius;
|
||||||
@ -72,6 +82,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
|
|||||||
{
|
{
|
||||||
computeSliderCursorPosition(lastSlider);
|
computeSliderCursorPosition(lastSlider);
|
||||||
TravelDistance = lastSlider.LazyTravelDistance * scalingFactor;
|
TravelDistance = lastSlider.LazyTravelDistance * scalingFactor;
|
||||||
|
TravelTime = Math.Max(lastSlider.LazyTravelTime / clockRate, 0);
|
||||||
|
MovementTime = Math.Max(StrainTime - TravelTime, 0);
|
||||||
|
MovementDistance = Math.Max(0, Vector2.Subtract(lastSlider.TailCircle.StackedPosition, BaseObject.StackedPosition).Length - 0) * scalingFactor;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2 lastCursorPosition = getEndCursorPosition(lastObject);
|
Vector2 lastCursorPosition = getEndCursorPosition(lastObject);
|
||||||
@ -79,8 +92,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
|
|||||||
// Don't need to jump to reach spinners
|
// Don't need to jump to reach spinners
|
||||||
if (!(BaseObject is Spinner))
|
if (!(BaseObject is Spinner))
|
||||||
{
|
{
|
||||||
JumpVector = (BaseObject.StackedPosition * scalingFactor - lastCursorPosition * scalingFactor);
|
JumpDistance = (BaseObject.StackedPosition * scalingFactor - lastCursorPosition * scalingFactor).Length;
|
||||||
JumpDistance = JumpVector.Length;
|
MovementDistance = Math.Min(JumpDistance, MovementDistance);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastLastObject != null)
|
if (lastLastObject != null)
|
||||||
@ -104,7 +117,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
|
|||||||
|
|
||||||
slider.LazyEndPosition = slider.StackedPosition;
|
slider.LazyEndPosition = slider.StackedPosition;
|
||||||
|
|
||||||
float approxFollowCircleRadius = (float)(slider.Radius * 3);
|
float approxFollowCircleRadius = (float)(slider.Radius * 2.4);
|
||||||
var computeVertex = new Action<double>(t =>
|
var computeVertex = new Action<double>(t =>
|
||||||
{
|
{
|
||||||
double progress = (t - slider.StartTime) / slider.SpanDuration;
|
double progress = (t - slider.StartTime) / slider.SpanDuration;
|
||||||
@ -117,6 +130,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
|
|||||||
var diff = slider.StackedPosition + slider.Path.PositionAt(progress) - slider.LazyEndPosition.Value;
|
var diff = slider.StackedPosition + slider.Path.PositionAt(progress) - slider.LazyEndPosition.Value;
|
||||||
float dist = diff.Length;
|
float dist = diff.Length;
|
||||||
|
|
||||||
|
slider.LazyTravelTime = t - slider.StartTime;
|
||||||
|
|
||||||
if (dist > approxFollowCircleRadius)
|
if (dist > approxFollowCircleRadius)
|
||||||
{
|
{
|
||||||
// The cursor would be outside the follow circle, we need to move it
|
// The cursor would be outside the follow circle, we need to move it
|
||||||
|
@ -7,7 +7,6 @@ using osu.Game.Rulesets.Mods;
|
|||||||
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osuTK;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Difficulty.Skills
|
namespace osu.Game.Rulesets.Osu.Difficulty.Skills
|
||||||
{
|
{
|
||||||
@ -23,13 +22,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
|
|||||||
|
|
||||||
protected override int HistoryLength => 2;
|
protected override int HistoryLength => 2;
|
||||||
|
|
||||||
private const double wide_angle_multiplier = 1.0;
|
private const double wide_angle_multiplier = 1.5;
|
||||||
private const double acute_angle_multiplier = 1.0;
|
private const double acute_angle_multiplier = 2.0;
|
||||||
private const double rhythm_variance_multiplier = 1.0;
|
|
||||||
|
|
||||||
private double currentStrain = 1;
|
private double currentStrain = 1;
|
||||||
|
|
||||||
private double skillMultiplier => 26.25;
|
private double skillMultiplier => 23.25;
|
||||||
private double strainDecayBase => 0.15;
|
private double strainDecayBase => 0.15;
|
||||||
|
|
||||||
private double strainValueOf(DifficultyHitObject current)
|
private double strainValueOf(DifficultyHitObject current)
|
||||||
@ -41,75 +39,65 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
|
|||||||
var osuPrevObj = (OsuDifficultyHitObject)Previous[0];
|
var osuPrevObj = (OsuDifficultyHitObject)Previous[0];
|
||||||
var osuLastObj = (OsuDifficultyHitObject)Previous[1];
|
var osuLastObj = (OsuDifficultyHitObject)Previous[1];
|
||||||
|
|
||||||
var currVector = Vector2.Divide(osuCurrObj.JumpVector, (float)osuCurrObj.StrainTime);
|
double currVelocity = osuCurrObj.JumpDistance / osuCurrObj.StrainTime; // Start iwth the base distance / time
|
||||||
var prevVector = Vector2.Divide(osuPrevObj.JumpVector, (float)osuPrevObj.StrainTime);
|
|
||||||
|
|
||||||
// Start with regular velocity.
|
if (osuPrevObj.BaseObject is Slider) // If object is a slider
|
||||||
double aimStrain = currVector.Length;
|
|
||||||
|
|
||||||
if (Precision.AlmostEquals(osuCurrObj.StrainTime, osuPrevObj.StrainTime, 10)) // Rhythms are the same.
|
|
||||||
{
|
{
|
||||||
if (osuCurrObj.Angle != null)
|
double movementVelocity = osuCurrObj.MovementDistance / osuCurrObj.MovementTime; // calculate the movement velocity from slider end to next note
|
||||||
|
double travelVelocity = osuCurrObj.TravelDistance / osuCurrObj.TravelTime; // calculate the slider velocity from slider head to lazy end.
|
||||||
|
|
||||||
|
currVelocity = Math.Max(currVelocity, movementVelocity + travelVelocity); // take the larger total combined velocity.
|
||||||
|
}
|
||||||
|
|
||||||
|
double prevVelocity = osuPrevObj.JumpDistance / osuPrevObj.StrainTime; // do the same for the previous velocity.
|
||||||
|
|
||||||
|
if (osuLastObj.BaseObject is Slider)
|
||||||
|
{
|
||||||
|
double movementVelocity = osuPrevObj.MovementDistance / osuPrevObj.MovementTime;
|
||||||
|
double travelVelocity = osuPrevObj.TravelDistance / osuPrevObj.TravelTime;
|
||||||
|
|
||||||
|
prevVelocity = Math.Max(prevVelocity, movementVelocity + travelVelocity);
|
||||||
|
}
|
||||||
|
|
||||||
|
double angleBonus = 0;
|
||||||
|
|
||||||
|
double aimStrain = currVelocity; // Start strain with regular velocity.
|
||||||
|
|
||||||
|
if (Precision.AlmostEquals(osuCurrObj.StrainTime, osuPrevObj.StrainTime, 10)) // If rhythms are the same.
|
||||||
|
{
|
||||||
|
if (osuCurrObj.Angle != null && osuPrevObj.Angle != null)
|
||||||
{
|
{
|
||||||
double angle = osuCurrObj.Angle.Value;
|
double currAngle = osuCurrObj.Angle.Value;
|
||||||
|
double prevAngle = osuPrevObj.Angle.Value;
|
||||||
|
|
||||||
// Rewarding angles, take the smaller velocity as base.
|
// Rewarding angles, take the smaller velocity as base.
|
||||||
double angleBonus = Math.Min(currVector.Length, prevVector.Length);
|
angleBonus = Math.Min(currVelocity, prevVelocity);
|
||||||
|
|
||||||
double wideAngleBonus = calcWideAngleBonus(angle);
|
double wideAngleBonus = calcWideAngleBonus(currAngle);
|
||||||
double acuteAngleBonus = calcAcuteAngleBonus(angle);
|
double acuteAngleBonus = calcAcuteAngleBonus(currAngle);
|
||||||
|
|
||||||
if (osuCurrObj.StrainTime > 100)
|
if (osuCurrObj.StrainTime > 100) // Only buff deltaTime exceeding 300 bpm 1/2.
|
||||||
acuteAngleBonus = 0;
|
acuteAngleBonus = 0;
|
||||||
else
|
else
|
||||||
{
|
acuteAngleBonus *= calcAcuteAngleBonus(prevAngle) // Multiply by previous angle, we don't want to buff unless this is a wiggle type pattern.
|
||||||
acuteAngleBonus *= Math.Min(2, Math.Pow((100 - osuCurrObj.StrainTime) / 15, 1.5));
|
* Math.Min(angleBonus, 125 / osuCurrObj.StrainTime) // The maximum velocity we buff is equal to 125 / strainTime
|
||||||
wideAngleBonus *= Math.Pow(osuCurrObj.StrainTime / 100, 6);
|
* Math.Pow(Math.Sin(Math.PI / 2 * Math.Min(1, (100 - osuCurrObj.StrainTime) / 25)), 2) // scale buff from 150 bpm 1/4 to 200 bpm 1/4
|
||||||
}
|
* Math.Pow(Math.Sin(Math.PI / 2 * (Math.Min(100, osuCurrObj.JumpDistance) - 50) / 50), 2); // Buff distance exceeding 50 (radius) up to 100 (diameter).
|
||||||
|
|
||||||
if (acuteAngleBonus > wideAngleBonus)
|
wideAngleBonus *= angleBonus * (1 - Math.Pow(calcWideAngleBonus(prevAngle), 3)); // Penalize wide angles if they're repeated, reducing the penalty as the prevAngle gets more acute.
|
||||||
angleBonus = Math.Min(angleBonus, 150 / osuCurrObj.StrainTime) * Math.Min(1, Math.Pow(Math.Min(osuCurrObj.JumpDistance, osuPrevObj.JumpDistance) / 150, 2));
|
|
||||||
|
|
||||||
angleBonus *= Math.Max(acuteAngleBonus * acute_angle_multiplier, wideAngleBonus * wide_angle_multiplier);
|
angleBonus = acuteAngleBonus * acute_angle_multiplier + wideAngleBonus * wide_angle_multiplier; // add the anglebuffs together.
|
||||||
|
|
||||||
// add in angle velocity.
|
|
||||||
aimStrain += angleBonus;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else // There is a rhythm change
|
|
||||||
{
|
|
||||||
// Rewarding rhythm, take the smaller velocity as base.
|
|
||||||
double rhythmBonus = Math.Min(currVector.Length, prevVector.Length);
|
|
||||||
|
|
||||||
if (osuCurrObj.StrainTime + 10 < osuPrevObj.StrainTime && osuPrevObj.StrainTime > osuLastObj.StrainTime + 10)
|
aimStrain += angleBonus; // Add in angle bonus.
|
||||||
// Don't want to reward for a rhythm change back to back (unless its a double, which is why this only checks for fast -> slow -> fast).
|
|
||||||
rhythmBonus = 0;
|
|
||||||
|
|
||||||
aimStrain += rhythmBonus * rhythm_variance_multiplier; // add in rhythm velocity.
|
|
||||||
}
|
|
||||||
|
|
||||||
return aimStrain;
|
return aimStrain;
|
||||||
}
|
}
|
||||||
|
|
||||||
private double calcWideAngleBonus(double angle)
|
private double calcWideAngleBonus(double angle) => Math.Pow(Math.Sin(3.0 / 4 * (Math.Min(5.0 / 6 * Math.PI, Math.Max(Math.PI / 6, angle)) - Math.PI / 6)), 2);
|
||||||
{
|
|
||||||
if (angle < Math.PI / 3)
|
|
||||||
return 0;
|
|
||||||
if (angle < 2 * Math.PI / 3)
|
|
||||||
return Math.Pow(Math.Sin(1.5 * (angle - Math.PI / 3)), 2);
|
|
||||||
|
|
||||||
return 0.25 + 0.75 * Math.Pow(Math.Sin(1.5 * (Math.PI - angle)), 2);
|
private double calcAcuteAngleBonus(double angle) => 1 - calcWideAngleBonus(angle);
|
||||||
}
|
|
||||||
|
|
||||||
private double calcAcuteAngleBonus(double angle)
|
|
||||||
{
|
|
||||||
if (angle < Math.PI / 3)
|
|
||||||
return 0.5 + 0.5 * Math.Pow(Math.Sin(1.5 * angle), 2);
|
|
||||||
if (angle < 2 * Math.PI / 3)
|
|
||||||
return Math.Pow(Math.Sin(1.5 * (2 * Math.PI / 3 - angle)), 2);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private double applyDiminishingExp(double val) => Math.Pow(val, 0.99);
|
private double applyDiminishingExp(double val) => Math.Pow(val, 0.99);
|
||||||
|
|
||||||
|
@ -79,6 +79,12 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal float LazyTravelDistance;
|
internal float LazyTravelDistance;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The time taken by the cursor upon completion of this <see cref="Slider"/> if it was hit
|
||||||
|
/// with as few movements as possible. This is set and used by difficulty calculation.
|
||||||
|
/// </summary>
|
||||||
|
internal double LazyTravelTime;
|
||||||
|
|
||||||
public List<IList<HitSampleInfo>> NodeSamples { get; set; } = new List<IList<HitSampleInfo>>();
|
public List<IList<HitSampleInfo>> NodeSamples { get; set; } = new List<IList<HitSampleInfo>>();
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
|
Loading…
Reference in New Issue
Block a user