From 6e2ed0c4f3f4389e32e84d548262b5037daa015e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 29 Apr 2020 20:28:46 +0200 Subject: [PATCH] Refactor mascot to only contain state transitions --- .../Skinning/TestSceneDrawableTaikoMascot.cs | 47 ++++---- .../UI/DrawableTaikoMascot.cs | 109 +++++++----------- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 8 +- 3 files changed, 67 insertions(+), 97 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs index 2966c90b5e..f37c723a36 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneDrawableTaikoMascot.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; @@ -41,26 +42,25 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning [Test] public void TestStateTextures() { - AddStep("Set beatmap", () => setBeatmap()); + AddStep("set beatmap", () => setBeatmap()); - AddStep("Create mascot (idle)", () => + AddStep("create mascot", () => { SetContents(() => new TestDrawableTaikoMascot()); }); - AddStep("Clear state", () => setState(TaikoMascotAnimationState.Clear)); - - AddStep("Kiai state", () => setState(TaikoMascotAnimationState.Kiai)); - - AddStep("Fail state", () => setState(TaikoMascotAnimationState.Fail)); + AddStep("clear state", () => setState(TaikoMascotAnimationState.Clear)); + AddStep("kiai state", () => setState(TaikoMascotAnimationState.Kiai)); + AddStep("fail state", () => setState(TaikoMascotAnimationState.Fail)); + AddStep("idle state", () => setState(TaikoMascotAnimationState.Idle)); } [Test] public void TestPlayfield() { - AddStep("Set beatmap", () => setBeatmap()); + AddStep("set beatmap", () => setBeatmap()); - AddStep("Create ruleset", () => + AddStep("create drawable ruleset", () => { SetContents(() => { @@ -69,21 +69,21 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning }); }); - AddStep("Create hit (great)", () => addJudgement(HitResult.Miss)); - AddUntilStep("Wait for idle state", () => checkForState(TaikoMascotAnimationState.Fail)); + AddStep("new judgement (miss)", () => addJudgement(HitResult.Miss)); + AddUntilStep("wait for fail state", () => assertState(TaikoMascotAnimationState.Fail)); - AddStep("Create hit (great)", () => addJudgement(HitResult.Great)); - AddUntilStep("Wait for idle state", () => checkForState(TaikoMascotAnimationState.Idle)); + AddStep("new judgement (great)", () => addJudgement(HitResult.Great)); + AddUntilStep("wait for idle state", () => assertState(TaikoMascotAnimationState.Idle)); } [Test] public void TestKiai() { - AddStep("Set beatmap", () => setBeatmap(true)); + AddStep("set beatmap", () => setBeatmap(true)); - AddUntilStep("Wait for beatmap to be loaded", () => Beatmap.Value.Track.IsLoaded); + AddUntilStep("wait for beatmap to be loaded", () => Beatmap.Value.Track.IsLoaded); - AddStep("Create kiai ruleset", () => + AddStep("create drawable ruleset", () => { Beatmap.Value.Track.Start(); @@ -94,10 +94,10 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning }); }); - AddUntilStep("Wait for idle state", () => checkForState(TaikoMascotAnimationState.Fail)); + AddUntilStep("wait for fail state", () => assertState(TaikoMascotAnimationState.Fail)); - AddStep("Create hit (great)", () => addJudgement(HitResult.Great)); - AddUntilStep("Wait for kiai state", () => checkForState(TaikoMascotAnimationState.Kiai)); + AddStep("new judgement (great)", () => addJudgement(HitResult.Great)); + AddUntilStep("wait for kiai state", () => assertState(TaikoMascotAnimationState.Kiai)); } private void setBeatmap(bool kiai = false) @@ -129,7 +129,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning private void setState(TaikoMascotAnimationState state) { foreach (var mascot in mascots) - mascot?.ShowState(state); + mascot.State.Value = state; } private void addJudgement(HitResult result) @@ -143,7 +143,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning } } - private bool checkForState(TaikoMascotAnimationState state) => mascots.All(d => d.State == state); + private bool assertState(TaikoMascotAnimationState state) => mascots.All(d => d.State.Value == state); private class TestDrawableTaikoMascot : DrawableTaikoMascot { @@ -152,10 +152,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning { } - protected override TaikoMascotAnimationState GetFinalAnimationState(EffectControlPoint effectPoint, TaikoMascotAnimationState playfieldState) - { - return State; - } + public new Bindable State => base.State; } } } diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs index 7c4dfe2da7..be744de5f4 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoMascot.cs @@ -1,29 +1,36 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; +using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Audio.Track; +using osu.Framework.Bindables; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics.Containers; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Taiko.UI { public class DrawableTaikoMascot : BeatSyncedContainer { - private TaikoMascotTextureAnimation idleDrawable, clearDrawable, kiaiDrawable, failDrawable; - private EffectControlPoint lastEffectControlPoint; - private TaikoMascotAnimationState playfieldState; + protected Bindable State { get; } - public TaikoMascotAnimationState State { get; private set; } + private readonly Dictionary animations; + private Drawable currentAnimation; + + private bool lastHitMissed; + private bool kiaiMode; public DrawableTaikoMascot(TaikoMascotAnimationState startingState = TaikoMascotAnimationState.Idle) { RelativeSizeAxes = Axes.Both; - State = startingState; + State = new Bindable(startingState); + animations = new Dictionary(); } [BackgroundDependencyLoader] @@ -31,81 +38,53 @@ namespace osu.Game.Rulesets.Taiko.UI { InternalChildren = new[] { - idleDrawable = new TaikoMascotTextureAnimation(TaikoMascotAnimationState.Idle), - clearDrawable = new TaikoMascotTextureAnimation(TaikoMascotAnimationState.Clear), - kiaiDrawable = new TaikoMascotTextureAnimation(TaikoMascotAnimationState.Kiai), - failDrawable = new TaikoMascotTextureAnimation(TaikoMascotAnimationState.Fail), + animations[TaikoMascotAnimationState.Idle] = new TaikoMascotTextureAnimation(TaikoMascotAnimationState.Idle), + animations[TaikoMascotAnimationState.Clear] = new TaikoMascotTextureAnimation(TaikoMascotAnimationState.Clear), + animations[TaikoMascotAnimationState.Kiai] = new TaikoMascotTextureAnimation(TaikoMascotAnimationState.Kiai), + animations[TaikoMascotAnimationState.Fail] = new TaikoMascotTextureAnimation(TaikoMascotAnimationState.Fail), }; - ShowState(State); + updateState(); } - public void ShowState(TaikoMascotAnimationState state) + protected override void LoadComplete() { - foreach (var child in InternalChildren) - child.Hide(); + base.LoadComplete(); - State = state; - - var drawable = getStateDrawable(State); - drawable.Show(); + animations.Values.ForEach(animation => animation.Hide()); + State.BindValueChanged(mascotStateChanged, true); } - /// - /// Sets the playfield state used for determining the final state. - /// - /// - /// If you're looking to change the state manually, please look at . - /// - public void SetPlayfieldState(TaikoMascotAnimationState state) + public void OnNewResult(JudgementResult result) { - playfieldState = state; - - if (lastEffectControlPoint != null) - ShowState(GetFinalAnimationState(lastEffectControlPoint, playfieldState)); - } - - private TaikoMascotTextureAnimation getStateDrawable(TaikoMascotAnimationState state) - { - switch (state) - { - case TaikoMascotAnimationState.Idle: - return idleDrawable; - - case TaikoMascotAnimationState.Clear: - return clearDrawable; - - case TaikoMascotAnimationState.Kiai: - return kiaiDrawable; - - case TaikoMascotAnimationState.Fail: - return failDrawable; - - default: - throw new ArgumentOutOfRangeException(nameof(state), $"There's no animation available for state {state}"); - } - } - - protected virtual TaikoMascotAnimationState GetFinalAnimationState(EffectControlPoint effectPoint, TaikoMascotAnimationState playfieldState) - { - if (playfieldState == TaikoMascotAnimationState.Fail) - return playfieldState; - - return effectPoint.KiaiMode ? TaikoMascotAnimationState.Kiai : TaikoMascotAnimationState.Idle; + lastHitMissed = result.Type == HitResult.Miss && result.Judgement.AffectsCombo; + updateState(); } protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) { - base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); + kiaiMode = effectPoint.KiaiMode; + updateState(); + } - var state = GetFinalAnimationState(lastEffectControlPoint = effectPoint, playfieldState); - ShowState(state); + private void updateState() + { + State.Value = getNextState(); + } - if (state == TaikoMascotAnimationState.Clear) - return; + private TaikoMascotAnimationState getNextState() + { + if (lastHitMissed) + return TaikoMascotAnimationState.Fail; - var drawable = getStateDrawable(state); - drawable.Move(); + return kiaiMode ? TaikoMascotAnimationState.Kiai : TaikoMascotAnimationState.Idle; + } + + private void mascotStateChanged(ValueChangedEvent state) + { + currentAnimation?.Hide(); + currentAnimation = animations[state.NewValue]; + currentAnimation.Show(); } } } diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index c6e867a5d9..084a11d523 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -15,7 +15,6 @@ using osu.Game.Rulesets.Taiko.Objects.Drawables; using osu.Game.Rulesets.Taiko.Judgements; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Skinning; -using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Taiko.UI { @@ -216,12 +215,7 @@ namespace osu.Game.Rulesets.Taiko.UI if (mascotDrawable.Drawable is DrawableTaikoMascot mascot) { - var miss = result.Type == HitResult.Miss; - - if (miss && judgedObject.HitObject is StrongHitObject) - miss = result.Judgement.AffectsCombo; - - mascot.SetPlayfieldState(miss ? TaikoMascotAnimationState.Fail : TaikoMascotAnimationState.Idle); + mascot.OnNewResult(result); } }