mirror of
https://github.com/ppy/osu.git
synced 2025-01-06 07:42:55 +08:00
new Approach to approach rate
This commit is contained in:
parent
dc365bfc11
commit
3ba5dce052
@ -37,7 +37,6 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
private void applyStacking(Beatmap<OsuHitObject> beatmap)
|
private void applyStacking(Beatmap<OsuHitObject> beatmap)
|
||||||
{
|
{
|
||||||
const int stack_distance = 3;
|
const int stack_distance = 3;
|
||||||
float stackThreshold = DrawableOsuHitObject.TIME_PREEMPT * beatmap.BeatmapInfo?.StackLeniency ?? 0.7f;
|
|
||||||
|
|
||||||
// Reset stacking
|
// Reset stacking
|
||||||
for (int i = 0; i <= beatmap.HitObjects.Count - 1; i++)
|
for (int i = 0; i <= beatmap.HitObjects.Count - 1; i++)
|
||||||
@ -58,6 +57,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
double endTime = (stackBaseObject as IHasEndTime)?.EndTime ?? stackBaseObject.StartTime;
|
double endTime = (stackBaseObject as IHasEndTime)?.EndTime ?? stackBaseObject.StartTime;
|
||||||
|
float stackThreshold = objectN.TimePreempt * beatmap.BeatmapInfo?.StackLeniency ?? 0.7f;
|
||||||
|
|
||||||
if (objectN.StartTime - endTime > stackThreshold)
|
if (objectN.StartTime - endTime > stackThreshold)
|
||||||
//We are no longer within stacking range of the next object.
|
//We are no longer within stacking range of the next object.
|
||||||
@ -112,6 +112,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
if (objectN is Spinner) continue;
|
if (objectN is Spinner) continue;
|
||||||
|
|
||||||
double endTime = (objectN as IHasEndTime)?.EndTime ?? objectN.StartTime;
|
double endTime = (objectN as IHasEndTime)?.EndTime ?? objectN.StartTime;
|
||||||
|
float stackThreshold = objectN.TimePreempt * beatmap.BeatmapInfo?.StackLeniency ?? 0.7f;
|
||||||
|
|
||||||
if (objectI.StartTime - endTime > stackThreshold)
|
if (objectI.StartTime - endTime > stackThreshold)
|
||||||
//We are no longer within stacking range of the previous object.
|
//We are no longer within stacking range of the previous object.
|
||||||
@ -165,6 +166,8 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
OsuHitObject objectN = beatmap.HitObjects[n];
|
OsuHitObject objectN = beatmap.HitObjects[n];
|
||||||
if (objectN is Spinner) continue;
|
if (objectN is Spinner) continue;
|
||||||
|
|
||||||
|
float stackThreshold = objectN.TimePreempt * beatmap.BeatmapInfo?.StackLeniency ?? 0.7f;
|
||||||
|
|
||||||
if (objectI.StartTime - objectN.StartTime > stackThreshold)
|
if (objectI.StartTime - objectN.StartTime > stackThreshold)
|
||||||
//We are no longer within stacking range of the previous object.
|
//We are no longer within stacking range of the previous object.
|
||||||
break;
|
break;
|
||||||
|
@ -36,14 +36,12 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
private const double fade_in_duration_multiplier = 0.4;
|
private const double fade_in_duration_multiplier = 0.4;
|
||||||
private const double fade_out_duration_multiplier = 0.3;
|
private const double fade_out_duration_multiplier = 0.3;
|
||||||
|
|
||||||
private float preEmpt => DrawableOsuHitObject.TIME_PREEMPT;
|
|
||||||
|
|
||||||
public void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables)
|
public void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables)
|
||||||
{
|
{
|
||||||
foreach (var d in drawables.OfType<DrawableOsuHitObject>())
|
foreach (var d in drawables.OfType<DrawableOsuHitObject>())
|
||||||
{
|
{
|
||||||
d.ApplyCustomUpdateState += ApplyHiddenState;
|
d.ApplyCustomUpdateState += ApplyHiddenState;
|
||||||
d.FadeInDuration = preEmpt * fade_in_duration_multiplier;
|
d.FadeInDuration = d.HitObject.TimePreempt * fade_in_duration_multiplier;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,8 +50,8 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
if (!(drawable is DrawableOsuHitObject d))
|
if (!(drawable is DrawableOsuHitObject d))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var fadeOutStartTime = d.HitObject.StartTime - preEmpt + d.FadeInDuration;
|
var fadeOutStartTime = d.HitObject.StartTime - d.HitObject.TimePreempt + d.FadeInDuration;
|
||||||
var fadeOutDuration = preEmpt * fade_out_duration_multiplier;
|
var fadeOutDuration = d.HitObject.TimePreempt * fade_out_duration_multiplier;
|
||||||
|
|
||||||
// new duration from completed fade in to end (before fading out)
|
// new duration from completed fade in to end (before fading out)
|
||||||
var longFadeDuration = ((d.HitObject as IHasEndTime)?.EndTime ?? d.HitObject.StartTime) - fadeOutStartTime;
|
var longFadeDuration = ((d.HitObject as IHasEndTime)?.EndTime ?? d.HitObject.StartTime) - fadeOutStartTime;
|
||||||
|
@ -96,12 +96,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
|||||||
|
|
||||||
using (fp.BeginAbsoluteSequence(fadeInTime))
|
using (fp.BeginAbsoluteSequence(fadeInTime))
|
||||||
{
|
{
|
||||||
fp.FadeIn(DrawableOsuHitObject.TIME_FADEIN);
|
fp.FadeIn(currHitObject.TimeFadein);
|
||||||
fp.ScaleTo(1, DrawableOsuHitObject.TIME_FADEIN, Easing.Out);
|
fp.ScaleTo(1, currHitObject.TimeFadein, Easing.Out);
|
||||||
|
|
||||||
fp.MoveTo(pointEndPosition, DrawableOsuHitObject.TIME_FADEIN, Easing.Out);
|
fp.MoveTo(pointEndPosition, currHitObject.TimeFadein, Easing.Out);
|
||||||
|
|
||||||
fp.Delay(fadeOutTime - fadeInTime).FadeOut(DrawableOsuHitObject.TIME_FADEIN);
|
fp.Delay(fadeOutTime - fadeInTime).FadeOut(currHitObject.TimeFadein);
|
||||||
}
|
}
|
||||||
|
|
||||||
fp.Expire(true);
|
fp.Expire(true);
|
||||||
|
@ -88,8 +88,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
{
|
{
|
||||||
base.UpdatePreemptState();
|
base.UpdatePreemptState();
|
||||||
|
|
||||||
ApproachCircle.FadeIn(Math.Min(FadeInDuration * 2, TIME_PREEMPT));
|
ApproachCircle.FadeIn(Math.Min(FadeInDuration * 2, HitObject.TimePreempt));
|
||||||
ApproachCircle.ScaleTo(1.1f, TIME_PREEMPT);
|
ApproachCircle.ScaleTo(1.1f, HitObject.TimePreempt);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UpdateCurrentState(ArmedState state)
|
protected override void UpdateCurrentState(ArmedState state)
|
||||||
@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case ArmedState.Idle:
|
case ArmedState.Idle:
|
||||||
this.Delay(TIME_PREEMPT).FadeOut(500);
|
this.Delay(HitObject.TimePreempt).FadeOut(500);
|
||||||
|
|
||||||
Expire(true);
|
Expire(true);
|
||||||
|
|
||||||
|
@ -10,26 +10,25 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
{
|
{
|
||||||
public class DrawableOsuHitObject : DrawableHitObject<OsuHitObject>
|
public class DrawableOsuHitObject : DrawableHitObject<OsuHitObject>
|
||||||
{
|
{
|
||||||
public const float TIME_PREEMPT = 600;
|
|
||||||
public const float TIME_FADEIN = 400;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The number of milliseconds used to fade in.
|
/// The number of milliseconds used to fade in.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual double FadeInDuration { get; set; } = TIME_FADEIN;
|
public virtual double FadeInDuration { get; set; }
|
||||||
|
|
||||||
public override bool IsPresent => base.IsPresent || State.Value == ArmedState.Idle && Time.Current >= HitObject.StartTime - TIME_PREEMPT;
|
public override bool IsPresent => base.IsPresent || State.Value == ArmedState.Idle && Time.Current >= HitObject.StartTime - HitObject.TimePreempt;
|
||||||
|
|
||||||
protected DrawableOsuHitObject(OsuHitObject hitObject)
|
protected DrawableOsuHitObject(OsuHitObject hitObject)
|
||||||
: base(hitObject)
|
: base(hitObject)
|
||||||
{
|
{
|
||||||
|
FadeInDuration = hitObject.TimeFadein;
|
||||||
|
|
||||||
AccentColour = HitObject.ComboColour;
|
AccentColour = HitObject.ComboColour;
|
||||||
Alpha = 0;
|
Alpha = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected sealed override void UpdateState(ArmedState state)
|
protected sealed override void UpdateState(ArmedState state)
|
||||||
{
|
{
|
||||||
double transformTime = HitObject.StartTime - TIME_PREEMPT;
|
double transformTime = HitObject.StartTime - HitObject.TimePreempt;
|
||||||
|
|
||||||
base.ApplyTransformsAt(transformTime, true);
|
base.ApplyTransformsAt(transformTime, true);
|
||||||
base.ClearTransformsAfter(transformTime, true);
|
base.ClearTransformsAfter(transformTime, true);
|
||||||
@ -38,7 +37,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
{
|
{
|
||||||
UpdatePreemptState();
|
UpdatePreemptState();
|
||||||
|
|
||||||
using (BeginDelayedSequence(TIME_PREEMPT + (Judgements.FirstOrDefault()?.TimeOffset ?? 0), true))
|
using (BeginDelayedSequence(HitObject.TimePreempt + (Judgements.FirstOrDefault()?.TimeOffset ?? 0), true))
|
||||||
UpdateCurrentState(state);
|
UpdateCurrentState(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -191,14 +191,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
base.UpdatePreemptState();
|
base.UpdatePreemptState();
|
||||||
|
|
||||||
circleContainer.ScaleTo(spinner.Scale * 0.3f);
|
circleContainer.ScaleTo(spinner.Scale * 0.3f);
|
||||||
circleContainer.ScaleTo(spinner.Scale, TIME_PREEMPT / 1.4f, Easing.OutQuint);
|
circleContainer.ScaleTo(spinner.Scale, HitObject.TimePreempt / 1.4f, Easing.OutQuint);
|
||||||
|
|
||||||
Disc.RotateTo(-720);
|
Disc.RotateTo(-720);
|
||||||
symbol.RotateTo(-720);
|
symbol.RotateTo(-720);
|
||||||
|
|
||||||
mainContainer
|
mainContainer
|
||||||
.ScaleTo(0)
|
.ScaleTo(0)
|
||||||
.ScaleTo(spinner.Scale * circle.DrawHeight / DrawHeight * 1.4f, TIME_PREEMPT - 150, Easing.OutQuint)
|
.ScaleTo(spinner.Scale * circle.DrawHeight / DrawHeight * 1.4f, HitObject.TimePreempt - 150, Easing.OutQuint)
|
||||||
.Then()
|
.Then()
|
||||||
.ScaleTo(1, 500, Easing.OutQuint);
|
.ScaleTo(1, 500, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
|
@ -167,7 +167,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
public void UpdateProgress(double progress, int repeat)
|
public void UpdateProgress(double progress, int repeat)
|
||||||
{
|
{
|
||||||
double start = 0;
|
double start = 0;
|
||||||
double end = snakingIn ? MathHelper.Clamp((Time.Current - (slider.StartTime - DrawableOsuHitObject.TIME_PREEMPT)) / DrawableOsuHitObject.TIME_FADEIN, 0, 1) : 1;
|
double end = snakingIn ? MathHelper.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / slider.TimeFadein, 0, 1) : 1;
|
||||||
|
|
||||||
if (repeat >= slider.RepeatCount - 1)
|
if (repeat >= slider.RepeatCount - 1)
|
||||||
{
|
{
|
||||||
|
@ -20,6 +20,9 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
private const double hit_window_100 = 80;
|
private const double hit_window_100 = 80;
|
||||||
private const double hit_window_300 = 30;
|
private const double hit_window_300 = 30;
|
||||||
|
|
||||||
|
public float TimePreempt = 600;
|
||||||
|
public float TimeFadein = 400;
|
||||||
|
|
||||||
public Vector2 Position { get; set; }
|
public Vector2 Position { get; set; }
|
||||||
public float X => Position.X;
|
public float X => Position.X;
|
||||||
public float Y => Position.Y;
|
public float Y => Position.Y;
|
||||||
@ -72,6 +75,13 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
{
|
{
|
||||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||||
|
|
||||||
|
if (difficulty.ApproachRate >= 5)
|
||||||
|
TimePreempt = 1200 - (difficulty.ApproachRate - 5) * 150;
|
||||||
|
else
|
||||||
|
TimePreempt = 1800 - difficulty.ApproachRate * 120;
|
||||||
|
TimeFadein = TimePreempt / 3;
|
||||||
|
|
||||||
|
|
||||||
Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2;
|
Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ using osu.Game.Rulesets.Osu.Objects.Drawables;
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Replays;
|
using osu.Game.Rulesets.Replays;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
@ -133,7 +134,7 @@ namespace osu.Game.Rulesets.Osu.Replays
|
|||||||
// Do some nice easing for cursor movements
|
// Do some nice easing for cursor movements
|
||||||
if (Frames.Count > 0)
|
if (Frames.Count > 0)
|
||||||
{
|
{
|
||||||
moveToHitObject(h.StartTime, startPosition, h.Radius, easing);
|
moveToHitObject(h, startPosition, easing);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add frames to click the hitobject
|
// Add frames to click the hitobject
|
||||||
@ -191,12 +192,12 @@ namespace osu.Game.Rulesets.Osu.Replays
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void moveToHitObject(double targetTime, Vector2 targetPos, double hitObjectRadius, Easing easing)
|
private void moveToHitObject(OsuHitObject h, Vector2 targetPos, Easing easing)
|
||||||
{
|
{
|
||||||
ReplayFrame lastFrame = Frames[Frames.Count - 1];
|
ReplayFrame lastFrame = Frames[Frames.Count - 1];
|
||||||
|
|
||||||
// Wait until Auto could "see and react" to the next note.
|
// Wait until Auto could "see and react" to the next note.
|
||||||
double waitTime = targetTime - Math.Max(0.0, DrawableOsuHitObject.TIME_PREEMPT - reactionTime);
|
double waitTime = h.StartTime - Math.Max(0.0, h.TimePreempt - reactionTime);
|
||||||
if (waitTime > lastFrame.Time)
|
if (waitTime > lastFrame.Time)
|
||||||
{
|
{
|
||||||
lastFrame = new ReplayFrame(waitTime, lastFrame.MouseX, lastFrame.MouseY, lastFrame.ButtonState);
|
lastFrame = new ReplayFrame(waitTime, lastFrame.MouseX, lastFrame.MouseY, lastFrame.ButtonState);
|
||||||
@ -205,17 +206,17 @@ namespace osu.Game.Rulesets.Osu.Replays
|
|||||||
|
|
||||||
Vector2 lastPosition = lastFrame.Position;
|
Vector2 lastPosition = lastFrame.Position;
|
||||||
|
|
||||||
double timeDifference = ApplyModsToTime(targetTime - lastFrame.Time);
|
double timeDifference = ApplyModsToTime(h.StartTime - lastFrame.Time);
|
||||||
|
|
||||||
// Only "snap" to hitcircles if they are far enough apart. As the time between hitcircles gets shorter the snapping threshold goes up.
|
// Only "snap" to hitcircles if they are far enough apart. As the time between hitcircles gets shorter the snapping threshold goes up.
|
||||||
if (timeDifference > 0 && // Sanity checks
|
if (timeDifference > 0 && // Sanity checks
|
||||||
((lastPosition - targetPos).Length > hitObjectRadius * (1.5 + 100.0 / timeDifference) || // Either the distance is big enough
|
((lastPosition - targetPos).Length > h.Radius * (1.5 + 100.0 / timeDifference) || // Either the distance is big enough
|
||||||
timeDifference >= 266)) // ... or the beats are slow enough to tap anyway.
|
timeDifference >= 266)) // ... or the beats are slow enough to tap anyway.
|
||||||
{
|
{
|
||||||
// Perform eased movement
|
// Perform eased movement
|
||||||
for (double time = lastFrame.Time + FrameDelay; time < targetTime; time += FrameDelay)
|
for (double time = lastFrame.Time + FrameDelay; time < h.StartTime; time += FrameDelay)
|
||||||
{
|
{
|
||||||
Vector2 currentPosition = Interpolation.ValueAt(time, lastPosition, targetPos, lastFrame.Time, targetTime, easing);
|
Vector2 currentPosition = Interpolation.ValueAt(time, lastPosition, targetPos, lastFrame.Time, h.StartTime, easing);
|
||||||
AddFrameToReplay(new ReplayFrame((int)time, currentPosition.X, currentPosition.Y, lastFrame.ButtonState));
|
AddFrameToReplay(new ReplayFrame((int)time, currentPosition.X, currentPosition.Y, lastFrame.ButtonState));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user