Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add initial animation for health bars #24986

Merged
merged 7 commits into from
Oct 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -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()
Expand Down Expand Up @@ -62,4 +63,4 @@ public void TestHealthDisplayIncrementing()
}, 3);
}
}
}
}
4 changes: 2 additions & 2 deletions osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public partial class ArgonHealthDisplay : HealthDisplay, ISerialisableDrawable
private readonly List<Vector2> missBarVertices = new List<Vector2>();
private readonly List<Vector2> healthBarVertices = new List<Vector2>();

private double glowBarValue = 1;
private double glowBarValue;

public double GlowBarValue
{
Expand All @@ -61,7 +61,7 @@ public double GlowBarValue
}
}

private double healthBarValue = 1;
private double healthBarValue;

public double HealthBarValue
{
Expand Down
2 changes: 2 additions & 0 deletions osu.Game/Screens/Play/HUD/FailingLayer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ public partial class FailingLayer : HealthDisplay
/// </summary>
public readonly Bindable<bool> ShowHealth = new Bindable<bool>();

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;
Expand Down
54 changes: 52 additions & 2 deletions osu.Game/Screens/Play/HUD/HealthDisplay.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -23,12 +25,18 @@ public abstract partial class HealthDisplay : CompositeDrawable
[Resolved]
protected HealthProcessor HealthProcessor { get; private set; } = null!;

public Bindable<double> Current { get; } = new BindableDouble(1)
protected virtual bool PlayInitialIncreaseAnimation => true;

public Bindable<double> Current { get; } = new BindableDouble
{
MinValue = 0,
MaxValue = 1
};

private BindableNumber<double> health = null!;

private ScheduledDelegate? initialIncrease;

/// <summary>
/// Triggered when a <see cref="Judgement"/> is a successful hit, signaling the health display to perform a flash animation (if designed to do so).
/// </summary>
Expand All @@ -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)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wow

FinishTransforms(targetMember: $"{Current.GetHashCode()}.{nameof(Current.Value)}");
}

private void onNewJudgement(JudgementResult judgement)
Expand Down
1 change: 1 addition & 0 deletions osu.Game/Skinning/LegacyHealthDisplay.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ private void load(ISkinSource source)
marker.Current.BindTo(Current);

maxFillWidth = fill.Width;
fill.Width = 0;
}

protected override void Update()
Expand Down