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

Merge branch 'skin-components-bind-outwards-health' into remove-hud-overlay-unused-pieces

This commit is contained in:
Dean Herbert 2021-05-07 17:51:41 +09:00
commit a7acecc52b
13 changed files with 88 additions and 109 deletions

View File

@ -4,6 +4,7 @@
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics.Containers;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
@ -22,22 +23,30 @@ namespace osu.Game.Tests.Visual.Gameplay
[SetUpSteps] [SetUpSteps]
public void SetUpSteps() public void SetUpSteps()
{
}
private void create(HealthProcessor healthProcessor)
{ {
AddStep("create layer", () => AddStep("create layer", () =>
{ {
Child = layer = new FailingLayer(); Child = new HealthProcessorContainer(healthProcessor)
layer.BindHealthProcessor(new DrainingHealthProcessor(1)); {
Child = layer = new FailingLayer()
};
layer.ShowHealth.BindTo(showHealth); layer.ShowHealth.BindTo(showHealth);
}); });
AddStep("show health", () => showHealth.Value = true); AddStep("show health", () => showHealth.Value = true);
AddStep("enable layer", () => config.SetValue(OsuSetting.FadePlayfieldWhenHealthLow, true)); AddStep("enable layer", () => config.SetValue(OsuSetting.FadePlayfieldWhenHealthLow, true));
AddUntilStep("layer is visible", () => layer.IsPresent);
} }
[Test] [Test]
public void TestLayerFading() public void TestLayerFading()
{ {
create(new DrainingHealthProcessor(0));
AddSliderStep("current health", 0.0, 1.0, 1.0, val => AddSliderStep("current health", 0.0, 1.0, 1.0, val =>
{ {
if (layer != null) if (layer != null)
@ -53,6 +62,8 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test] [Test]
public void TestLayerDisabledViaConfig() public void TestLayerDisabledViaConfig()
{ {
create(new DrainingHealthProcessor(0));
AddUntilStep("layer is visible", () => layer.IsPresent);
AddStep("disable layer", () => config.SetValue(OsuSetting.FadePlayfieldWhenHealthLow, false)); AddStep("disable layer", () => config.SetValue(OsuSetting.FadePlayfieldWhenHealthLow, false));
AddStep("set health to 0.10", () => layer.Current.Value = 0.1); AddStep("set health to 0.10", () => layer.Current.Value = 0.1);
AddUntilStep("layer is not visible", () => !layer.IsPresent); AddUntilStep("layer is not visible", () => !layer.IsPresent);
@ -61,7 +72,8 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test] [Test]
public void TestLayerVisibilityWithAccumulatingProcessor() public void TestLayerVisibilityWithAccumulatingProcessor()
{ {
AddStep("bind accumulating processor", () => layer.BindHealthProcessor(new AccumulatingHealthProcessor(1))); create(new AccumulatingHealthProcessor(1));
AddUntilStep("layer is not visible", () => !layer.IsPresent);
AddStep("set health to 0.10", () => layer.Current.Value = 0.1); AddStep("set health to 0.10", () => layer.Current.Value = 0.1);
AddUntilStep("layer is not visible", () => !layer.IsPresent); AddUntilStep("layer is not visible", () => !layer.IsPresent);
} }
@ -69,7 +81,7 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test] [Test]
public void TestLayerVisibilityWithDrainingProcessor() public void TestLayerVisibilityWithDrainingProcessor()
{ {
AddStep("bind accumulating processor", () => layer.BindHealthProcessor(new DrainingHealthProcessor(1))); create(new DrainingHealthProcessor(0));
AddStep("set health to 0.10", () => layer.Current.Value = 0.1); AddStep("set health to 0.10", () => layer.Current.Value = 0.1);
AddWaitStep("wait for potential fade", 10); AddWaitStep("wait for potential fade", 10);
AddAssert("layer is still visible", () => layer.IsPresent); AddAssert("layer is still visible", () => layer.IsPresent);
@ -78,6 +90,8 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test] [Test]
public void TestLayerVisibilityWithDifferentOptions() public void TestLayerVisibilityWithDifferentOptions()
{ {
create(new DrainingHealthProcessor(0));
AddStep("set health to 0.10", () => layer.Current.Value = 0.1); AddStep("set health to 0.10", () => layer.Current.Value = 0.1);
AddStep("don't show health", () => showHealth.Value = false); AddStep("don't show health", () => showHealth.Value = false);
@ -96,5 +110,16 @@ namespace osu.Game.Tests.Visual.Gameplay
AddStep("enable FadePlayfieldWhenHealthLow", () => config.SetValue(OsuSetting.FadePlayfieldWhenHealthLow, true)); AddStep("enable FadePlayfieldWhenHealthLow", () => config.SetValue(OsuSetting.FadePlayfieldWhenHealthLow, true));
AddUntilStep("layer fade is visible", () => layer.IsPresent); AddUntilStep("layer fade is visible", () => layer.IsPresent);
} }
private class HealthProcessorContainer : Container
{
[Cached(typeof(HealthProcessor))]
private readonly HealthProcessor healthProcessor;
public HealthProcessorContainer(HealthProcessor healthProcessor)
{
this.healthProcessor = healthProcessor;
}
}
} }
} }

View File

@ -23,6 +23,9 @@ namespace osu.Game.Tests.Visual.Gameplay
[Cached] [Cached]
private ScoreProcessor scoreProcessor = new ScoreProcessor(); private ScoreProcessor scoreProcessor = new ScoreProcessor();
[Cached(typeof(HealthProcessor))]
private HealthProcessor healthProcessor = new DrainingHealthProcessor(0);
// best way to check without exposing. // best way to check without exposing.
private Drawable hideTarget => hudOverlay.KeyCounter; private Drawable hideTarget => hudOverlay.KeyCounter;
private FillFlowContainer<KeyCounter> keyCounterFlow => hudOverlay.KeyCounter.ChildrenOfType<FillFlowContainer<KeyCounter>>().First(); private FillFlowContainer<KeyCounter> keyCounterFlow => hudOverlay.KeyCounter.ChildrenOfType<FillFlowContainer<KeyCounter>>().First();
@ -143,7 +146,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{ {
AddStep("create overlay", () => AddStep("create overlay", () =>
{ {
hudOverlay = new HUDOverlay(scoreProcessor, null, null, Array.Empty<Mod>()); hudOverlay = new HUDOverlay(scoreProcessor, null, Array.Empty<Mod>());
// Add any key just to display the key counter visually. // Add any key just to display the key counter visually.
hudOverlay.KeyCounter.Add(new KeyCounterKeyboard(Key.Space)); hudOverlay.KeyCounter.Add(new KeyCounterKeyboard(Key.Space));

View File

@ -21,6 +21,9 @@ namespace osu.Game.Tests.Visual.Gameplay
[Cached] [Cached]
private readonly ScoreProcessor scoreProcessor = new ScoreProcessor(); private readonly ScoreProcessor scoreProcessor = new ScoreProcessor();
[Cached(typeof(HealthProcessor))]
private HealthProcessor healthProcessor = new DrainingHealthProcessor(0);
[SetUpSteps] [SetUpSteps]
public void SetUpSteps() public void SetUpSteps()
{ {
@ -34,7 +37,7 @@ namespace osu.Game.Tests.Visual.Gameplay
var drawableRuleset = ruleset.CreateDrawableRulesetWith(beatmap); var drawableRuleset = ruleset.CreateDrawableRulesetWith(beatmap);
var hudOverlay = new HUDOverlay(scoreProcessor, null, drawableRuleset, Array.Empty<Mod>()) var hudOverlay = new HUDOverlay(scoreProcessor, drawableRuleset, Array.Empty<Mod>())
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,

View File

@ -27,6 +27,9 @@ namespace osu.Game.Tests.Visual.Gameplay
[Cached] [Cached]
private ScoreProcessor scoreProcessor = new ScoreProcessor(); private ScoreProcessor scoreProcessor = new ScoreProcessor();
[Cached(typeof(HealthProcessor))]
private HealthProcessor healthProcessor = new DrainingHealthProcessor(0);
private IEnumerable<HUDOverlay> hudOverlays => CreatedDrawables.OfType<HUDOverlay>(); private IEnumerable<HUDOverlay> hudOverlays => CreatedDrawables.OfType<HUDOverlay>();
// best way to check without exposing. // best way to check without exposing.
@ -76,7 +79,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{ {
SetContents(() => SetContents(() =>
{ {
hudOverlay = new HUDOverlay(scoreProcessor, null, null, Array.Empty<Mod>()); hudOverlay = new HUDOverlay(scoreProcessor, null, Array.Empty<Mod>());
// Add any key just to display the key counter visually. // Add any key just to display the key counter visually.
hudOverlay.KeyCounter.Add(new KeyCounterKeyboard(Key.Space)); hudOverlay.KeyCounter.Add(new KeyCounterKeyboard(Key.Space));

View File

@ -4,11 +4,14 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Play.HUD;
namespace osu.Game.Tests.Visual.Gameplay namespace osu.Game.Tests.Visual.Gameplay
@ -19,6 +22,9 @@ namespace osu.Game.Tests.Visual.Gameplay
protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset();
[Cached(typeof(HealthProcessor))]
private HealthProcessor healthProcessor = new DrainingHealthProcessor(0);
[SetUpSteps] [SetUpSteps]
public void SetUpSteps() public void SetUpSteps()
{ {
@ -28,8 +34,7 @@ namespace osu.Game.Tests.Visual.Gameplay
}); });
AddStep(@"Reset all", delegate AddStep(@"Reset all", delegate
{ {
foreach (var s in healthDisplays) healthProcessor.Health.Value = 1;
s.Current.Value = 1;
}); });
} }
@ -38,23 +43,18 @@ namespace osu.Game.Tests.Visual.Gameplay
{ {
AddRepeatStep(@"decrease hp", delegate AddRepeatStep(@"decrease hp", delegate
{ {
foreach (var healthDisplay in healthDisplays) healthProcessor.Health.Value = 0.08f;
healthDisplay.Current.Value -= 0.08f;
}, 10); }, 10);
AddRepeatStep(@"increase hp without flash", delegate AddRepeatStep(@"increase hp without flash", delegate
{ {
foreach (var healthDisplay in healthDisplays) healthProcessor.Health.Value = 0.1f;
healthDisplay.Current.Value += 0.1f;
}, 3); }, 3);
AddRepeatStep(@"increase hp with flash", delegate AddRepeatStep(@"increase hp with flash", delegate
{ {
foreach (var healthDisplay in healthDisplays) healthProcessor.Health.Value = 0.1f;
{ healthProcessor.ApplyResult(new JudgementResult(new HitCircle(), new OsuJudgement()));
healthDisplay.Current.Value += 0.1f;
healthDisplay.Flash(new JudgementResult(null, new OsuJudgement()));
}
}, 3); }, 3);
} }
} }

View File

@ -107,7 +107,7 @@ namespace osu.Game.Screens.Play.HUD
GlowColour = colours.BlueDarker; GlowColour = colours.BlueDarker;
} }
public override void Flash(JudgementResult result) => Scheduler.AddOnce(flash); protected override void Flash(JudgementResult result) => Scheduler.AddOnce(flash);
private void flash() private void flash()
{ {

View File

@ -39,7 +39,6 @@ namespace osu.Game.Screens.Play.HUD
private readonly Container boxes; private readonly Container boxes;
private Bindable<bool> fadePlayfieldWhenHealthLow; private Bindable<bool> fadePlayfieldWhenHealthLow;
private HealthProcessor healthProcessor;
public FailingLayer() public FailingLayer()
{ {
@ -88,18 +87,10 @@ namespace osu.Game.Screens.Play.HUD
updateState(); updateState();
} }
public override void BindHealthProcessor(HealthProcessor processor)
{
base.BindHealthProcessor(processor);
healthProcessor = processor;
updateState();
}
private void updateState() private void updateState()
{ {
// Don't display ever if the ruleset is not using a draining health display. // Don't display ever if the ruleset is not using a draining health display.
var showLayer = healthProcessor is DrainingHealthProcessor && fadePlayfieldWhenHealthLow.Value && ShowHealth.Value; var showLayer = HealthProcessor is DrainingHealthProcessor && fadePlayfieldWhenHealthLow.Value && ShowHealth.Value;
this.FadeTo(showLayer ? 1 : 0, fade_time, Easing.OutQuint); this.FadeTo(showLayer ? 1 : 0, fade_time, Easing.OutQuint);
} }

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// 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 osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
@ -11,26 +12,42 @@ namespace osu.Game.Screens.Play.HUD
{ {
/// <summary> /// <summary>
/// A container for components displaying the current player health. /// A container for components displaying the current player health.
/// Gets bound automatically to the <see cref="HealthProcessor"/> when inserted to <see cref="DrawableRuleset.Overlays"/> hierarchy. /// Gets bound automatically to the <see cref="Rulesets.Scoring.HealthProcessor"/> when inserted to <see cref="DrawableRuleset.Overlays"/> hierarchy.
/// </summary> /// </summary>
public abstract class HealthDisplay : Container, IHealthDisplay public abstract class HealthDisplay : Container
{ {
[Resolved]
protected HealthProcessor HealthProcessor { get; private set; }
public Bindable<double> Current { get; } = new BindableDouble(1) public Bindable<double> Current { get; } = new BindableDouble(1)
{ {
MinValue = 0, MinValue = 0,
MaxValue = 1 MaxValue = 1
}; };
public virtual void Flash(JudgementResult result) protected virtual void Flash(JudgementResult result)
{ {
} }
/// <summary> [BackgroundDependencyLoader]
/// Bind the tracked fields of <see cref="HealthProcessor"/> to this health display. private void load()
/// </summary>
public virtual void BindHealthProcessor(HealthProcessor processor)
{ {
Current.BindTo(processor.Health); Current.BindTo(HealthProcessor.Health);
HealthProcessor.NewJudgement += onNewJudgement;
}
private void onNewJudgement(JudgementResult judgement)
{
if (judgement.IsHit && judgement.Type != HitResult.IgnoreHit) Flash(judgement);
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (HealthProcessor != null)
HealthProcessor.NewJudgement -= onNewJudgement;
} }
} }
} }

View File

@ -1,26 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Judgements;
namespace osu.Game.Screens.Play.HUD
{
/// <summary>
/// An interface providing a set of methods to update a health display.
/// </summary>
public interface IHealthDisplay : IDrawable
{
/// <summary>
/// The current health to be displayed.
/// </summary>
Bindable<double> Current { get; }
/// <summary>
/// Flash the display for a specified result type.
/// </summary>
/// <param name="result">The result type.</param>
void Flash(JudgementResult result);
}
}

View File

@ -3,13 +3,12 @@
using System; using System;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning; using osu.Game.Skinning;
namespace osu.Game.Screens.Play.HUD namespace osu.Game.Screens.Play.HUD
{ {
public class SkinnableHealthDisplay : SkinnableDrawable, IHealthDisplay public class SkinnableHealthDisplay : SkinnableDrawable
{ {
public Bindable<double> Current { get; } = new BindableDouble(1) public Bindable<double> Current { get; } = new BindableDouble(1)
{ {
@ -17,8 +16,6 @@ namespace osu.Game.Screens.Play.HUD
MaxValue = 1 MaxValue = 1
}; };
public void Flash(JudgementResult result) => skinnedCounter?.Flash(result);
private HealthProcessor processor; private HealthProcessor processor;
public void BindHealthProcessor(HealthProcessor processor) public void BindHealthProcessor(HealthProcessor processor)
@ -36,15 +33,5 @@ namespace osu.Game.Screens.Play.HUD
{ {
CentreComponent = false; CentreComponent = false;
} }
private IHealthDisplay skinnedCounter;
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
{
base.SkinChanged(skin, allowFallback);
skinnedCounter = Drawable as IHealthDisplay;
skinnedCounter?.Current.BindTo(Current);
}
} }
} }

View File

@ -47,7 +47,6 @@ namespace osu.Game.Screens.Play
public Bindable<bool> ShowHealthbar = new Bindable<bool>(true); public Bindable<bool> ShowHealthbar = new Bindable<bool>(true);
private readonly ScoreProcessor scoreProcessor; private readonly ScoreProcessor scoreProcessor;
private readonly HealthProcessor healthProcessor;
private readonly DrawableRuleset drawableRuleset; private readonly DrawableRuleset drawableRuleset;
private readonly IReadOnlyList<Mod> mods; private readonly IReadOnlyList<Mod> mods;
@ -75,10 +74,9 @@ namespace osu.Game.Screens.Play
private IEnumerable<Drawable> hideTargets => new Drawable[] { visibilityContainer, KeyCounter, topRightElements }; private IEnumerable<Drawable> hideTargets => new Drawable[] { visibilityContainer, KeyCounter, topRightElements };
public HUDOverlay(ScoreProcessor scoreProcessor, HealthProcessor healthProcessor, DrawableRuleset drawableRuleset, IReadOnlyList<Mod> mods) public HUDOverlay(ScoreProcessor scoreProcessor, DrawableRuleset drawableRuleset, IReadOnlyList<Mod> mods)
{ {
this.scoreProcessor = scoreProcessor; this.scoreProcessor = scoreProcessor;
this.healthProcessor = healthProcessor;
this.drawableRuleset = drawableRuleset; this.drawableRuleset = drawableRuleset;
this.mods = mods; this.mods = mods;
@ -161,9 +159,6 @@ namespace osu.Game.Screens.Play
if (scoreProcessor != null) if (scoreProcessor != null)
BindScoreProcessor(scoreProcessor); BindScoreProcessor(scoreProcessor);
if (healthProcessor != null)
BindHealthProcessor(healthProcessor);
if (drawableRuleset != null) if (drawableRuleset != null)
{ {
BindDrawableRuleset(drawableRuleset); BindDrawableRuleset(drawableRuleset);
@ -321,21 +316,6 @@ namespace osu.Game.Screens.Play
protected virtual void BindScoreProcessor(ScoreProcessor processor) protected virtual void BindScoreProcessor(ScoreProcessor processor)
{ {
AccuracyCounter?.Current.BindTo(processor.Accuracy); AccuracyCounter?.Current.BindTo(processor.Accuracy);
if (HealthDisplay is IHealthDisplay shd)
{
processor.NewJudgement += judgement =>
{
if (judgement.IsHit && judgement.Type != HitResult.IgnoreHit)
shd.Flash(judgement);
};
}
}
protected virtual void BindHealthProcessor(HealthProcessor processor)
{
HealthDisplay?.BindHealthProcessor(processor);
FailingLayer?.BindHealthProcessor(processor);
} }
public bool OnPressed(GlobalAction action) public bool OnPressed(GlobalAction action)

View File

@ -208,6 +208,8 @@ namespace osu.Game.Screens.Play
HealthProcessor = ruleset.CreateHealthProcessor(playableBeatmap.HitObjects[0].StartTime); HealthProcessor = ruleset.CreateHealthProcessor(playableBeatmap.HitObjects[0].StartTime);
HealthProcessor.ApplyBeatmap(playableBeatmap); HealthProcessor.ApplyBeatmap(playableBeatmap);
dependencies.CacheAs(HealthProcessor);
if (!ScoreProcessor.Mode.Disabled) if (!ScoreProcessor.Mode.Disabled)
config.BindWith(OsuSetting.ScoreDisplayMode, ScoreProcessor.Mode); config.BindWith(OsuSetting.ScoreDisplayMode, ScoreProcessor.Mode);
@ -343,7 +345,7 @@ namespace osu.Game.Screens.Play
// display the cursor above some HUD elements. // display the cursor above some HUD elements.
DrawableRuleset.Cursor?.CreateProxy() ?? new Container(), DrawableRuleset.Cursor?.CreateProxy() ?? new Container(),
DrawableRuleset.ResumeOverlay?.CreateProxy() ?? new Container(), DrawableRuleset.ResumeOverlay?.CreateProxy() ?? new Container(),
HUDOverlay = new HUDOverlay(ScoreProcessor, HealthProcessor, DrawableRuleset, Mods.Value) HUDOverlay = new HUDOverlay(ScoreProcessor, DrawableRuleset, Mods.Value)
{ {
HoldToQuit = HoldToQuit =
{ {

View File

@ -16,7 +16,7 @@ using osuTK.Graphics;
namespace osu.Game.Skinning namespace osu.Game.Skinning
{ {
public class LegacyHealthDisplay : CompositeDrawable, IHealthDisplay, ISkinnableComponent public class LegacyHealthDisplay : HealthDisplay
{ {
private const double epic_cutoff = 0.5; private const double epic_cutoff = 0.5;
@ -28,12 +28,6 @@ namespace osu.Game.Skinning
private bool isNewStyle; private bool isNewStyle;
public Bindable<double> Current { get; } = new BindableDouble(1)
{
MinValue = 0,
MaxValue = 1
};
public LegacyHealthDisplay(Skin skin) public LegacyHealthDisplay(Skin skin)
{ {
this.skin = skin; this.skin = skin;
@ -83,7 +77,7 @@ namespace osu.Game.Skinning
marker.Position = fill.Position + new Vector2(fill.DrawWidth, isNewStyle ? fill.DrawHeight / 2 : 0); marker.Position = fill.Position + new Vector2(fill.DrawWidth, isNewStyle ? fill.DrawHeight / 2 : 0);
} }
public void Flash(JudgementResult result) => marker.Flash(result); protected override void Flash(JudgementResult result) => marker.Flash(result);
private static Texture getTexture(Skin skin, string name) => skin.GetTexture($"scorebar-{name}"); private static Texture getTexture(Skin skin, string name) => skin.GetTexture($"scorebar-{name}");
@ -254,7 +248,7 @@ namespace osu.Game.Skinning
Main.ScaleTo(1.4f).Then().ScaleTo(1, 200, Easing.Out); Main.ScaleTo(1.4f).Then().ScaleTo(1, 200, Easing.Out);
} }
public class LegacyHealthPiece : CompositeDrawable, IHealthDisplay public class LegacyHealthPiece : CompositeDrawable
{ {
public Bindable<double> Current { get; } = new Bindable<double>(); public Bindable<double> Current { get; } = new Bindable<double>();