1
0
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:
Péter Nemes 2017-06-06 18:59:46 +02:00
parent ce0d70d651
commit 93f654a539
6 changed files with 56 additions and 48 deletions

View File

@ -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];

View File

@ -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)
{

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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;
}
}
}

View File

@ -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()
{