1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 23:12:56 +08:00

Merge pull request #20398 from peppy/fix-missing-notelock-shake

Fix "note lock" shake not always animating
This commit is contained in:
Dan Balasescu 2022-09-22 20:07:38 +09:00 committed by GitHub
commit 5dbe73ad0e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 69 additions and 51 deletions

View File

@ -8,7 +8,6 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Containers;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Osu.Objects;
@ -88,7 +87,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
if (!objects.Any())
return false;
return objects.All(o => Precision.AlmostEquals(o.ChildrenOfType<ShakeContainer>().First().Children.OfType<Container>().Single().Scale.X, target));
return objects.All(o => Precision.AlmostEquals(o.ChildrenOfType<Container>().First().Scale.X, target));
}
private bool checkSomeHit()

View File

@ -12,6 +12,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Game.Graphics.Containers;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Judgements;
@ -47,12 +48,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
}
private ShakeContainer shakeContainer;
[BackgroundDependencyLoader]
private void load()
{
Origin = Anchor.Centre;
InternalChildren = new Drawable[]
AddRangeInternal(new Drawable[]
{
scaleContainer = new Container
{
@ -72,22 +75,30 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
return true;
},
},
CirclePiece = new SkinnableDrawable(new OsuSkinComponent(CirclePieceComponent), _ => new MainCirclePiece())
shakeContainer = new ShakeContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
ApproachCircle = new ProxyableSkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.ApproachCircle), _ => new DefaultApproachCircle())
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
ShakeDuration = 30,
RelativeSizeAxes = Axes.Both,
Alpha = 0,
Scale = new Vector2(4),
Children = new Drawable[]
{
CirclePiece = new SkinnableDrawable(new OsuSkinComponent(CirclePieceComponent), _ => new MainCirclePiece())
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
ApproachCircle = new ProxyableSkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.ApproachCircle), _ => new DefaultApproachCircle())
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Alpha = 0,
Scale = new Vector2(4),
}
}
}
}
},
};
});
Size = HitArea.DrawSize;
@ -123,6 +134,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
}
}
public override void Shake() => shakeContainer.Shake();
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
Debug.Assert(HitObject.HitWindows != null);
@ -139,7 +152,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
if (result == HitResult.None || CheckHittable?.Invoke(this, Time.Current) == false)
{
Shake(Math.Abs(timeOffset) - HitObject.HitWindows.WindowFor(HitResult.Miss));
Shake();
return;
}

View File

