mirror of
https://github.com/ppy/osu.git
synced 2025-01-12 18:23:04 +08:00
Merge pull request #22543 from Cwazywierdo/hitcircle-late-miss-fade
Fix hit circle late-miss fading differences compared to stable
This commit is contained in:
commit
ee87a29376
156
osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLateFade.cs
Normal file
156
osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLateFade.cs
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
// 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.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Tests
|
||||||
|
{
|
||||||
|
public partial class TestSceneHitCircleLateFade : OsuTestScene
|
||||||
|
{
|
||||||
|
private float? alphaAtMiss;
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestHitCircleClassicMod()
|
||||||
|
{
|
||||||
|
AddStep("Create hit circle", () =>
|
||||||
|
{
|
||||||
|
SelectedMods.Value = new Mod[] { new OsuModClassic() };
|
||||||
|
createCircle();
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("Wait until circle is missed", () => alphaAtMiss.IsNotNull());
|
||||||
|
AddAssert("Transparent when missed", () => alphaAtMiss == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestHitCircleClassicAndFullHiddenMods()
|
||||||
|
{
|
||||||
|
AddStep("Create hit circle", () =>
|
||||||
|
{
|
||||||
|
SelectedMods.Value = new Mod[] { new OsuModHidden(), new OsuModClassic() };
|
||||||
|
createCircle();
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("Wait until circle is missed", () => alphaAtMiss.IsNotNull());
|
||||||
|
AddAssert("Transparent when missed", () => alphaAtMiss == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestHitCircleClassicAndApproachCircleOnlyHiddenMods()
|
||||||
|
{
|
||||||
|
AddStep("Create hit circle", () =>
|
||||||
|
{
|
||||||
|
SelectedMods.Value = new Mod[] { new OsuModHidden { OnlyFadeApproachCircles = { Value = true } }, new OsuModClassic() };
|
||||||
|
createCircle();
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("Wait until circle is missed", () => alphaAtMiss.IsNotNull());
|
||||||
|
AddAssert("Transparent when missed", () => alphaAtMiss == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestHitCircleNoMod()
|
||||||
|
{
|
||||||
|
AddStep("Create hit circle", () =>
|
||||||
|
{
|
||||||
|
SelectedMods.Value = Array.Empty<Mod>();
|
||||||
|
createCircle();
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("Wait until circle is missed", () => alphaAtMiss.IsNotNull());
|
||||||
|
AddAssert("Opaque when missed", () => alphaAtMiss == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSliderClassicMod()
|
||||||
|
{
|
||||||
|
AddStep("Create slider", () =>
|
||||||
|
{
|
||||||
|
SelectedMods.Value = new Mod[] { new OsuModClassic() };
|
||||||
|
createSlider();
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("Wait until head circle is missed", () => alphaAtMiss.IsNotNull());
|
||||||
|
AddAssert("Head circle transparent when missed", () => alphaAtMiss == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSliderNoMod()
|
||||||
|
{
|
||||||
|
AddStep("Create slider", () =>
|
||||||
|
{
|
||||||
|
SelectedMods.Value = Array.Empty<Mod>();
|
||||||
|
createSlider();
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("Wait until head circle is missed", () => alphaAtMiss.IsNotNull());
|
||||||
|
AddAssert("Head circle opaque when missed", () => alphaAtMiss == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createCircle()
|
||||||
|
{
|
||||||
|
alphaAtMiss = null;
|
||||||
|
|
||||||
|
DrawableHitCircle drawableHitCircle = new DrawableHitCircle(new HitCircle
|
||||||
|
{
|
||||||
|
StartTime = Time.Current + 500,
|
||||||
|
Position = new Vector2(250)
|
||||||
|
});
|
||||||
|
|
||||||
|
foreach (var mod in SelectedMods.Value.OfType<IApplicableToDrawableHitObject>())
|
||||||
|
mod.ApplyToDrawableHitObject(drawableHitCircle);
|
||||||
|
|
||||||
|
drawableHitCircle.HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||||
|
|
||||||
|
drawableHitCircle.OnNewResult += (_, _) =>
|
||||||
|
{
|
||||||
|
alphaAtMiss = drawableHitCircle.Alpha;
|
||||||
|
};
|
||||||
|
|
||||||
|
Child = drawableHitCircle;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createSlider()
|
||||||
|
{
|
||||||
|
alphaAtMiss = null;
|
||||||
|
|
||||||
|
DrawableSlider drawableSlider = new DrawableSlider(new Slider
|
||||||
|
{
|
||||||
|
StartTime = Time.Current + 500,
|
||||||
|
Position = new Vector2(250),
|
||||||
|
Path = new SliderPath(PathType.Linear, new[]
|
||||||
|
{
|
||||||
|
Vector2.Zero,
|
||||||
|
new Vector2(0, 100),
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
drawableSlider.HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||||
|
|
||||||
|
drawableSlider.OnLoadComplete += _ =>
|
||||||
|
{
|
||||||
|
foreach (var mod in SelectedMods.Value.OfType<IApplicableToDrawableHitObject>())
|
||||||
|
mod.ApplyToDrawableHitObject(drawableSlider.HeadCircle);
|
||||||
|
|
||||||
|
drawableSlider.HeadCircle.OnNewResult += (_, _) =>
|
||||||
|
{
|
||||||
|
alphaAtMiss = drawableSlider.HeadCircle.Alpha;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
Child = drawableSlider;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
@ -11,6 +12,7 @@ using osu.Game.Rulesets.Objects.Drawables;
|
|||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.UI;
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Mods
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
@ -31,6 +33,11 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
[SettingSource("Always play a slider's tail sample", "Always plays a slider's tail sample regardless of whether it was hit or not.")]
|
[SettingSource("Always play a slider's tail sample", "Always plays a slider's tail sample regardless of whether it was hit or not.")]
|
||||||
public Bindable<bool> AlwaysPlayTailSample { get; } = new BindableBool(true);
|
public Bindable<bool> AlwaysPlayTailSample { get; } = new BindableBool(true);
|
||||||
|
|
||||||
|
[SettingSource("Fade out hit circles earlier", "Make hit circles fade out into a miss, rather than after it.")]
|
||||||
|
public Bindable<bool> FadeHitCircleEarly { get; } = new Bindable<bool>(true);
|
||||||
|
|
||||||
|
private bool usingHiddenFading;
|
||||||
|
|
||||||
public void ApplyToHitObject(HitObject hitObject)
|
public void ApplyToHitObject(HitObject hitObject)
|
||||||
{
|
{
|
||||||
switch (hitObject)
|
switch (hitObject)
|
||||||
@ -51,6 +58,8 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
|
|
||||||
if (ClassicNoteLock.Value)
|
if (ClassicNoteLock.Value)
|
||||||
osuRuleset.Playfield.HitPolicy = new ObjectOrderedHitPolicy();
|
osuRuleset.Playfield.HitPolicy = new ObjectOrderedHitPolicy();
|
||||||
|
|
||||||
|
usingHiddenFading = drawableRuleset.Mods.OfType<OsuModHidden>().SingleOrDefault()?.OnlyFadeApproachCircles.Value == false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ApplyToDrawableHitObject(DrawableHitObject obj)
|
public void ApplyToDrawableHitObject(DrawableHitObject obj)
|
||||||
@ -59,12 +68,32 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
{
|
{
|
||||||
case DrawableSliderHead head:
|
case DrawableSliderHead head:
|
||||||
head.TrackFollowCircle = !NoSliderHeadMovement.Value;
|
head.TrackFollowCircle = !NoSliderHeadMovement.Value;
|
||||||
|
if (FadeHitCircleEarly.Value && !usingHiddenFading)
|
||||||
|
applyEarlyFading(head);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DrawableSliderTail tail:
|
case DrawableSliderTail tail:
|
||||||
tail.SamplePlaysOnlyOnHit = !AlwaysPlayTailSample.Value;
|
tail.SamplePlaysOnlyOnHit = !AlwaysPlayTailSample.Value;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case DrawableHitCircle circle:
|
||||||
|
if (FadeHitCircleEarly.Value && !usingHiddenFading)
|
||||||
|
applyEarlyFading(circle);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void applyEarlyFading(DrawableHitCircle circle)
|
||||||
|
{
|
||||||
|
circle.ApplyCustomUpdateState += (o, _) =>
|
||||||
|
{
|
||||||
|
using (o.BeginAbsoluteSequence(o.StateUpdateTime))
|
||||||
|
{
|
||||||
|
double okWindow = o.HitObject.HitWindows.WindowFor(HitResult.Ok);
|
||||||
|
double lateMissFadeTime = o.HitObject.HitWindows.WindowFor(HitResult.Meh) - okWindow;
|
||||||
|
o.Delay(okWindow).FadeOut(lateMissFadeTime);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user