1
0
mirror of https://github.com/ppy/osu.git synced 2024-09-21 21:27:24 +08:00

Rework osu! hidden mod to avoid storing hitobjects

This commit is contained in:
smoogipoo 2020-12-03 14:28:37 +09:00
parent a5bb194cb8
commit e3bbc2b128
4 changed files with 97 additions and 70 deletions

View File

@ -2,9 +2,10 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
@ -25,23 +26,19 @@ namespace osu.Game.Rulesets.Osu.Mods
protected override bool IsFirstAdjustableObject(HitObject hitObject) => !(hitObject is Spinner);
public override void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables)
public override void ApplyToBeatmap(IBeatmap beatmap)
{
foreach (var d in drawables)
d.HitObjectApplied += applyFadeInAdjustment;
base.ApplyToBeatmap(beatmap);
base.ApplyToDrawableHitObjects(drawables);
}
foreach (var obj in beatmap.HitObjects.OfType<OsuHitObject>())
applyFadeInAdjustment(obj);
private void applyFadeInAdjustment(DrawableHitObject hitObject)
{
if (!(hitObject is DrawableOsuHitObject d))
return;
d.HitObject.TimeFadeIn = d.HitObject.TimePreempt * fade_in_duration_multiplier;
foreach (var nested in d.NestedHitObjects)
applyFadeInAdjustment(nested);
static void applyFadeInAdjustment(OsuHitObject osuObject)
{
osuObject.TimeFadeIn = osuObject.TimePreempt * fade_in_duration_multiplier;
foreach (var nested in osuObject.NestedHitObjects.OfType<OsuHitObject>())
applyFadeInAdjustment(nested);
}
}
protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state)
@ -56,37 +53,27 @@ namespace osu.Game.Rulesets.Osu.Mods
applyState(hitObject, false);
}
private void applyState(DrawableHitObject drawable, bool increaseVisibility)
private void applyState(DrawableHitObject drawableObject, bool increaseVisibility)
{
if (!(drawable is DrawableOsuHitObject d))
if (!(drawableObject is DrawableOsuHitObject drawableOsuObject))
return;
var h = d.HitObject;
OsuHitObject hitObject = drawableOsuObject.HitObject;
var fadeOutStartTime = h.StartTime - h.TimePreempt + h.TimeFadeIn;
var fadeOutDuration = h.TimePreempt * fade_out_duration_multiplier;
(double startTime, double duration) fadeOut = getFadeOutParameters(drawableOsuObject);
// new duration from completed fade in to end (before fading out)
var longFadeDuration = h.GetEndTime() - fadeOutStartTime;
switch (drawable)
switch (drawableObject)
{
case DrawableSliderTail sliderTail:
// use stored values from head circle to achieve same fade sequence.
var tailFadeOutParameters = getFadeOutParametersFromSliderHead(h);
using (drawable.BeginAbsoluteSequence(tailFadeOutParameters.startTime, true))
sliderTail.FadeOut(tailFadeOutParameters.duration);
case DrawableSliderTail _:
using (drawableObject.BeginAbsoluteSequence(fadeOut.startTime, true))
drawableObject.FadeOut(fadeOut.duration);
break;
case DrawableSliderRepeat sliderRepeat:
// use stored values from head circle to achieve same fade sequence.
var repeatFadeOutParameters = getFadeOutParametersFromSliderHead(h);
using (drawable.BeginAbsoluteSequence(repeatFadeOutParameters.startTime, true))
using (drawableObject.BeginAbsoluteSequence(fadeOut.startTime, true))
// only apply to circle piece reverse arrow is not affected by hidden.
sliderRepeat.CirclePiece.FadeOut(repeatFadeOutParameters.duration);
sliderRepeat.CirclePiece.FadeOut(fadeOut.duration);
break;
@ -101,29 +88,23 @@ namespace osu.Game.Rulesets.Osu.Mods
else
{
// we don't want to see the approach circle
using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true))
using (circle.BeginAbsoluteSequence(hitObject.StartTime - hitObject.TimePreempt, true))
circle.ApproachCircle.Hide();
}
// fade out immediately after fade in.
using (drawable.BeginAbsoluteSequence(fadeOutStartTime, true))
fadeTarget.FadeOut(fadeOutDuration);
using (drawableObject.BeginAbsoluteSequence(fadeOut.startTime, true))
fadeTarget.FadeOut(fadeOut.duration);
break;
case DrawableSlider slider:
associateNestedSliderCirclesWithHead(slider.HitObject);
using (slider.BeginAbsoluteSequence(fadeOutStartTime, true))
slider.Body.FadeOut(longFadeDuration, Easing.Out);
using (slider.BeginAbsoluteSequence(fadeOut.startTime, true))
slider.Body.FadeOut(fadeOut.duration, Easing.Out);
break;
case DrawableSliderTick sliderTick:
// slider ticks fade out over up to one second
var tickFadeOutDuration = Math.Min(sliderTick.HitObject.TimePreempt - DrawableSliderTick.ANIM_DURATION, 1000);
using (sliderTick.BeginAbsoluteSequence(sliderTick.HitObject.StartTime - tickFadeOutDuration, true))
sliderTick.FadeOut(tickFadeOutDuration);
using (sliderTick.BeginAbsoluteSequence(fadeOut.startTime, true))
sliderTick.FadeOut(fadeOut.duration);
break;
@ -131,30 +112,55 @@ namespace osu.Game.Rulesets.Osu.Mods
// hide elements we don't care about.
// todo: hide background
using (spinner.BeginAbsoluteSequence(fadeOutStartTime + longFadeDuration, true))
spinner.FadeOut(fadeOutDuration);
using (spinner.BeginAbsoluteSequence(fadeOut.startTime, true))
spinner.FadeOut(fadeOut.duration);
break;
}
}
private readonly Dictionary<HitObject, SliderHeadCircle> correspondingSliderHeadForObject = new Dictionary<HitObject, SliderHeadCircle>();
private void associateNestedSliderCirclesWithHead(Slider slider)
private (double startTime, double duration) getFadeOutParameters(DrawableOsuHitObject drawableObject)
{
var sliderHead = slider.NestedHitObjects.Single(obj => obj is SliderHeadCircle);
foreach (var nested in slider.NestedHitObjects)
switch (drawableObject)
{
if ((nested is SliderRepeat || nested is SliderEndCircle) && !correspondingSliderHeadForObject.ContainsKey(nested))
correspondingSliderHeadForObject[nested] = (SliderHeadCircle)sliderHead;
}
}
case DrawableSliderTail tail:
// Use the same fade sequence as the slider head.
Debug.Assert(tail.Slider != null);
return getParameters(tail.Slider.HeadCircle);
private (double startTime, double duration) getFadeOutParametersFromSliderHead(OsuHitObject h)
{
var sliderHead = correspondingSliderHeadForObject[h];
return (sliderHead.StartTime - sliderHead.TimePreempt + sliderHead.TimeFadeIn, sliderHead.TimePreempt * fade_out_duration_multiplier);
case DrawableSliderRepeat repeat:
// Use the same fade sequence as the slider head.
Debug.Assert(repeat.Slider != null);
return getParameters(repeat.Slider.HeadCircle);
default:
return getParameters(drawableObject.HitObject);
}
static (double startTime, double duration) getParameters(OsuHitObject hitObject)
{
var fadeOutStartTime = hitObject.StartTime - hitObject.TimePreempt + hitObject.TimeFadeIn;
var fadeOutDuration = hitObject.TimePreempt * fade_out_duration_multiplier;
// new duration from completed fade in to end (before fading out)
var longFadeDuration = hitObject.GetEndTime() - fadeOutStartTime;
switch (hitObject)
{
case Slider _:
return (fadeOutStartTime, longFadeDuration);
case SliderTick _:
var tickFadeOutDuration = Math.Min(hitObject.TimePreempt - DrawableSliderTick.ANIM_DURATION, 1000);
return (hitObject.StartTime - tickFadeOutDuration, tickFadeOutDuration);
case Spinner _:
return (fadeOutStartTime + longFadeDuration, fadeOutDuration);
default:
return (fadeOutStartTime, fadeOutDuration);
}
}
}
}
}

