diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs index 4d8ddcd5816f..8d3eee2445f8 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs @@ -12,6 +12,7 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Play.HUD; using osu.Game.Skinning; +using osuTK; namespace osu.Game.Tests.Visual.Gameplay { @@ -20,9 +21,9 @@ public partial class TestSceneSkinnableHealthDisplay : SkinnableHUDComponentTest [Cached(typeof(HealthProcessor))] private HealthProcessor healthProcessor = new DrainingHealthProcessor(0); - protected override Drawable CreateArgonImplementation() => new ArgonHealthDisplay(); - protected override Drawable CreateDefaultImplementation() => new DefaultHealthDisplay(); - protected override Drawable CreateLegacyImplementation() => new LegacyHealthDisplay(); + protected override Drawable CreateArgonImplementation() => new ArgonHealthDisplay { Scale = new Vector2(0.6f) }; + protected override Drawable CreateDefaultImplementation() => new DefaultHealthDisplay { Scale = new Vector2(0.6f) }; + protected override Drawable CreateLegacyImplementation() => new LegacyHealthDisplay { Scale = new Vector2(0.6f) }; [SetUpSteps] public void SetUpSteps() @@ -62,4 +63,4 @@ public void TestHealthDisplayIncrementing() }, 3); } } -} \ No newline at end of file +} diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs index 62a4b958c262..f7059c5853c1 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs @@ -46,7 +46,7 @@ public partial class ArgonHealthDisplay : HealthDisplay, ISerialisableDrawable private readonly List missBarVertices = new List(); private readonly List healthBarVertices = new List(); - private double glowBarValue = 1; + private double glowBarValue; public double GlowBarValue { @@ -61,7 +61,7 @@ public double GlowBarValue } } - private double healthBarValue = 1; + private double healthBarValue; public double HealthBarValue { diff --git a/osu.Game/Screens/Play/HUD/FailingLayer.cs b/osu.Game/Screens/Play/HUD/FailingLayer.cs index 67e7ae8f3f1f..3954e23cbe31 100644 --- a/osu.Game/Screens/Play/HUD/FailingLayer.cs +++ b/osu.Game/Screens/Play/HUD/FailingLayer.cs @@ -29,6 +29,8 @@ public partial class FailingLayer : HealthDisplay /// public readonly Bindable ShowHealth = new Bindable(); + protected override bool PlayInitialIncreaseAnimation => false; + private const float max_alpha = 0.4f; private const int fade_time = 400; private const float gradient_size = 0.2f; diff --git a/osu.Game/Screens/Play/HUD/HealthDisplay.cs b/osu.Game/Screens/Play/HUD/HealthDisplay.cs index 5131f93ca29e..986efe30367e 100644 --- a/osu.Game/Screens/Play/HUD/HealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/HealthDisplay.cs @@ -6,7 +6,9 @@ using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Threading; using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; @@ -23,12 +25,18 @@ public abstract partial class HealthDisplay : CompositeDrawable [Resolved] protected HealthProcessor HealthProcessor { get; private set; } = null!; - public Bindable Current { get; } = new BindableDouble(1) + protected virtual bool PlayInitialIncreaseAnimation => true; + + public Bindable Current { get; } = new BindableDouble { MinValue = 0, MaxValue = 1 }; + private BindableNumber health = null!; + + private ScheduledDelegate? initialIncrease; + /// /// Triggered when a is a successful hit, signaling the health display to perform a flash animation (if designed to do so). /// @@ -52,14 +60,56 @@ protected override void LoadComplete() { base.LoadComplete(); - Current.BindTo(HealthProcessor.Health); HealthProcessor.NewJudgement += onNewJudgement; + // Don't bind directly so we can animate the startup procedure. + health = HealthProcessor.Health.GetBoundCopy(); + health.BindValueChanged(h => + { + finishInitialAnimation(); + Current.Value = h.NewValue; + }); + if (hudOverlay != null) showHealthBar.BindTo(hudOverlay.ShowHealthBar); // this probably shouldn't be operating on `this.` showHealthBar.BindValueChanged(healthBar => this.FadeTo(healthBar.NewValue ? 1 : 0, HUDOverlay.FADE_DURATION, HUDOverlay.FADE_EASING), true); + + if (PlayInitialIncreaseAnimation) + startInitialAnimation(); + else + Current.Value = 1; + } + + private void startInitialAnimation() + { + // TODO: this should run in gameplay time, including showing a larger increase when skipping. + // TODO: it should also start increasing relative to the first hitobject. + const double increase_delay = 150; + + initialIncrease = Scheduler.AddDelayed(() => + { + double newValue = Current.Value + 0.05f; + this.TransformBindableTo(Current, newValue, increase_delay); + Flash(new JudgementResult(new HitObject(), new Judgement())); + + if (newValue >= 1) + finishInitialAnimation(); + }, increase_delay, true); + } + + private void finishInitialAnimation() + { + initialIncrease?.Cancel(); + initialIncrease = null; + + // aside from the repeating `initialIncrease` scheduled task, + // there may also be a `Current` transform in progress from that schedule. + // ensure it plays out fully, to prevent changes to `Current.Value` being discarded by the ongoing transform. + // and yes, this funky `targetMember` spec is seemingly the only way to do this + // (see: https://github.com/ppy/osu-framework/blob/fe2769171c6e26d1b6fdd6eb7ea8353162fe9065/osu.Framework/Graphics/Transforms/TransformBindable.cs#L21) + FinishTransforms(targetMember: $"{Current.GetHashCode()}.{nameof(Current.Value)}"); } private void onNewJudgement(JudgementResult judgement) diff --git a/osu.Game/Skinning/LegacyHealthDisplay.cs b/osu.Game/Skinning/LegacyHealthDisplay.cs index f785022f8416..08add79fc161 100644 --- a/osu.Game/Skinning/LegacyHealthDisplay.cs +++ b/osu.Game/Skinning/LegacyHealthDisplay.cs @@ -66,6 +66,7 @@ private void load(ISkinSource source) marker.Current.BindTo(Current); maxFillWidth = fill.Width; + fill.Width = 0; } protected override void Update()