@ -6,17 +6,16 @@
using System;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Primitives;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Graphics.Containers;
using osuTK;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
public class DrawableOsuHitObject : DrawableHitObject<OsuHitObject>
public abstract class DrawableOsuHitObject : DrawableHitObject<OsuHitObject>
{
public readonly IBindable<Vector2> PositionBindable = new Bindable<Vector2>();
public readonly IBindable<int> StackHeightBindable = new Bindable<int>();
@ -34,8 +33,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
/// </summary>
public Func<DrawableHitObject, double, bool> CheckHittable;
private ShakeContainer shakeContainer;
protected DrawableOsuHitObject(OsuHitObject hitObject)
: base(hitObject)
{
@ -45,12 +42,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private void load()
{
Alpha = 0;
base.AddInternal(shakeContainer = new ShakeContainer
{
ShakeDuration = 30,
RelativeSizeAxes = Axes.Both
});
}
protected override void OnApply()
@ -73,18 +64,15 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
ScaleBindable.UnbindFrom(HitObject.ScaleBindable);
}
// Forward all internal management to shakeContainer.
// This is a bit ugly but we don't have the concept of InternalContent so it'll have to do for now. (https://github.com/ppy/osu-framework/issues/1690)
protected override void AddInternal(Drawable drawable) => shakeContainer.Add(drawable);
protected override void ClearInternal(bool disposeChildren = true) => shakeContainer.Clear(disposeChildren);
protected override bool RemoveInternal(Drawable drawable, bool disposeImmediately) => shakeContainer.Remove(drawable, disposeImmediately);
protected sealed override double InitialLifetimeOffset => HitObject.TimePreempt;
private OsuInputManager osuActionInputManager;
internal OsuInputManager OsuActionInputManager => osuActionInputManager ??= GetContainingInputManager() as OsuInputManager;
public virtual void Shake(double maximumLength) => shakeContainer.Shake(maximumLength);
/// <summary>
/// Shake the hit object in case it was clicked far too early or late (aka "note lock").
/// </summary>
public virtual void Shake() { }
/// <summary>
/// Causes this <see cref="DrawableOsuHitObject"/> to get missed, disregarding all conditions in implementations of <see cref="DrawableHitObject.CheckForResult"/>.

View File

@ -11,6 +11,7 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Audio;
using osu.Game.Graphics.Containers;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Skinning;
@ -34,6 +35,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public SkinnableDrawable Body { get; private set; }
private ShakeContainer shakeContainer;
/// <summary>
/// A target container which can be used to add top level elements to the slider's display.
/// Intended to be used for proxy purposes only.
@ -74,17 +77,26 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
[BackgroundDependencyLoader]
private void load()
{
InternalChildren = new Drawable[]
AddRangeInternal(new Drawable[]
{
Body = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderBody), _ => new DefaultSliderBody(), confineMode: ConfineMode.NoScaling),
tailContainer = new Container<DrawableSliderTail> { RelativeSizeAxes = Axes.Both },
tickContainer = new Container<DrawableSliderTick> { RelativeSizeAxes = Axes.Both },
repeatContainer = new Container<DrawableSliderRepeat> { RelativeSizeAxes = Axes.Both },
shakeContainer = new ShakeContainer
{
ShakeDuration = 30,
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
Body = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderBody), _ => new DefaultSliderBody(), confineMode: ConfineMode.NoScaling),
tailContainer = new Container<DrawableSliderTail> { RelativeSizeAxes = Axes.Both },
tickContainer = new Container<DrawableSliderTick> { RelativeSizeAxes = Axes.Both },
repeatContainer = new Container<DrawableSliderRepeat> { RelativeSizeAxes = Axes.Both },
}
},
// slider head is not included in shake as it handles hit detection, and handles its own shaking.
headContainer = new Container<DrawableSliderHead> { RelativeSizeAxes = Axes.Both },
OverlayElementContainer = new Container { RelativeSizeAxes = Axes.Both, },
Ball,
slidingSample = new PausableSkinnableSound { Looping = true }
};
});
PositionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition);
StackHeightBindable.BindValueChanged(_ => Position = HitObject.StackedPosition);
@ -109,6 +121,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
PathVersion.BindTo(HitObject.Path.Version);
}
public override void Shake() => shakeContainer.Shake();
protected override void OnFree()
{
base.OnFree();

View File

@ -63,7 +63,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
pathVersion.BindTo(DrawableSlider.PathVersion);
OnShake = DrawableSlider.Shake;
CheckHittable = (d, t) => DrawableSlider.CheckHittable?.Invoke(d, t) ?? true;
}
@ -96,9 +95,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
return result.IsHit() ? HitResult.LargeTickHit : HitResult.LargeTickMiss;
}
public Action<double> OnShake;
public override void Shake(double maximumLength) => OnShake?.Invoke(maximumLength);
public override void Shake()
{
base.Shake();
DrawableSlider.Shake();
}
private void updatePosition()
{

View File

@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Origin = Anchor.Centre;
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
InternalChild = scaleContainer = new Container
AddInternal(scaleContainer = new Container
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
},
Arrow = new ReverseArrowPiece(),
}
};
});
ScaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue));
}

View File

@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Origin = Anchor.Centre;
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
InternalChildren = new Drawable[]
AddRangeInternal(new Drawable[]
{
scaleContainer = new Container
{
@ -71,7 +71,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
CirclePiece = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderTailHitCircle), _ => Empty())
}
},
};
});
ScaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue));
}

View File

@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
Origin = Anchor.Centre;
InternalChild = scaleContainer = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderScorePoint), _ => new CircularContainer
AddInternal(scaleContainer = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderScorePoint), _ => new CircularContainer
{
Masking = true,
Origin = Anchor.Centre,
@ -61,7 +61,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
};
});
ScaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue));
}

View File

@ -172,7 +172,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
{
config.BindWith(OsuSetting.PositionalHitsoundsLevel, positionalHitsoundsLevel);
// Explicit non-virtual function call.
// Explicit non-virtual function call in case a DrawableHitObject overrides AddInternal.
base.AddInternal(Samples = new PausableSkinnableSound());
CurrentSkin = skinSource;
@ -405,7 +405,10 @@ namespace osu.Game.Rulesets.Objects.Drawables
/// </summary>
public event Action<DrawableHitObject, ArmedState> ApplyCustomUpdateState;
protected override void ClearInternal(bool disposeChildren = true) => throw new InvalidOperationException($"Should never clear a {nameof(DrawableHitObject)}");
protected override void ClearInternal(bool disposeChildren = true) =>
// See sample addition in load method.
throw new InvalidOperationException(
$"Should never clear a {nameof(DrawableHitObject)} as the base implementation adds components. If attempting to use {nameof(InternalChild)} or {nameof(InternalChildren)}, using {nameof(AddInternal)} or {nameof(AddRangeInternal)} instead.");
private void updateState(ArmedState newState, bool force = false)
{