View File

@ -2,6 +2,8 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Diagnostics;
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Game.Rulesets.Objects.Drawables;
@ -11,14 +13,15 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
public class DrawableSliderHead : DrawableHitCircle
{
[CanBeNull]
public Slider Slider => drawableSlider?.HitObject;
private readonly IBindable<int> pathVersion = new Bindable<int>();
protected override OsuSkinComponents CirclePieceComponent => OsuSkinComponents.SliderHeadHitCircle;
private DrawableSlider drawableSlider;
private Slider slider => drawableSlider?.HitObject;
public DrawableSliderHead()
{
}
@ -58,11 +61,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
base.Update();
double completionProgress = Math.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1);
Debug.Assert(Slider != null);
double completionProgress = Math.Clamp((Time.Current - Slider.StartTime) / Slider.Duration, 0, 1);
//todo: we probably want to reconsider this before adding scoring, but it looks and feels nice.
if (!IsHit)
Position = slider.CurvePositionAt(completionProgress);
Position = Slider.CurvePositionAt(completionProgress);
}
public Action<double> OnShake;
@ -71,8 +76,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private void updatePosition()
{
if (slider != null)
Position = HitObject.Position - slider.Position;
if (Slider != null)
Position = HitObject.Position - Slider.Position;
}
}
}

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@ -18,6 +19,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
public new SliderRepeat HitObject => (SliderRepeat)base.HitObject;
[CanBeNull]
public Slider Slider => drawableSlider?.HitObject;
private double animDuration;
public Drawable CirclePiece { get; private set; }

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System.Diagnostics;
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@ -15,6 +16,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
public new SliderTailCircle HitObject => (SliderTailCircle)base.HitObject;
[CanBeNull]
public Slider Slider => drawableSlider?.HitObject;
/// <summary>
/// The judgement text is provided by the <see cref="DrawableSlider"/>.
/// </summary>
@ -22,6 +26,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public bool Tracking { get; set; }
private DrawableSlider drawableSlider;
private SkinnableDrawable circlePiece;
private Container scaleContainer;
@ -59,6 +64,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
ScaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue));
}
protected override void OnParentReceived(DrawableHitObject parent)
{
base.OnParentReceived(parent);
drawableSlider = (DrawableSlider)parent;
}
protected override void UpdateInitialTransforms()
{
base.UpdateInitialTransforms();