mirror of
https://github.com/ppy/osu.git
synced 2025-01-12 15:22:55 +08:00
More docs, better docs.
This commit is contained in:
parent
ce0d70d651
commit
93f654a539
@ -7,39 +7,44 @@ using osu.Game.Rulesets.Osu.Objects;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
|
||||
{
|
||||
/// <summary>
|
||||
/// An enumerable container wrapping <see cref="OsuHitObject"/> input as <see cref="OsuDifficultyHitObject"/>
|
||||
/// which contains extra data required for difficulty calculation.
|
||||
/// </summary>
|
||||
public class OsuDifficultyBeatmap : IEnumerable<OsuDifficultyHitObject>
|
||||
{
|
||||
private readonly IEnumerator<OsuDifficultyHitObject> difficultyObjects;
|
||||
private readonly Queue<OsuDifficultyHitObject> onScreen = new Queue<OsuDifficultyHitObject>();
|
||||
|
||||
/// <summary>
|
||||
/// Creates an enumerable, which handles the preprocessing of HitObjects, getting them ready to be used in difficulty calculation.
|
||||
/// Creates an enumerator, which preprocesses a list of <see cref="OsuHitObject"/>s recieved as input, wrapping them as
|
||||
/// <see cref="OsuDifficultyHitObject"/> which contains extra data required for difficulty calculation.
|
||||
/// </summary>
|
||||
public OsuDifficultyBeatmap(List<OsuHitObject> objects)
|
||||
{
|
||||
// Sort HitObjects by StartTime - they are not correctly ordered in some cases.
|
||||
// Sort OsuHitObjects by StartTime - they are not correctly ordered in some cases.
|
||||
// This should probably happen before the objects reach the difficulty calculator.
|
||||
objects.Sort((a, b) => a.StartTime.CompareTo(b.StartTime));
|
||||
difficultyObjects = createDifficultyObjectEnumerator(objects);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an enumerator that enumerates all difficulty objects in the beatmap.
|
||||
/// The inner loop adds notes that appear on screen into a queue until we need to hit the next note,
|
||||
/// the outer loop returns notes from this queue one at a time, only after they had to be hit, and should no longer be on screen.
|
||||
/// Returns an enumerator that enumerates all <see cref="OsuDifficultyHitObject"/>s in the <see cref="OsuDifficultyBeatmap"/>.
|
||||
/// The inner loop adds objects that appear on screen into a queue until we need to hit the next object.
|
||||
/// The outer loop returns objects from this queue one at a time, only after they had to be hit, and should no longer be on screen.
|
||||
/// This means that we can loop through every object that is on screen at the time when a new one appears,
|
||||
/// allowing us to determine a reading strain for the note that just appeared.
|
||||
/// allowing us to determine a reading strain for the object that just appeared.
|
||||
/// </summary>
|
||||
public IEnumerator<OsuDifficultyHitObject> GetEnumerator()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
// Add upcoming notes to the queue until we have at least one note that had been hit and can be dequeued.
|
||||
// This means there is always at least one note in the queue unless we reached the end of the map.
|
||||
// Add upcoming objects to the queue until we have at least one object that had been hit and can be dequeued.
|
||||
// This means there is always at least one object in the queue unless we reached the end of the map.
|
||||
do
|
||||
{
|
||||
if (!difficultyObjects.MoveNext())
|
||||
break; // New notes can't be added anymore, but we still need to dequeue and return the ones already on screen.
|
||||
break; // New objects can't be added anymore, but we still need to dequeue and return the ones already on screen.
|
||||
|
||||
OsuDifficultyHitObject latest = difficultyObjects.Current;
|
||||
// Calculate flow values here
|
||||
@ -52,10 +57,10 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
|
||||
|
||||
onScreen.Enqueue(latest);
|
||||
}
|
||||
while (onScreen.Peek().TimeUntilHit > 0); // Keep adding new notes on screen while there is still time before we have to hit the next one.
|
||||
while (onScreen.Peek().TimeUntilHit > 0); // Keep adding new objects on screen while there is still time before we have to hit the next one.
|
||||
|
||||
if (onScreen.Count == 0) break; // We have reached the end of the map and enumerated all the notes.
|
||||
yield return onScreen.Dequeue(); // Remove and return notes one by one that had to be hit before the latest note appeared.
|
||||
if (onScreen.Count == 0) break; // We have reached the end of the map and enumerated all the objects.
|
||||
yield return onScreen.Dequeue(); // Remove and return objects one by one that had to be hit before the latest one appeared.
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,18 +68,18 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
|
||||
|
||||
private IEnumerator<OsuDifficultyHitObject> createDifficultyObjectEnumerator(List<OsuHitObject> objects)
|
||||
{
|
||||
// We will process HitObjects in groups of three to form a triangle, so we can calculate an angle for each note.
|
||||
// We will process OsuHitObjects in groups of three to form a triangle, so we can calculate an angle for each object.
|
||||
OsuHitObject[] triangle = new OsuHitObject[3];
|
||||
|
||||
// Difficulty object construction requires three components, an extra copy of the first object is used at the beginning.
|
||||
// OsuDifficultyHitObject construction requires three components, an extra copy of the first OsuHitObject is used at the beginning.
|
||||
if (objects.Count > 1)
|
||||
{
|
||||
triangle[1] = objects[0]; // This copy will get shifted to the last spot in the triangle.
|
||||
triangle[0] = objects[0]; // This is the real first note.
|
||||
triangle[0] = objects[0]; // This component corresponds to the real first OsuHitOject.
|
||||
}
|
||||
|
||||
// The final component of the first triangle will be the second note, which forms the first jump.
|
||||
// If the beatmap has less than two HitObjects, the enumerator will not return anything.
|
||||
// The final component of the first triangle will be the second OsuHitOject of the map, which forms the first jump.
|
||||
// If the map has less than two OsuHitObjects, the enumerator will not return anything.
|
||||
for (int i = 1; i < objects.Count; ++i)
|
||||
{
|
||||
triangle[2] = triangle[1];
|
||||
|
@ -6,25 +6,28 @@ using osu.Game.Rulesets.Osu.Objects;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
|
||||
{
|
||||
/// <summary>
|
||||
/// A wrapper around <see cref="OsuHitObject"/> extending it with additional data required for difficulty calculation.
|
||||
/// </summary>
|
||||
public class OsuDifficultyHitObject
|
||||
{
|
||||
/// <summary>
|
||||
/// The current note. Attributes are calculated relative to previous ones.
|
||||
/// The <see cref="OsuHitObject"/> this <see cref="OsuDifficultyHitObject"/> refers to.
|
||||
/// </summary>
|
||||
public OsuHitObject BaseObject { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Normalized distance from the StartPosition of the previous note.
|
||||
/// Normalized distance from the <see cref="OsuHitObject.StackedPosition"/> of the previous <see cref="OsuDifficultyHitObject"/>.
|
||||
/// </summary>
|
||||
public double Distance { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Milliseconds elapsed since the StartTime of the previous note.
|
||||
/// Milliseconds elapsed since the StartTime of the previous <see cref="OsuDifficultyHitObject"/>.
|
||||
/// </summary>
|
||||
public double DeltaTime { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of milliseconds until the note has to be hit.
|
||||
/// Number of milliseconds until the <see cref="OsuDifficultyHitObject"/> has to be hit. <see cref="OsuHitObject."/>
|
||||
/// </summary>
|
||||
public double TimeUntilHit { get; set; }
|
||||
|
||||
@ -33,7 +36,7 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
|
||||
private readonly OsuHitObject[] t;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a wrapper around a HitObject calculating additional data required for difficulty calculation.
|
||||
/// Initializes the object calculating extra data required for difficulty calculation.
|
||||
/// </summary>
|
||||
public OsuDifficultyHitObject(OsuHitObject[] triangle)
|
||||
{
|
||||
|
@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.OsuDifficulty.Skills
|
||||
{
|
||||
@ -10,6 +11,6 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Skills
|
||||
protected override double SkillMultiplier => 26.25;
|
||||
protected override double StrainDecayBase => 0.15;
|
||||
|
||||
protected override double StrainValue() => Math.Pow(Current.Distance, 0.99) / Current.DeltaTime;
|
||||
protected override double StrainValueOf(OsuDifficultyHitObject current) => Math.Pow(current.Distance, 0.99) / current.DeltaTime;
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,10 @@ using osu.Game.Rulesets.Osu.OsuDifficulty.Utils;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.OsuDifficulty.Skills
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to processes strain values of <see cref="OsuDifficultyHitObject"/>s, keep track of strain levels caused by the processed objects
|
||||
/// and to calculate a final difficulty value representing the difficulty of hitting all the processed objects.
|
||||
/// </summary>
|
||||
public abstract class Skill
|
||||
{
|
||||
/// <summary>
|
||||
@ -18,38 +22,31 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Skills
|
||||
|
||||
/// <summary>
|
||||
/// Determines how quickly strain decays for the given skill.
|
||||
/// For example a value of 0.15 indicates that strain decays to 15% of it's original value in one second.
|
||||
/// For example a value of 0.15 indicates that strain decays to 15% of its original value in one second.
|
||||
/// </summary>
|
||||
protected abstract double StrainDecayBase { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The note that will be processed.
|
||||
/// <see cref="OsuDifficultyHitObject"/>s that were processed previously. They can affect the strain values of the following objects.
|
||||
/// </summary>
|
||||
protected OsuDifficultyHitObject Current;
|
||||
|
||||
/// <summary>
|
||||
/// Notes that were processed previously. They can affect the strain value of the current note.
|
||||
/// </summary>
|
||||
protected History<OsuDifficultyHitObject> Previous = new History<OsuDifficultyHitObject>(2); // Contained objects not used yet
|
||||
protected readonly History<OsuDifficultyHitObject> Previous = new History<OsuDifficultyHitObject>(2); // Contained objects not used yet
|
||||
|
||||
private double currentStrain = 1; // We keep track of the strain level at all times throughout the beatmap.
|
||||
private double currentSectionPeak = 1; // We also keep track of the peak strain level in the current section.
|
||||
private readonly List<double> strainPeaks = new List<double>();
|
||||
|
||||
/// <summary>
|
||||
/// Process a HitObject and update current strain values accordingly.
|
||||
/// Process an <see cref="OsuDifficultyHitObject"/> and update current strain values accordingly.
|
||||
/// </summary>
|
||||
public void Process(OsuDifficultyHitObject h)
|
||||
public void Process(OsuDifficultyHitObject current)
|
||||
{
|
||||
Current = h;
|
||||
|
||||
currentStrain *= strainDecay(Current.DeltaTime);
|
||||
if (!(Current.BaseObject is Spinner))
|
||||
currentStrain += StrainValue() * SkillMultiplier;
|
||||
currentStrain *= strainDecay(current.DeltaTime);
|
||||
if (!(current.BaseObject is Spinner))
|
||||
currentStrain += StrainValueOf(current) * SkillMultiplier;
|
||||
|
||||
currentSectionPeak = Math.Max(currentStrain, currentSectionPeak);
|
||||
|
||||
Previous.Push(Current);
|
||||
Previous.Push(current);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -74,7 +71,7 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Skills
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the calculated difficulty value representing all currently processed HitObjects.
|
||||
/// Returns the calculated difficulty value representing all processed <see cref="OsuDifficultyHitObject"/>s.
|
||||
/// </summary>
|
||||
public double DifficultyValue()
|
||||
{
|
||||
@ -94,9 +91,9 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Skills
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the strain value of the current note. This value is affected by previous notes.
|
||||
/// Calculates the strain value of an <see cref="OsuDifficultyHitObject"/>. This value is affected by previously processed objects.
|
||||
/// </summary>
|
||||
protected abstract double StrainValue();
|
||||
protected abstract double StrainValueOf(OsuDifficultyHitObject current);
|
||||
|
||||
private double strainDecay(double ms) => Math.Pow(StrainDecayBase, ms / 1000);
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.OsuDifficulty.Skills
|
||||
{
|
||||
public class Speed : Skill
|
||||
@ -12,9 +14,9 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Skills
|
||||
private const double stream_spacing_threshold = 110;
|
||||
private const double almost_diameter = 90;
|
||||
|
||||
protected override double StrainValue()
|
||||
protected override double StrainValueOf(OsuDifficultyHitObject current)
|
||||
{
|
||||
double distance = Current.Distance;
|
||||
double distance = current.Distance;
|
||||
|
||||
double speedValue;
|
||||
if (distance > single_spacing_threshold)
|
||||
@ -28,7 +30,7 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Skills
|
||||
else
|
||||
speedValue = 0.95;
|
||||
|
||||
return speedValue / Current.DeltaTime;
|
||||
return speedValue / current.DeltaTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -52,8 +52,8 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Utils
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the element as the most recent one in the history.
|
||||
/// The oldest element is disposed if the history is full.
|
||||
/// Adds the item as the most recent one in the history.
|
||||
/// The oldest item is disposed if the history is full.
|
||||
/// </summary>
|
||||
public void Push(T item) // Overwrite the oldest item instead of shifting every item by one with every addition.
|
||||
{
|
||||
@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Utils
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an enumerator which enumerates items in the history starting from the most recently added item.
|
||||
/// Returns an enumerator which enumerates items in the history starting from the most recently added one.
|
||||
/// </summary>
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user