mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 14:17:26 +08:00
Rework osu! hidden mod to avoid storing hitobjects
This commit is contained in:
parent
a5bb194cb8
commit
e3bbc2b128
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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; }
|
||||||
|
@ -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();
|
||||||
|
Loading…
Reference in New Issue
Block a user