1
0
mirror of https://github.com/ppy/osu.git synced 2024-09-21 23:27:25 +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. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using System.Collections.Generic; using System.Diagnostics;
using System.Linq; using System.Linq;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables; 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); 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) base.ApplyToBeatmap(beatmap);
d.HitObjectApplied += applyFadeInAdjustment;
base.ApplyToDrawableHitObjects(drawables); foreach (var obj in beatmap.HitObjects.OfType<OsuHitObject>())
} applyFadeInAdjustment(obj);
private void applyFadeInAdjustment(DrawableHitObject hitObject) static void applyFadeInAdjustment(OsuHitObject osuObject)
{ {
if (!(hitObject is DrawableOsuHitObject d)) osuObject.TimeFadeIn = osuObject.TimePreempt * fade_in_duration_multiplier;
return; foreach (var nested in osuObject.NestedHitObjects.OfType<OsuHitObject>())
applyFadeInAdjustment(nested);
d.HitObject.TimeFadeIn = d.HitObject.TimePreempt * fade_in_duration_multiplier; }
foreach (var nested in d.NestedHitObjects)
applyFadeInAdjustment(nested);
} }
protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state)
@ -56,37 +53,27 @@ namespace osu.Game.Rulesets.Osu.Mods
applyState(hitObject, false); 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; return;
var h = d.HitObject; OsuHitObject hitObject = drawableOsuObject.HitObject;
var fadeOutStartTime = h.StartTime - h.TimePreempt + h.TimeFadeIn; (double startTime, double duration) fadeOut = getFadeOutParameters(drawableOsuObject);
var fadeOutDuration = h.TimePreempt * fade_out_duration_multiplier;
// new duration from completed fade in to end (before fading out) switch (drawableObject)
var longFadeDuration = h.GetEndTime() - fadeOutStartTime;
switch (drawable)
{ {
case DrawableSliderTail sliderTail: case DrawableSliderTail _:
// use stored values from head circle to achieve same fade sequence. using (drawableObject.BeginAbsoluteSequence(fadeOut.startTime, true))
var tailFadeOutParameters = getFadeOutParametersFromSliderHead(h); drawableObject.FadeOut(fadeOut.duration);
using (drawable.BeginAbsoluteSequence(tailFadeOutParameters.startTime, true))
sliderTail.FadeOut(tailFadeOutParameters.duration);
break; break;
case DrawableSliderRepeat sliderRepeat: case DrawableSliderRepeat sliderRepeat:
// use stored values from head circle to achieve same fade sequence. using (drawableObject.BeginAbsoluteSequence(fadeOut.startTime, true))
var repeatFadeOutParameters = getFadeOutParametersFromSliderHead(h);
using (drawable.BeginAbsoluteSequence(repeatFadeOutParameters.startTime, true))
// only apply to circle piece reverse arrow is not affected by hidden. // only apply to circle piece reverse arrow is not affected by hidden.
sliderRepeat.CirclePiece.FadeOut(repeatFadeOutParameters.duration); sliderRepeat.CirclePiece.FadeOut(fadeOut.duration);
break; break;
@ -101,29 +88,23 @@ namespace osu.Game.Rulesets.Osu.Mods
else else
{ {
// we don't want to see the approach circle // 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(); circle.ApproachCircle.Hide();
} }
// fade out immediately after fade in. using (drawableObject.BeginAbsoluteSequence(fadeOut.startTime, true))
using (drawable.BeginAbsoluteSequence(fadeOutStartTime, true)) fadeTarget.FadeOut(fadeOut.duration);
fadeTarget.FadeOut(fadeOutDuration);
break; break;
case DrawableSlider slider: case DrawableSlider slider:
associateNestedSliderCirclesWithHead(slider.HitObject); using (slider.BeginAbsoluteSequence(fadeOut.startTime, true))
slider.Body.FadeOut(fadeOut.duration, Easing.Out);
using (slider.BeginAbsoluteSequence(fadeOutStartTime, true))
slider.Body.FadeOut(longFadeDuration, Easing.Out);
break; break;
case DrawableSliderTick sliderTick: case DrawableSliderTick sliderTick:
// slider ticks fade out over up to one second using (sliderTick.BeginAbsoluteSequence(fadeOut.startTime, true))
var tickFadeOutDuration = Math.Min(sliderTick.HitObject.TimePreempt - DrawableSliderTick.ANIM_DURATION, 1000); sliderTick.FadeOut(fadeOut.duration);
using (sliderTick.BeginAbsoluteSequence(sliderTick.HitObject.StartTime - tickFadeOutDuration, true))
sliderTick.FadeOut(tickFadeOutDuration);
break; break;
@ -131,30 +112,55 @@ namespace osu.Game.Rulesets.Osu.Mods
// hide elements we don't care about. // hide elements we don't care about.
// todo: hide background // todo: hide background
using (spinner.BeginAbsoluteSequence(fadeOutStartTime + longFadeDuration, true)) using (spinner.BeginAbsoluteSequence(fadeOut.startTime, true))
spinner.FadeOut(fadeOutDuration); spinner.FadeOut(fadeOut.duration);
break; break;
} }
} }
private readonly Dictionary<HitObject, SliderHeadCircle> correspondingSliderHeadForObject = new Dictionary<HitObject, SliderHeadCircle>(); private (double startTime, double duration) getFadeOutParameters(DrawableOsuHitObject drawableObject)
private void associateNestedSliderCirclesWithHead(Slider slider)
{ {
var sliderHead = slider.NestedHitObjects.Single(obj => obj is SliderHeadCircle); switch (drawableObject)
foreach (var nested in slider.NestedHitObjects)
{ {
if ((nested is SliderRepeat || nested is SliderEndCircle) && !correspondingSliderHeadForObject.ContainsKey(nested)) case DrawableSliderTail tail:
correspondingSliderHeadForObject[nested] = (SliderHeadCircle)sliderHead; // 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) case DrawableSliderRepeat repeat:
{ // Use the same fade sequence as the slider head.
var sliderHead = correspondingSliderHeadForObject[h]; Debug.Assert(repeat.Slider != null);
return (sliderHead.StartTime - sliderHead.TimePreempt + sliderHead.TimeFadeIn, sliderHead.TimePreempt * fade_out_duration_multiplier); 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. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using System.Diagnostics;
using JetBrains.Annotations;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
@ -11,14 +13,15 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{ {
public class DrawableSliderHead : DrawableHitCircle public class DrawableSliderHead : DrawableHitCircle
{ {
[CanBeNull]
public Slider Slider => drawableSlider?.HitObject;
private readonly IBindable<int> pathVersion = new Bindable<int>(); private readonly IBindable<int> pathVersion = new Bindable<int>();
protected override OsuSkinComponents CirclePieceComponent => OsuSkinComponents.SliderHeadHitCircle; protected override OsuSkinComponents CirclePieceComponent => OsuSkinComponents.SliderHeadHitCircle;
private DrawableSlider drawableSlider; private DrawableSlider drawableSlider;
private Slider slider => drawableSlider?.HitObject;
public DrawableSliderHead() public DrawableSliderHead()
{ {
} }
@ -58,11 +61,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{ {
base.Update(); 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. //todo: we probably want to reconsider this before adding scoring, but it looks and feels nice.
if (!IsHit) if (!IsHit)
Position = slider.CurvePositionAt(completionProgress); Position = Slider.CurvePositionAt(completionProgress);
} }
public Action<double> OnShake; public Action<double> OnShake;
@ -71,8 +76,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private void updatePosition() private void updatePosition()
{ {
if (slider != null) if (Slider != null)
Position = HitObject.Position - slider.Position; Position = HitObject.Position - Slider.Position;
} }
} }
} }

View File

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

View File

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