From 56721a6fa9aec7d9ae55091931e474186862d6b7 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 8 Dec 2020 12:48:13 +0900 Subject: [PATCH 01/37] Compute object position in stack via a pure function --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index a806e623af..76c5afeda7 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -447,8 +447,10 @@ namespace osu.Game.Rulesets.Catch.UI if (caughtObject == null) return; + var positionInStack = computePositionInStack(new Vector2(source.X - X, 0), caughtObject.DisplayRadius); + caughtObject.RelativePositionAxes = Axes.None; - caughtObject.X = source.X - X; + caughtObject.Position = positionInStack; caughtObject.IsOnPlate = true; caughtObject.Anchor = Anchor.TopCentre; @@ -457,8 +459,6 @@ namespace osu.Game.Rulesets.Catch.UI caughtObject.LifetimeStart = source.StartTime; caughtObject.LifetimeEnd = double.MaxValue; - adjustPositionInStack(caughtObject); - caughtFruitContainer.Add(caughtObject); addLighting(caughtObject); @@ -467,22 +467,22 @@ namespace osu.Game.Rulesets.Catch.UI removeFromPlate(caughtObject, DroppedObjectAnimation.Explode); } - private void adjustPositionInStack(DrawablePalpableCatchHitObject caughtObject) + private Vector2 computePositionInStack(Vector2 position, float displayRadius) { const float radius_div_2 = CatchHitObject.OBJECT_RADIUS / 2; const float allowance = 10; - float caughtObjectRadius = caughtObject.DisplayRadius; - - while (caughtFruitContainer.Any(f => Vector2Extensions.Distance(f.Position, caughtObject.Position) < (caughtObjectRadius + radius_div_2) / (allowance / 2))) + while (caughtFruitContainer.Any(f => Vector2Extensions.Distance(f.Position, position) < (displayRadius + radius_div_2) / (allowance / 2))) { - float diff = (caughtObjectRadius + radius_div_2) / allowance; + float diff = (displayRadius + radius_div_2) / allowance; - caughtObject.X += (RNG.NextSingle() - 0.5f) * diff * 2; - caughtObject.Y -= RNG.NextSingle() * diff; + position.X += (RNG.NextSingle() - 0.5f) * diff * 2; + position.Y -= RNG.NextSingle() * diff; } - caughtObject.X = Math.Clamp(caughtObject.X, -CatcherArea.CATCHER_SIZE / 2, CatcherArea.CATCHER_SIZE / 2); + position.X = Math.Clamp(position.X, -CatcherArea.CATCHER_SIZE / 2, CatcherArea.CATCHER_SIZE / 2); + + return position; } private void addLighting(DrawablePalpableCatchHitObject caughtObject) From 004c705aa9da1024078af02f8ef6c87c8468d804 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 8 Dec 2020 20:05:18 +0900 Subject: [PATCH 02/37] Remove ScaleContainer and flatten the Drawable tree of catch DHO --- .../TestSceneFruitRandomness.cs | 47 +++++-------------- .../Objects/Drawables/DrawableBanana.cs | 14 +++--- .../Objects/Drawables/DrawableDroplet.cs | 6 +-- .../Objects/Drawables/DrawableFruit.cs | 6 +-- .../DrawablePalpableCatchHitObject.cs | 14 +----- 5 files changed, 28 insertions(+), 59 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneFruitRandomness.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneFruitRandomness.cs index 2ffebb7de1..e1d817b314 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneFruitRandomness.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneFruitRandomness.cs @@ -11,13 +11,13 @@ namespace osu.Game.Rulesets.Catch.Tests { public class TestSceneFruitRandomness : OsuTestScene { - private readonly TestDrawableFruit drawableFruit; - private readonly TestDrawableBanana drawableBanana; + private readonly DrawableFruit drawableFruit; + private readonly DrawableBanana drawableBanana; public TestSceneFruitRandomness() { - drawableFruit = new TestDrawableFruit(new Fruit()); - drawableBanana = new TestDrawableBanana(new Banana()); + drawableFruit = new DrawableFruit(new Fruit()); + drawableBanana = new DrawableBanana(new Banana()); Add(new TestDrawableCatchHitObjectSpecimen(drawableFruit) { X = -200 }); Add(new TestDrawableCatchHitObjectSpecimen(drawableBanana)); @@ -44,9 +44,9 @@ namespace osu.Game.Rulesets.Catch.Tests { drawableFruit.HitObject.StartTime = drawableBanana.HitObject.StartTime = initial_start_time; - fruitRotation = drawableFruit.InnerRotation; - bananaRotation = drawableBanana.InnerRotation; - bananaScale = drawableBanana.InnerScale; + fruitRotation = drawableFruit.Rotation; + bananaRotation = drawableBanana.Rotation; + bananaScale = drawableBanana.Scale.X; bananaColour = drawableBanana.AccentColour.Value; }); @@ -55,9 +55,9 @@ namespace osu.Game.Rulesets.Catch.Tests drawableFruit.HitObject.StartTime = drawableBanana.HitObject.StartTime = another_start_time; }); - AddAssert("fruit rotation is changed", () => drawableFruit.InnerRotation != fruitRotation); - AddAssert("banana rotation is changed", () => drawableBanana.InnerRotation != bananaRotation); - AddAssert("banana scale is changed", () => drawableBanana.InnerScale != bananaScale); + AddAssert("fruit rotation is changed", () => drawableFruit.Rotation != fruitRotation); + AddAssert("banana rotation is changed", () => drawableBanana.Rotation != bananaRotation); + AddAssert("banana scale is changed", () => drawableBanana.Scale.X != bananaScale); AddAssert("banana colour is changed", () => drawableBanana.AccentColour.Value != bananaColour); AddStep("reset start time", () => @@ -66,31 +66,10 @@ namespace osu.Game.Rulesets.Catch.Tests }); AddAssert("rotation and scale restored", () => - drawableFruit.InnerRotation == fruitRotation && - drawableBanana.InnerRotation == bananaRotation && - drawableBanana.InnerScale == bananaScale && + drawableFruit.Rotation == fruitRotation && + drawableBanana.Rotation == bananaRotation && + drawableBanana.Scale.X == bananaScale && drawableBanana.AccentColour.Value == bananaColour); } - - private class TestDrawableFruit : DrawableFruit - { - public float InnerRotation => ScaleContainer.Rotation; - - public TestDrawableFruit(Fruit h) - : base(h) - { - } - } - - private class TestDrawableBanana : DrawableBanana - { - public float InnerRotation => ScaleContainer.Rotation; - public float InnerScale => ScaleContainer.Scale.X; - - public TestDrawableBanana(Banana h) - : base(h) - { - } - } } } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs index 2a543a0e04..4bc5da2396 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs @@ -24,9 +24,9 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables [BackgroundDependencyLoader] private void load() { - ScaleContainer.Child = new SkinnableDrawable( + AddInternal(new SkinnableDrawable( new CatchSkinComponent(CatchSkinComponents.Banana), - _ => new BananaPiece()); + _ => new BananaPiece())); } protected override void LoadComplete() @@ -44,12 +44,12 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables const float end_scale = 0.6f; const float random_scale_range = 1.6f; - ScaleContainer.ScaleTo(HitObject.Scale * (end_scale + random_scale_range * RandomSingle(3))) - .Then().ScaleTo(HitObject.Scale * end_scale, HitObject.TimePreempt); + this.ScaleTo(HitObject.Scale * (end_scale + random_scale_range * RandomSingle(3))) + .Then().ScaleTo(HitObject.Scale * end_scale, HitObject.TimePreempt); - ScaleContainer.RotateTo(getRandomAngle(1)) - .Then() - .RotateTo(getRandomAngle(2), HitObject.TimePreempt); + this.RotateTo(getRandomAngle(1)) + .Then() + .RotateTo(getRandomAngle(2), HitObject.TimePreempt); float getRandomAngle(int series) => 180 * (RandomSingle(series) * 2 - 1); } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs index 81c8de2e59..d96b1c8902 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs @@ -26,9 +26,9 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables [BackgroundDependencyLoader] private void load() { - ScaleContainer.Child = new SkinnableDrawable( + AddInternal(new SkinnableDrawable( new CatchSkinComponent(CatchSkinComponents.Droplet), - _ => new DropletPiece()); + _ => new DropletPiece())); } protected override void UpdateInitialTransforms() @@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables float startRotation = RandomSingle(1) * 20; double duration = HitObject.TimePreempt + 2000; - ScaleContainer.RotateTo(startRotation).RotateTo(startRotation + 720, duration); + this.RotateTo(startRotation).RotateTo(startRotation + 720, duration); } } } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs index 0fcd319a93..8135a31b43 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs @@ -32,16 +32,16 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables VisualRepresentation.Value = (FruitVisualRepresentation)(change.NewValue % 4); }, true); - ScaleContainer.Child = new SkinnableDrawable( + AddInternal(new SkinnableDrawable( new CatchSkinComponent(CatchSkinComponents.Fruit), - _ => new FruitPiece()); + _ => new FruitPiece())); } protected override void UpdateInitialTransforms() { base.UpdateInitialTransforms(); - ScaleContainer.RotateTo((RandomSingle(1) - 0.5f) * 40); + this.RotateTo((RandomSingle(1) - 0.5f) * 40); } } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs index 0877b5e248..b99710ae8e 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs @@ -5,7 +5,6 @@ using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osuTK; namespace osu.Game.Rulesets.Catch.Objects.Drawables @@ -21,7 +20,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables public readonly Bindable IndexInBeatmap = new Bindable(); /// - /// The multiplicative factor applied to scale relative to scale. + /// The multiplicative factor applied to relative to scale. /// protected virtual float ScaleFactor => 1; @@ -32,20 +31,11 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables public float DisplayRadius => CatchHitObject.OBJECT_RADIUS * HitObject.Scale * ScaleFactor; - protected readonly Container ScaleContainer; - protected DrawablePalpableCatchHitObject([CanBeNull] CatchHitObject h) : base(h) { Origin = Anchor.Centre; Size = new Vector2(CatchHitObject.OBJECT_RADIUS * 2); - - AddInternal(ScaleContainer = new Container - { - RelativeSizeAxes = Axes.Both, - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - }); } [BackgroundDependencyLoader] @@ -58,7 +48,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables ScaleBindable.BindValueChanged(scale => { - ScaleContainer.Scale = new Vector2(scale.NewValue * ScaleFactor); + Scale = new Vector2(scale.NewValue * ScaleFactor); }, true); IndexInBeatmap.BindValueChanged(_ => UpdateComboColour()); From 94a59ac3b240c75a00e440f2062ddbd7d9cec6e5 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 8 Dec 2020 20:41:26 +0900 Subject: [PATCH 03/37] Make catch hit lighting logic not dependent on caught object --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 33 +++++++++++++++------------ 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 76c5afeda7..ac0f25e21b 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -215,10 +215,19 @@ namespace osu.Game.Rulesets.Catch.UI catchResult.CatcherAnimationState = CurrentState; catchResult.CatcherHyperDash = HyperDashing; - if (!(drawableObject.HitObject is PalpableCatchHitObject hitObject)) return; + if (!(drawableObject is DrawablePalpableCatchHitObject palpableObject)) return; + + var hitObject = palpableObject.HitObject; if (result.IsHit) - placeCaughtObject(hitObject); + { + var positionInStack = computePositionInStack(new Vector2(palpableObject.X - X, 0), palpableObject.DisplayRadius); + + placeCaughtObject(hitObject, positionInStack); + + if (hitLighting.Value) + addLighting(hitObject, positionInStack.X, drawableObject.AccentColour.Value); + } // droplet doesn't affect the catcher state if (hitObject is TinyDroplet) return; @@ -441,16 +450,14 @@ namespace osu.Game.Rulesets.Catch.UI updateCatcher(); } - private void placeCaughtObject(PalpableCatchHitObject source) + private void placeCaughtObject(PalpableCatchHitObject source, Vector2 position) { var caughtObject = createCaughtObject(source); if (caughtObject == null) return; - var positionInStack = computePositionInStack(new Vector2(source.X - X, 0), caughtObject.DisplayRadius); - caughtObject.RelativePositionAxes = Axes.None; - caughtObject.Position = positionInStack; + caughtObject.Position = position; caughtObject.IsOnPlate = true; caughtObject.Anchor = Anchor.TopCentre; @@ -461,8 +468,6 @@ namespace osu.Game.Rulesets.Catch.UI caughtFruitContainer.Add(caughtObject); - addLighting(caughtObject); - if (!caughtObject.StaysOnPlate) removeFromPlate(caughtObject, DroppedObjectAnimation.Explode); } @@ -485,15 +490,13 @@ namespace osu.Game.Rulesets.Catch.UI return position; } - private void addLighting(DrawablePalpableCatchHitObject caughtObject) + private void addLighting(CatchHitObject hitObject, float x, Color4 colour) { - if (!hitLighting.Value) return; - HitExplosion hitExplosion = hitExplosionPool.Get(); - hitExplosion.HitObject = caughtObject.HitObject; - hitExplosion.X = caughtObject.X; - hitExplosion.Scale = new Vector2(caughtObject.HitObject.Scale); - hitExplosion.ObjectColour = caughtObject.AccentColour.Value; + hitExplosion.HitObject = hitObject; + hitExplosion.X = x; + hitExplosion.Scale = new Vector2(hitObject.Scale); + hitExplosion.ObjectColour = colour; hitExplosionContainer.Add(hitExplosion); } From ed50fd445e81589df6375b284fbb003617bd129a Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 8 Dec 2020 21:07:12 +0900 Subject: [PATCH 04/37] Fix hit lighting colour not applied in TestSceneCatcher --- .../TestSceneCatcher.cs | 59 +++++++++++++------ 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs index cf6011d721..94cd0ec8a7 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs @@ -18,6 +18,7 @@ using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects.Drawables; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; +using osu.Game.Skinning; using osu.Game.Tests.Visual; namespace osu.Game.Rulesets.Catch.Tests @@ -65,13 +66,11 @@ namespace osu.Game.Rulesets.Catch.Tests JudgementResult result2 = null; AddStep("catch hyper fruit", () => { - drawableObject1 = createDrawableObject(new Fruit { HyperDashTarget = new Fruit { X = 100 } }); - result1 = attemptCatch(drawableObject1); + attemptCatch(new Fruit { HyperDashTarget = new Fruit { X = 100 } }, out drawableObject1, out result1); }); AddStep("catch normal fruit", () => { - drawableObject2 = createDrawableObject(new Fruit()); - result2 = attemptCatch(drawableObject2); + attemptCatch(new Fruit(), out drawableObject2, out result2); }); AddStep("revert second result", () => { @@ -92,8 +91,7 @@ namespace osu.Game.Rulesets.Catch.Tests JudgementResult result = null; AddStep("catch kiai fruit", () => { - drawableObject = createDrawableObject(new TestKiaiFruit()); - result = attemptCatch(drawableObject); + attemptCatch(new TestKiaiFruit(), out drawableObject, out result); }); checkState(CatcherAnimationState.Kiai); AddStep("revert result", () => @@ -200,13 +198,22 @@ namespace osu.Game.Rulesets.Catch.Tests AddAssert("fruits are dropped", () => !catcher.CaughtObjects.Any() && droppedObjectContainer.Count == 10); } - [TestCase(true)] - [TestCase(false)] - public void TestHitLighting(bool enabled) + [Test] + public void TestHitLightingColour() { - AddStep($"{(enabled ? "enable" : "disable")} hit lighting", () => config.Set(OsuSetting.HitLighting, enabled)); + var fruitColour = SkinConfiguration.DefaultComboColours[1]; + AddStep("enable hit lighting", () => config.Set(OsuSetting.HitLighting, true)); AddStep("catch fruit", () => attemptCatch(new Fruit())); - AddAssert("check hit lighting", () => catcher.ChildrenOfType().Any() == enabled); + AddAssert("correct hit lighting colour", () => + catcher.ChildrenOfType().First()?.ObjectColour == fruitColour); + } + + [Test] + public void TestHitLightingDisabled() + { + AddStep("disable hit lighting", () => config.Set(OsuSetting.HitLighting, false)); + AddStep("catch fruit", () => attemptCatch(new Fruit())); + AddAssert("no hit lighting", () => !catcher.ChildrenOfType().Any()); } private void checkPlate(int count) => AddAssert($"{count} objects on the plate", () => catcher.CaughtObjects.Count() == count); @@ -218,18 +225,34 @@ namespace osu.Game.Rulesets.Catch.Tests private void attemptCatch(CatchHitObject hitObject, int count = 1) { for (var i = 0; i < count; i++) - attemptCatch(createDrawableObject(hitObject)); + attemptCatch(hitObject, out _, out _); } - private JudgementResult attemptCatch(DrawableCatchHitObject drawableObject) + private void attemptCatch(CatchHitObject hitObject, out DrawableCatchHitObject drawableObject, out JudgementResult result) { - drawableObject.HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); - var result = new CatchJudgementResult(drawableObject.HitObject, drawableObject.HitObject.CreateJudgement()) + hitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + drawableObject = createDrawableObject(hitObject); + result = createResult(hitObject); + applyResult(drawableObject, result); + } + + private void applyResult(DrawableCatchHitObject drawableObject, JudgementResult result) + { + // Load DHO to set colour of hit explosion correctly + Add(drawableObject); + drawableObject.OnLoadComplete += _ => { - Type = catcher.CanCatch(drawableObject.HitObject) ? HitResult.Great : HitResult.Miss + catcher.OnNewResult(drawableObject, result); + drawableObject.Expire(); + }; + } + + private JudgementResult createResult(CatchHitObject hitObject) + { + return new CatchJudgementResult(hitObject, hitObject.CreateJudgement()) + { + Type = catcher.CanCatch(hitObject) ? HitResult.Great : HitResult.Miss }; - catcher.OnNewResult(drawableObject, result); - return result; } private DrawableCatchHitObject createDrawableObject(CatchHitObject hitObject) From c301223d8cf12897e6d0327b11c505b59798be3d Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 8 Dec 2020 20:34:08 +0900 Subject: [PATCH 05/37] Make object on the catcher plate separate CaughtObject class --- .../TestSceneCatcher.cs | 2 +- .../Objects/Drawables/CaughtObject.cs | 83 +++++++++++++++++++ .../Drawables/DrawableCatchHitObject.cs | 4 - .../Objects/Drawables/DrawableDroplet.cs | 2 - .../DrawablePalpableCatchHitObject.cs | 7 +- .../Skinning/Default/CatchHitObjectPiece.cs | 16 +++- .../Skinning/Default/FruitPiece.cs | 4 + .../Skinning/Legacy/LegacyFruitPiece.cs | 4 + .../Skinning/LegacyCatchHitObjectPiece.cs | 7 ++ osu.Game.Rulesets.Catch/UI/Catcher.cs | 76 ++++++----------- 10 files changed, 141 insertions(+), 64 deletions(-) create mode 100644 osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs index 94cd0ec8a7..2747524503 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs @@ -275,7 +275,7 @@ namespace osu.Game.Rulesets.Catch.Tests public class TestCatcher : Catcher { - public IEnumerable CaughtObjects => this.ChildrenOfType(); + public IEnumerable CaughtObjects => this.ChildrenOfType(); public TestCatcher(Container trailsTarget, Container droppedObjectTarget, BeatmapDifficulty difficulty) : base(trailsTarget, droppedObjectTarget, difficulty) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs new file mode 100644 index 0000000000..0f3a143553 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs @@ -0,0 +1,83 @@ +// 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 osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Catch.Skinning.Default; +using osu.Game.Skinning; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Catch.Objects.Drawables +{ + [Cached(typeof(CaughtObject))] + public abstract class CaughtObject : SkinnableDrawable + { + public readonly Bindable AccentColour = new Bindable(); + + public CatchHitObject HitObject { get; private set; } + + /// + /// Whether this hit object should stay on the catcher plate when the object is caught by the catcher. + /// + public virtual bool StaysOnPlate => true; + + public override bool RemoveWhenNotAlive => true; + + protected CaughtObject(CatchSkinComponents skinComponent, Func defaultImplementation) + : base(new CatchSkinComponent(skinComponent), defaultImplementation) + { + Anchor = Anchor.TopCentre; + Origin = Anchor.Centre; + + RelativeSizeAxes = Axes.None; + Size = new Vector2(CatchHitObject.OBJECT_RADIUS * 2); + } + + public virtual void CopyFrom(DrawablePalpableCatchHitObject drawableObject) + { + HitObject = drawableObject.HitObject; + Scale = drawableObject.Scale / 2; + Rotation = drawableObject.Rotation; + AccentColour.Value = drawableObject.AccentColour.Value; + } + } + + public class CaughtFruit : CaughtObject + { + public readonly Bindable VisualRepresentation = new Bindable(); + + public CaughtFruit() + : base(CatchSkinComponents.Fruit, _ => new FruitPiece()) + { + } + + public override void CopyFrom(DrawablePalpableCatchHitObject drawableObject) + { + base.CopyFrom(drawableObject); + + var drawableFruit = (DrawableFruit)drawableObject; + VisualRepresentation.Value = drawableFruit.VisualRepresentation.Value; + } + } + + public class CaughtBanana : CaughtObject + { + public CaughtBanana() + : base(CatchSkinComponents.Banana, _ => new BananaPiece()) + { + } + } + + public class CaughtDroplet : CaughtObject + { + public override bool StaysOnPlate => false; + + public CaughtDroplet() + : base(CatchSkinComponents.Droplet, _ => new DropletPiece()) + { + } + } +} diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs index 70efe9cf29..bfd124c691 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs @@ -50,10 +50,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables public Func CheckPosition; - public bool IsOnPlate; - - public override bool RemoveWhenNotAlive => IsOnPlate; - protected override JudgementResult CreateResult(Judgement judgement) => new CatchJudgementResult(HitObject, judgement); protected override void CheckForResult(bool userTriggered, double timeOffset) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs index d96b1c8902..c5f0bb8b18 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs @@ -11,8 +11,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables { public class DrawableDroplet : DrawablePalpableCatchHitObject { - public override bool StaysOnPlate => false; - public DrawableDroplet() : this(null) { diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs index b99710ae8e..34d9cf820e 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs @@ -24,11 +24,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables /// protected virtual float ScaleFactor => 1; - /// - /// Whether this hit object should stay on the catcher plate when the object is caught by the catcher. - /// - public virtual bool StaysOnPlate => true; - public float DisplayRadius => CatchHitObject.OBJECT_RADIUS * HitObject.Scale * ScaleFactor; protected DrawablePalpableCatchHitObject([CanBeNull] CatchHitObject h) @@ -43,7 +38,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables { XBindable.BindValueChanged(x => { - if (!IsOnPlate) X = x.NewValue; + X = x.NewValue; }, true); ScaleBindable.BindValueChanged(scale => diff --git a/osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs index d59b6cc0de..cb326b77e2 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs @@ -21,6 +21,10 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default [CanBeNull] protected DrawableHitObject DrawableHitObject { get; private set; } + [Resolved(canBeNull: true)] + [CanBeNull] + protected CaughtObject CaughtObject { get; private set; } + /// /// A part of this piece that will be faded out while falling in the playfield. /// @@ -45,6 +49,9 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default HyperDash.BindTo(hitObject.HyperDash); } + if (CaughtObject != null) + AccentColour.BindTo(CaughtObject.AccentColour); + HyperDash.BindValueChanged(hyper => { if (HyperBorderPiece != null) @@ -54,8 +61,13 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default protected override void Update() { - if (BorderPiece != null && DrawableHitObject?.HitObject != null) - BorderPiece.Alpha = (float)Math.Clamp((DrawableHitObject.HitObject.StartTime - Time.Current) / 500, 0, 1); + if (BorderPiece != null) + { + if (DrawableHitObject?.HitObject != null) + BorderPiece.Alpha = (float)Math.Clamp((DrawableHitObject.HitObject.StartTime - Time.Current) / 500, 0, 1); + else + BorderPiece.Alpha = 0; + } } } } diff --git a/osu.Game.Rulesets.Catch/Skinning/Default/FruitPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Default/FruitPiece.cs index 2e3803a31a..8c705cec4f 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Default/FruitPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Default/FruitPiece.cs @@ -43,6 +43,10 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default if (fruit != null) VisualRepresentation.BindTo(fruit.VisualRepresentation); + + var caughtFruit = (CaughtFruit)CaughtObject; + if (caughtFruit != null) + VisualRepresentation.BindTo(caughtFruit.VisualRepresentation); } } } diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyFruitPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyFruitPiece.cs index 6f93e68594..5140c62d3e 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyFruitPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyFruitPiece.cs @@ -19,6 +19,10 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy if (fruit != null) VisualRepresentation.BindTo(fruit.VisualRepresentation); + var caughtFruit = (CaughtFruit)CaughtObject; + if (caughtFruit != null) + VisualRepresentation.BindTo(caughtFruit.VisualRepresentation); + VisualRepresentation.BindValueChanged(visual => setTexture(visual.NewValue), true); } diff --git a/osu.Game.Rulesets.Catch/Skinning/LegacyCatchHitObjectPiece.cs b/osu.Game.Rulesets.Catch/Skinning/LegacyCatchHitObjectPiece.cs index 1e68439402..9527f15ff1 100644 --- a/osu.Game.Rulesets.Catch/Skinning/LegacyCatchHitObjectPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/LegacyCatchHitObjectPiece.cs @@ -33,6 +33,10 @@ namespace osu.Game.Rulesets.Catch.Skinning [CanBeNull] protected DrawableHitObject DrawableHitObject { get; private set; } + [Resolved(canBeNull: true)] + [CanBeNull] + protected CaughtObject CaughtObject { get; private set; } + protected LegacyCatchHitObjectPiece() { RelativeSizeAxes = Axes.Both; @@ -73,6 +77,9 @@ namespace osu.Game.Rulesets.Catch.Skinning HyperDash.BindTo(hitObject.HyperDash); } + if (CaughtObject != null) + AccentColour.BindTo(CaughtObject.AccentColour); + hyperSprite.Colour = Skin.GetConfig(CatchSkinColour.HyperDashFruit)?.Value ?? Skin.GetConfig(CatchSkinColour.HyperDash)?.Value ?? Catcher.DEFAULT_HYPER_DASH_COLOUR; diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index ac0f25e21b..756582e3f2 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Catch.UI private readonly Container droppedObjectTarget; - private readonly Container caughtFruitContainer; + private readonly Container caughtFruitContainer; public CatcherAnimationState CurrentState { get; private set; } @@ -124,7 +124,7 @@ namespace osu.Game.Rulesets.Catch.UI InternalChildren = new Drawable[] { hitExplosionPool = new DrawablePool(10), - caughtFruitContainer = new Container + caughtFruitContainer = new Container { Anchor = Anchor.TopCentre, Origin = Anchor.BottomCentre, @@ -223,7 +223,7 @@ namespace osu.Game.Rulesets.Catch.UI { var positionInStack = computePositionInStack(new Vector2(palpableObject.X - X, 0), palpableObject.DisplayRadius); - placeCaughtObject(hitObject, positionInStack); + placeCaughtObject(palpableObject, positionInStack); if (hitLighting.Value) addLighting(hitObject, positionInStack.X, drawableObject.AccentColour.Value); @@ -450,21 +450,14 @@ namespace osu.Game.Rulesets.Catch.UI updateCatcher(); } - private void placeCaughtObject(PalpableCatchHitObject source, Vector2 position) + private void placeCaughtObject(DrawablePalpableCatchHitObject drawableObject, Vector2 position) { - var caughtObject = createCaughtObject(source); + var caughtObject = createCaughtObject(drawableObject.HitObject); if (caughtObject == null) return; - caughtObject.RelativePositionAxes = Axes.None; + caughtObject.CopyFrom(drawableObject); caughtObject.Position = position; - caughtObject.IsOnPlate = true; - - caughtObject.Anchor = Anchor.TopCentre; - caughtObject.Origin = Anchor.Centre; - caughtObject.Scale *= 0.5f; - caughtObject.LifetimeStart = source.StartTime; - caughtObject.LifetimeEnd = double.MaxValue; caughtFruitContainer.Add(caughtObject); @@ -500,21 +493,18 @@ namespace osu.Game.Rulesets.Catch.UI hitExplosionContainer.Add(hitExplosion); } - private DrawablePalpableCatchHitObject createCaughtObject(PalpableCatchHitObject source) + private CaughtObject createCaughtObject(PalpableCatchHitObject source) { switch (source) { - case Banana banana: - return new DrawableBanana(banana); + case Fruit _: + return new CaughtFruit(); - case Fruit fruit: - return new DrawableFruit(fruit); + case Banana _: + return new CaughtBanana(); - case TinyDroplet tiny: - return new DrawableTinyDroplet(tiny); - - case Droplet droplet: - return new DrawableDroplet(droplet); + case Droplet _: + return new CaughtDroplet(); default: return null; @@ -532,7 +522,7 @@ namespace osu.Game.Rulesets.Catch.UI drop(caughtObject, animation); } - private void removeFromPlate(DrawablePalpableCatchHitObject caughtObject, DroppedObjectAnimation animation) + private void removeFromPlate(CaughtObject caughtObject, DroppedObjectAnimation animation) { if (!caughtFruitContainer.Remove(caughtObject)) throw new InvalidOperationException("Can only drop a caught object on the plate"); @@ -542,40 +532,28 @@ namespace osu.Game.Rulesets.Catch.UI drop(caughtObject, animation); } - private void drop(DrawablePalpableCatchHitObject d, DroppedObjectAnimation animation) + private void drop(CaughtObject d, DroppedObjectAnimation animation) { var originalX = d.X * Scale.X; - var startTime = Clock.CurrentTime; d.Anchor = Anchor.TopLeft; d.Position = caughtFruitContainer.ToSpaceOfOtherDrawable(d.DrawPosition, droppedObjectTarget); - // we cannot just apply the transforms because DHO clears transforms when state is updated - d.ApplyCustomUpdateState += (o, state) => animate(o, animation, originalX, startTime); - if (d.IsLoaded) - animate(d, animation, originalX, startTime); - } - - private void animate(Drawable d, DroppedObjectAnimation animation, float originalX, double startTime) - { - using (d.BeginAbsoluteSequence(startTime)) + switch (animation) { - switch (animation) - { - case DroppedObjectAnimation.Drop: - d.MoveToY(d.Y + 75, 750, Easing.InSine); - d.FadeOut(750); - break; + case DroppedObjectAnimation.Drop: + d.MoveToY(d.Y + 75, 750, Easing.InSine); + d.FadeOut(750); + break; - case DroppedObjectAnimation.Explode: - d.MoveToY(d.Y - 50, 250, Easing.OutSine).Then().MoveToY(d.Y + 50, 500, Easing.InSine); - d.MoveToX(d.X + originalX * 6, 1000); - d.FadeOut(750); - break; - } - - d.Expire(); + case DroppedObjectAnimation.Explode: + d.MoveToY(d.Y - 50, 250, Easing.OutSine).Then().MoveToY(d.Y + 50, 500, Easing.InSine); + d.MoveToX(d.X + originalX * 6, 1000); + d.FadeOut(750); + break; } + + d.Expire(); } private enum DroppedObjectAnimation From a32dac00ddf388f2359dd97ebd23fc7faa68d687 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 8 Dec 2020 21:29:03 +0900 Subject: [PATCH 06/37] Introduce IHasCatchObjectState implemented by DHO and CaughtObject --- .../Objects/Drawables/CaughtObject.cs | 31 ++++++++++--------- .../Objects/Drawables/DrawableBanana.cs | 2 +- .../Objects/Drawables/DrawableDroplet.cs | 2 +- .../Objects/Drawables/DrawableFruit.cs | 4 +-- ...s => DrawablePalpableHasCatchHitObject.cs} | 14 ++++++--- .../Objects/Drawables/IHasCatchObjectState.cs | 24 ++++++++++++++ .../Skinning/Default/CatchHitObjectPiece.cs | 31 ++++--------------- .../Skinning/Default/FruitPiece.cs | 10 ++---- .../Skinning/Legacy/LegacyFruitPiece.cs | 10 ++---- .../Skinning/LegacyCatchHitObjectPiece.cs | 23 +++----------- osu.Game.Rulesets.Catch/UI/Catcher.cs | 5 +-- 11 files changed, 70 insertions(+), 86 deletions(-) rename osu.Game.Rulesets.Catch/Objects/Drawables/{DrawablePalpableCatchHitObject.cs => DrawablePalpableHasCatchHitObject.cs} (78%) create mode 100644 osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs index 0f3a143553..d3555ea771 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs @@ -12,12 +12,12 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.Objects.Drawables { - [Cached(typeof(CaughtObject))] - public abstract class CaughtObject : SkinnableDrawable + [Cached(typeof(IHasCatchObjectState))] + public abstract class CaughtObject : SkinnableDrawable, IHasCatchObjectState { - public readonly Bindable AccentColour = new Bindable(); - public CatchHitObject HitObject { get; private set; } + public Bindable AccentColour { get; } = new Bindable(); + public Bindable HyperDash { get; } = new Bindable(); /// /// Whether this hit object should stay on the catcher plate when the object is caught by the catcher. @@ -36,30 +36,31 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables Size = new Vector2(CatchHitObject.OBJECT_RADIUS * 2); } - public virtual void CopyFrom(DrawablePalpableCatchHitObject drawableObject) + public virtual void CopyFrom(IHasCatchObjectState objectState) { - HitObject = drawableObject.HitObject; - Scale = drawableObject.Scale / 2; - Rotation = drawableObject.Rotation; - AccentColour.Value = drawableObject.AccentColour.Value; + HitObject = objectState.HitObject; + Scale = objectState.Scale; + Rotation = objectState.Rotation; + AccentColour.Value = objectState.AccentColour.Value; + HyperDash.Value = objectState.HyperDash.Value; } } - public class CaughtFruit : CaughtObject + public class CaughtFruit : CaughtObject, IHasFruitState { - public readonly Bindable VisualRepresentation = new Bindable(); + public Bindable VisualRepresentation { get; } = new Bindable(); public CaughtFruit() : base(CatchSkinComponents.Fruit, _ => new FruitPiece()) { } - public override void CopyFrom(DrawablePalpableCatchHitObject drawableObject) + public override void CopyFrom(IHasCatchObjectState objectState) { - base.CopyFrom(drawableObject); + base.CopyFrom(objectState); - var drawableFruit = (DrawableFruit)drawableObject; - VisualRepresentation.Value = drawableFruit.VisualRepresentation.Value; + var fruitState = (IHasFruitState)objectState; + VisualRepresentation.Value = fruitState.VisualRepresentation.Value; } } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs index 4bc5da2396..34d89eb188 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs @@ -9,7 +9,7 @@ using osu.Game.Skinning; namespace osu.Game.Rulesets.Catch.Objects.Drawables { - public class DrawableBanana : DrawablePalpableCatchHitObject + public class DrawableBanana : DrawablePalpableHasCatchHitObject { public DrawableBanana() : this(null) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs index c5f0bb8b18..acdb3bb38c 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs @@ -9,7 +9,7 @@ using osu.Game.Skinning; namespace osu.Game.Rulesets.Catch.Objects.Drawables { - public class DrawableDroplet : DrawablePalpableCatchHitObject + public class DrawableDroplet : DrawablePalpableHasCatchHitObject { public DrawableDroplet() : this(null) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs index 8135a31b43..6ec46f6535 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs @@ -10,9 +10,9 @@ using osu.Game.Skinning; namespace osu.Game.Rulesets.Catch.Objects.Drawables { - public class DrawableFruit : DrawablePalpableCatchHitObject + public class DrawableFruit : DrawablePalpableHasCatchHitObject, IHasFruitState { - public readonly Bindable VisualRepresentation = new Bindable(); + public Bindable VisualRepresentation { get; } = new Bindable(); public DrawableFruit() : this(null) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableHasCatchHitObject.cs similarity index 78% rename from osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs rename to osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableHasCatchHitObject.cs index 34d9cf820e..ee893f7880 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableHasCatchHitObject.cs @@ -6,18 +6,22 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osuTK; +using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.Objects.Drawables { - public abstract class DrawablePalpableCatchHitObject : DrawableCatchHitObject + [Cached(typeof(IHasCatchObjectState))] + public abstract class DrawablePalpableHasCatchHitObject : DrawableCatchHitObject, IHasCatchObjectState { public new PalpableCatchHitObject HitObject => (PalpableCatchHitObject)base.HitObject; - public readonly Bindable HyperDash = new Bindable(); + Bindable IHasCatchObjectState.AccentColour => AccentColour; - public readonly Bindable ScaleBindable = new Bindable(1); + public Bindable HyperDash { get; } = new Bindable(); - public readonly Bindable IndexInBeatmap = new Bindable(); + public Bindable ScaleBindable { get; } = new Bindable(1); + + public Bindable IndexInBeatmap { get; } = new Bindable(); /// /// The multiplicative factor applied to relative to scale. @@ -26,7 +30,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables public float DisplayRadius => CatchHitObject.OBJECT_RADIUS * HitObject.Scale * ScaleFactor; - protected DrawablePalpableCatchHitObject([CanBeNull] CatchHitObject h) + protected DrawablePalpableHasCatchHitObject([CanBeNull] CatchHitObject h) : base(h) { Origin = Anchor.Centre; diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs new file mode 100644 index 0000000000..01d833b61a --- /dev/null +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs @@ -0,0 +1,24 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Bindables; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Catch.Objects.Drawables +{ + public interface IHasCatchObjectState + { + CatchHitObject HitObject { get; } + Bindable AccentColour { get; } + Bindable HyperDash { get; } + + float Rotation { get; } + Vector2 Scale { get; } + } + + public interface IHasFruitState : IHasCatchObjectState + { + Bindable VisualRepresentation { get; } + } +} diff --git a/osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs index cb326b77e2..b105fb4034 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs @@ -7,7 +7,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Catch.Objects.Drawables; -using osu.Game.Rulesets.Objects.Drawables; using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.Skinning.Default @@ -17,13 +16,8 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default public readonly Bindable AccentColour = new Bindable(); public readonly Bindable HyperDash = new Bindable(); - [Resolved(canBeNull: true)] - [CanBeNull] - protected DrawableHitObject DrawableHitObject { get; private set; } - - [Resolved(canBeNull: true)] - [CanBeNull] - protected CaughtObject CaughtObject { get; private set; } + [Resolved] + protected IHasCatchObjectState ObjectState { get; private set; } /// /// A part of this piece that will be faded out while falling in the playfield. @@ -41,16 +35,8 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default { base.LoadComplete(); - var hitObject = (DrawablePalpableCatchHitObject)DrawableHitObject; - - if (hitObject != null) - { - AccentColour.BindTo(hitObject.AccentColour); - HyperDash.BindTo(hitObject.HyperDash); - } - - if (CaughtObject != null) - AccentColour.BindTo(CaughtObject.AccentColour); + AccentColour.BindTo(ObjectState.AccentColour); + HyperDash.BindTo(ObjectState.HyperDash); HyperDash.BindValueChanged(hyper => { @@ -61,13 +47,8 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default protected override void Update() { - if (BorderPiece != null) - { - if (DrawableHitObject?.HitObject != null) - BorderPiece.Alpha = (float)Math.Clamp((DrawableHitObject.HitObject.StartTime - Time.Current) / 500, 0, 1); - else - BorderPiece.Alpha = 0; - } + if (BorderPiece != null && ObjectState?.HitObject != null) + BorderPiece.Alpha = (float)Math.Clamp((ObjectState.HitObject.StartTime - Time.Current) / 500, 0, 1); } } } diff --git a/osu.Game.Rulesets.Catch/Skinning/Default/FruitPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Default/FruitPiece.cs index 8c705cec4f..49f128c960 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Default/FruitPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Default/FruitPiece.cs @@ -39,14 +39,8 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default { base.LoadComplete(); - var fruit = (DrawableFruit)DrawableHitObject; - - if (fruit != null) - VisualRepresentation.BindTo(fruit.VisualRepresentation); - - var caughtFruit = (CaughtFruit)CaughtObject; - if (caughtFruit != null) - VisualRepresentation.BindTo(caughtFruit.VisualRepresentation); + var fruitState = (IHasFruitState)ObjectState; + VisualRepresentation.BindTo(fruitState.VisualRepresentation); } } } diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyFruitPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyFruitPiece.cs index 5140c62d3e..969cc38e5b 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyFruitPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyFruitPiece.cs @@ -14,14 +14,8 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy { base.LoadComplete(); - var fruit = (DrawableFruit)DrawableHitObject; - - if (fruit != null) - VisualRepresentation.BindTo(fruit.VisualRepresentation); - - var caughtFruit = (CaughtFruit)CaughtObject; - if (caughtFruit != null) - VisualRepresentation.BindTo(caughtFruit.VisualRepresentation); + var fruitState = (IHasFruitState)ObjectState; + VisualRepresentation.BindTo(fruitState.VisualRepresentation); VisualRepresentation.BindValueChanged(visual => setTexture(visual.NewValue), true); } diff --git a/osu.Game.Rulesets.Catch/Skinning/LegacyCatchHitObjectPiece.cs b/osu.Game.Rulesets.Catch/Skinning/LegacyCatchHitObjectPiece.cs index 9527f15ff1..4b1f5a4724 100644 --- a/osu.Game.Rulesets.Catch/Skinning/LegacyCatchHitObjectPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/LegacyCatchHitObjectPiece.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -10,7 +9,6 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Game.Rulesets.Catch.Objects.Drawables; using osu.Game.Rulesets.Catch.UI; -using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Skinning; using osuTK; using osuTK.Graphics; @@ -29,13 +27,8 @@ namespace osu.Game.Rulesets.Catch.Skinning [Resolved] protected ISkinSource Skin { get; private set; } - [Resolved(canBeNull: true)] - [CanBeNull] - protected DrawableHitObject DrawableHitObject { get; private set; } - - [Resolved(canBeNull: true)] - [CanBeNull] - protected CaughtObject CaughtObject { get; private set; } + [Resolved] + protected IHasCatchObjectState ObjectState { get; private set; } protected LegacyCatchHitObjectPiece() { @@ -69,16 +62,8 @@ namespace osu.Game.Rulesets.Catch.Skinning { base.LoadComplete(); - var hitObject = (DrawablePalpableCatchHitObject)DrawableHitObject; - - if (hitObject != null) - { - AccentColour.BindTo(hitObject.AccentColour); - HyperDash.BindTo(hitObject.HyperDash); - } - - if (CaughtObject != null) - AccentColour.BindTo(CaughtObject.AccentColour); + AccentColour.BindTo(ObjectState.AccentColour); + HyperDash.BindTo(ObjectState.HyperDash); hyperSprite.Colour = Skin.GetConfig(CatchSkinColour.HyperDashFruit)?.Value ?? Skin.GetConfig(CatchSkinColour.HyperDash)?.Value ?? diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 756582e3f2..4d540bce03 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -215,7 +215,7 @@ namespace osu.Game.Rulesets.Catch.UI catchResult.CatcherAnimationState = CurrentState; catchResult.CatcherHyperDash = HyperDashing; - if (!(drawableObject is DrawablePalpableCatchHitObject palpableObject)) return; + if (!(drawableObject is DrawablePalpableHasCatchHitObject palpableObject)) return; var hitObject = palpableObject.HitObject; @@ -450,7 +450,7 @@ namespace osu.Game.Rulesets.Catch.UI updateCatcher(); } - private void placeCaughtObject(DrawablePalpableCatchHitObject drawableObject, Vector2 position) + private void placeCaughtObject(DrawablePalpableHasCatchHitObject drawableObject, Vector2 position) { var caughtObject = createCaughtObject(drawableObject.HitObject); @@ -458,6 +458,7 @@ namespace osu.Game.Rulesets.Catch.UI caughtObject.CopyFrom(drawableObject); caughtObject.Position = position; + caughtObject.Scale /= 2; caughtFruitContainer.Add(caughtObject); From 749d5380ca5a1c5bd118307e4f9459cd7df00130 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 8 Dec 2020 22:38:10 +0900 Subject: [PATCH 07/37] Pool caught objects and dropped objects --- .../TestSceneCatcherArea.cs | 5 +- .../Objects/Drawables/CaughtObject.cs | 14 ++++- .../Objects/Drawables/IHasCatchObjectState.cs | 2 +- osu.Game.Rulesets.Catch/UI/Catcher.cs | 52 +++++++++++++------ 4 files changed, 53 insertions(+), 20 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs index 8602c7aad1..3d5e44476e 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs @@ -73,7 +73,10 @@ namespace osu.Game.Rulesets.Catch.Tests SetContents(() => { - var droppedObjectContainer = new Container(); + var droppedObjectContainer = new Container + { + RelativeSizeAxes = Axes.Both + }; return new CatchInputManager(catchRuleset) { diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs index d3555ea771..f36d287126 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables [Cached(typeof(IHasCatchObjectState))] public abstract class CaughtObject : SkinnableDrawable, IHasCatchObjectState { - public CatchHitObject HitObject { get; private set; } + public PalpableCatchHitObject HitObject { get; private set; } public Bindable AccentColour { get; } = new Bindable(); public Bindable HyperDash { get; } = new Bindable(); @@ -29,7 +29,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables protected CaughtObject(CatchSkinComponents skinComponent, Func defaultImplementation) : base(new CatchSkinComponent(skinComponent), defaultImplementation) { - Anchor = Anchor.TopCentre; Origin = Anchor.Centre; RelativeSizeAxes = Axes.None; @@ -44,6 +43,17 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables AccentColour.Value = objectState.AccentColour.Value; HyperDash.Value = objectState.HyperDash.Value; } + + protected override void FreeAfterUse() + { + ClearTransforms(); + + Alpha = 1; + LifetimeStart = double.MinValue; + LifetimeEnd = double.MaxValue; + + base.FreeAfterUse(); + } } public class CaughtFruit : CaughtObject, IHasFruitState diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs index 01d833b61a..a282bc5da0 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs @@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables { public interface IHasCatchObjectState { - CatchHitObject HitObject { get; } + PalpableCatchHitObject HitObject { get; } Bindable AccentColour { get; } Bindable HyperDash { get; } diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 4d540bce03..7aecd95efd 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -108,6 +108,10 @@ namespace osu.Game.Rulesets.Catch.UI private readonly DrawablePool hitExplosionPool; private readonly Container hitExplosionContainer; + private readonly DrawablePool caughtFruitPool; + private readonly DrawablePool caughtBananaPool; + private readonly DrawablePool caughtDropletPool; + public Catcher([NotNull] Container trailsTarget, [NotNull] Container droppedObjectTarget, BeatmapDifficulty difficulty = null) { this.trailsTarget = trailsTarget; @@ -124,6 +128,10 @@ namespace osu.Game.Rulesets.Catch.UI InternalChildren = new Drawable[] { hitExplosionPool = new DrawablePool(10), + caughtFruitPool = new DrawablePool(50), + caughtBananaPool = new DrawablePool(100), + // less capacity is needed compared to fruit because droplet is not stacked + caughtDropletPool = new DrawablePool(25), caughtFruitContainer = new Container { Anchor = Anchor.TopCentre, @@ -452,11 +460,12 @@ namespace osu.Game.Rulesets.Catch.UI private void placeCaughtObject(DrawablePalpableHasCatchHitObject drawableObject, Vector2 position) { - var caughtObject = createCaughtObject(drawableObject.HitObject); + var caughtObject = getCaughtObject(drawableObject.HitObject); if (caughtObject == null) return; caughtObject.CopyFrom(drawableObject); + caughtObject.Anchor = Anchor.TopCentre; caughtObject.Position = position; caughtObject.Scale /= 2; @@ -494,52 +503,62 @@ namespace osu.Game.Rulesets.Catch.UI hitExplosionContainer.Add(hitExplosion); } - private CaughtObject createCaughtObject(PalpableCatchHitObject source) + private CaughtObject getCaughtObject(PalpableCatchHitObject source) { switch (source) { case Fruit _: - return new CaughtFruit(); + return caughtFruitPool.Get(); case Banana _: - return new CaughtBanana(); + return caughtBananaPool.Get(); case Droplet _: - return new CaughtDroplet(); + return caughtDropletPool.Get(); default: return null; } } + private CaughtObject getDroppedObject(CaughtObject caughtObject) + { + var droppedObject = getCaughtObject(caughtObject.HitObject); + + droppedObject.CopyFrom(caughtObject); + droppedObject.Anchor = Anchor.TopLeft; + droppedObject.Position = caughtFruitContainer.ToSpaceOfOtherDrawable(caughtObject.DrawPosition, droppedObjectTarget); + + return droppedObject; + } + private void clearPlate(DroppedObjectAnimation animation) { var caughtObjects = caughtFruitContainer.Children.ToArray(); + var droppedObjects = caughtObjects.Select(getDroppedObject).ToArray(); + caughtFruitContainer.Clear(false); - droppedObjectTarget.AddRange(caughtObjects); + droppedObjectTarget.AddRange(droppedObjects); - foreach (var caughtObject in caughtObjects) - drop(caughtObject, animation); + foreach (var droppedObject in droppedObjects) + applyDropAnimation(droppedObject, animation); } private void removeFromPlate(CaughtObject caughtObject, DroppedObjectAnimation animation) { + var droppedObject = getDroppedObject(caughtObject); + if (!caughtFruitContainer.Remove(caughtObject)) throw new InvalidOperationException("Can only drop a caught object on the plate"); - droppedObjectTarget.Add(caughtObject); + droppedObjectTarget.Add(droppedObject); - drop(caughtObject, animation); + applyDropAnimation(droppedObject, animation); } - private void drop(CaughtObject d, DroppedObjectAnimation animation) + private void applyDropAnimation(Drawable d, DroppedObjectAnimation animation) { - var originalX = d.X * Scale.X; - - d.Anchor = Anchor.TopLeft; - d.Position = caughtFruitContainer.ToSpaceOfOtherDrawable(d.DrawPosition, droppedObjectTarget); - switch (animation) { case DroppedObjectAnimation.Drop: @@ -548,6 +567,7 @@ namespace osu.Game.Rulesets.Catch.UI break; case DroppedObjectAnimation.Explode: + var originalX = droppedObjectTarget.ToSpaceOfOtherDrawable(d.DrawPosition, caughtFruitContainer).X * Scale.X; d.MoveToY(d.Y - 50, 250, Easing.OutSine).Then().MoveToY(d.Y + 50, 500, Easing.InSine); d.MoveToX(d.X + originalX * 6, 1000); d.FadeOut(750); From 1f36bbecd180049026e62f592201434c3cf2c283 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 8 Dec 2020 23:07:30 +0900 Subject: [PATCH 08/37] Fix dropped objects not removed on revert result --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 7aecd95efd..d2930af1ca 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -274,7 +274,7 @@ namespace osu.Game.Rulesets.Catch.UI } caughtFruitContainer.RemoveAll(d => d.HitObject == drawableObject.HitObject); - droppedObjectTarget.RemoveAll(d => (d as DrawableCatchHitObject)?.HitObject == drawableObject.HitObject); + droppedObjectTarget.RemoveAll(d => (d as CaughtObject)?.HitObject == drawableObject.HitObject); hitExplosionContainer.RemoveAll(d => d.HitObject == drawableObject.HitObject); } From 5ca98b00334946cf1f0e0a876cd268374f3a7267 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 8 Dec 2020 23:11:22 +0900 Subject: [PATCH 09/37] Add doc comments a bit --- osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs | 3 +++ .../Objects/Drawables/IHasCatchObjectState.cs | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs index f36d287126..01bf943e1a 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs @@ -12,6 +12,9 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.Objects.Drawables { + /// + /// Represents a caught by the catcher. + /// [Cached(typeof(IHasCatchObjectState))] public abstract class CaughtObject : SkinnableDrawable, IHasCatchObjectState { diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs index a282bc5da0..0a75fb2224 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs @@ -7,6 +7,9 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.Objects.Drawables { + /// + /// Provides a visual state of a . + /// public interface IHasCatchObjectState { PalpableCatchHitObject HitObject { get; } @@ -17,6 +20,9 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables Vector2 Scale { get; } } + /// + /// Provides a visual state of a . + /// public interface IHasFruitState : IHasCatchObjectState { Bindable VisualRepresentation { get; } From 1212ffd24f7c6aae776b796fdab47dec3673d2e8 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 8 Dec 2020 23:35:24 +0900 Subject: [PATCH 10/37] Rename to CopyStateFrom, and add comment --- .../Objects/Drawables/CaughtObject.cs | 9 ++++++--- osu.Game.Rulesets.Catch/UI/Catcher.cs | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs index 01bf943e1a..87319a4498 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs @@ -38,7 +38,10 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables Size = new Vector2(CatchHitObject.OBJECT_RADIUS * 2); } - public virtual void CopyFrom(IHasCatchObjectState objectState) + /// + /// Copies the hit object visual state from another object. + /// + public virtual void CopyStateFrom(IHasCatchObjectState objectState) { HitObject = objectState.HitObject; Scale = objectState.Scale; @@ -68,9 +71,9 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables { } - public override void CopyFrom(IHasCatchObjectState objectState) + public override void CopyStateFrom(IHasCatchObjectState objectState) { - base.CopyFrom(objectState); + base.CopyStateFrom(objectState); var fruitState = (IHasFruitState)objectState; VisualRepresentation.Value = fruitState.VisualRepresentation.Value; diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index d2930af1ca..c9580ca5f8 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -464,7 +464,7 @@ namespace osu.Game.Rulesets.Catch.UI if (caughtObject == null) return; - caughtObject.CopyFrom(drawableObject); + caughtObject.CopyStateFrom(drawableObject); caughtObject.Anchor = Anchor.TopCentre; caughtObject.Position = position; caughtObject.Scale /= 2; @@ -525,7 +525,7 @@ namespace osu.Game.Rulesets.Catch.UI { var droppedObject = getCaughtObject(caughtObject.HitObject); - droppedObject.CopyFrom(caughtObject); + droppedObject.CopyStateFrom(caughtObject); droppedObject.Anchor = Anchor.TopLeft; droppedObject.Position = caughtFruitContainer.ToSpaceOfOtherDrawable(caughtObject.DrawPosition, droppedObjectTarget); From b80204642e52a31163805731db903d1327419a49 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 9 Dec 2020 10:25:35 +0900 Subject: [PATCH 11/37] Revert rename error --- osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs | 2 +- osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs | 2 +- osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs | 2 +- ...HasCatchHitObject.cs => DrawablePalpableCatchHitObject.cs} | 4 ++-- osu.Game.Rulesets.Catch/UI/Catcher.cs | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) rename osu.Game.Rulesets.Catch/Objects/Drawables/{DrawablePalpableHasCatchHitObject.cs => DrawablePalpableCatchHitObject.cs} (92%) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs index 34d89eb188..4bc5da2396 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs @@ -9,7 +9,7 @@ using osu.Game.Skinning; namespace osu.Game.Rulesets.Catch.Objects.Drawables { - public class DrawableBanana : DrawablePalpableHasCatchHitObject + public class DrawableBanana : DrawablePalpableCatchHitObject { public DrawableBanana() : this(null) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs index acdb3bb38c..c5f0bb8b18 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs @@ -9,7 +9,7 @@ using osu.Game.Skinning; namespace osu.Game.Rulesets.Catch.Objects.Drawables { - public class DrawableDroplet : DrawablePalpableHasCatchHitObject + public class DrawableDroplet : DrawablePalpableCatchHitObject { public DrawableDroplet() : this(null) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs index 6ec46f6535..57f27d01b8 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs @@ -10,7 +10,7 @@ using osu.Game.Skinning; namespace osu.Game.Rulesets.Catch.Objects.Drawables { - public class DrawableFruit : DrawablePalpableHasCatchHitObject, IHasFruitState + public class DrawableFruit : DrawablePalpableCatchHitObject, IHasFruitState { public Bindable VisualRepresentation { get; } = new Bindable(); diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableHasCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs similarity index 92% rename from osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableHasCatchHitObject.cs rename to osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs index ee893f7880..aea9d2c082 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableHasCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs @@ -11,7 +11,7 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.Objects.Drawables { [Cached(typeof(IHasCatchObjectState))] - public abstract class DrawablePalpableHasCatchHitObject : DrawableCatchHitObject, IHasCatchObjectState + public abstract class DrawablePalpableCatchHitObject : DrawableCatchHitObject, IHasCatchObjectState { public new PalpableCatchHitObject HitObject => (PalpableCatchHitObject)base.HitObject; @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables public float DisplayRadius => CatchHitObject.OBJECT_RADIUS * HitObject.Scale * ScaleFactor; - protected DrawablePalpableHasCatchHitObject([CanBeNull] CatchHitObject h) + protected DrawablePalpableCatchHitObject([CanBeNull] CatchHitObject h) : base(h) { Origin = Anchor.Centre; diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index c9580ca5f8..30a5832c4f 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -223,7 +223,7 @@ namespace osu.Game.Rulesets.Catch.UI catchResult.CatcherAnimationState = CurrentState; catchResult.CatcherHyperDash = HyperDashing; - if (!(drawableObject is DrawablePalpableHasCatchHitObject palpableObject)) return; + if (!(drawableObject is DrawablePalpableCatchHitObject palpableObject)) return; var hitObject = palpableObject.HitObject; @@ -458,7 +458,7 @@ namespace osu.Game.Rulesets.Catch.UI updateCatcher(); } - private void placeCaughtObject(DrawablePalpableHasCatchHitObject drawableObject, Vector2 position) + private void placeCaughtObject(DrawablePalpableCatchHitObject drawableObject, Vector2 position) { var caughtObject = getCaughtObject(drawableObject.HitObject); From df9de7a8ddee1986df712deb138c7d4238f41bed Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 9 Dec 2020 10:28:42 +0900 Subject: [PATCH 12/37] Remove null check that is not required anymore --- osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs b/osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs index b105fb4034..51c06c8e37 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Default/CatchHitObjectPiece.cs @@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default protected override void Update() { - if (BorderPiece != null && ObjectState?.HitObject != null) + if (BorderPiece != null) BorderPiece.Alpha = (float)Math.Clamp((ObjectState.HitObject.StartTime - Time.Current) / 500, 0, 1); } } From ccca7e0b25a97f52d9ef6d754d2f39fe6132a65b Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 9 Dec 2020 10:35:01 +0900 Subject: [PATCH 13/37] more specific type droppedObjectContainer --- osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs | 6 +++--- .../TestSceneCatcherArea.cs | 4 ++-- .../TestSceneHyperDashColouring.cs | 2 +- osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 2 +- osu.Game.Rulesets.Catch/UI/Catcher.cs | 14 ++++++++++---- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 2 +- 6 files changed, 18 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs index 2747524503..e8bb57cdf3 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Catch.Tests [Resolved] private OsuConfigManager config { get; set; } - private Container droppedObjectContainer; + private Container droppedObjectContainer; private TestCatcher catcher; @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Catch.Tests }; var trailContainer = new Container(); - droppedObjectContainer = new Container(); + droppedObjectContainer = new Container(); catcher = new TestCatcher(trailContainer, droppedObjectContainer, difficulty); Child = new Container @@ -277,7 +277,7 @@ namespace osu.Game.Rulesets.Catch.Tests { public IEnumerable CaughtObjects => this.ChildrenOfType(); - public TestCatcher(Container trailsTarget, Container droppedObjectTarget, BeatmapDifficulty difficulty) + public TestCatcher(Container trailsTarget, Container droppedObjectTarget, BeatmapDifficulty difficulty) : base(trailsTarget, droppedObjectTarget, difficulty) { } diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs index 3d5e44476e..31c285ef22 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcherArea.cs @@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Catch.Tests SetContents(() => { - var droppedObjectContainer = new Container + var droppedObjectContainer = new Container { RelativeSizeAxes = Axes.Both }; @@ -102,7 +102,7 @@ namespace osu.Game.Rulesets.Catch.Tests private class TestCatcherArea : CatcherArea { - public TestCatcherArea(Container droppedObjectContainer, BeatmapDifficulty beatmapDifficulty) + public TestCatcherArea(Container droppedObjectContainer, BeatmapDifficulty beatmapDifficulty) : base(droppedObjectContainer, beatmapDifficulty) { } diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs index d78dc2d2b5..683a776dcc 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDashColouring.cs @@ -118,7 +118,7 @@ namespace osu.Game.Rulesets.Catch.Tests AddStep("create hyper-dashing catcher", () => { - Child = setupSkinHierarchy(catcherArea = new CatcherArea(new Container()) + Child = setupSkinHierarchy(catcherArea = new CatcherArea(new Container()) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index fdc12bf088..73420a9eda 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Catch.UI public CatchPlayfield(BeatmapDifficulty difficulty, Func> createDrawableRepresentation) { - var droppedObjectContainer = new Container + var droppedObjectContainer = new Container { RelativeSizeAxes = Axes.Both, }; diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 30a5832c4f..2893fcee92 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -53,10 +53,16 @@ namespace osu.Game.Rulesets.Catch.UI private CatcherTrailDisplay trails; - private readonly Container droppedObjectTarget; - + /// + /// Contains caught objects on the plate. + /// private readonly Container caughtFruitContainer; + /// + /// Contains objects dropped from the plate. + /// + private readonly Container droppedObjectTarget; + public CatcherAnimationState CurrentState { get; private set; } /// @@ -112,7 +118,7 @@ namespace osu.Game.Rulesets.Catch.UI private readonly DrawablePool caughtBananaPool; private readonly DrawablePool caughtDropletPool; - public Catcher([NotNull] Container trailsTarget, [NotNull] Container droppedObjectTarget, BeatmapDifficulty difficulty = null) + public Catcher([NotNull] Container trailsTarget, [NotNull] Container droppedObjectTarget, BeatmapDifficulty difficulty = null) { this.trailsTarget = trailsTarget; this.droppedObjectTarget = droppedObjectTarget; @@ -274,7 +280,7 @@ namespace osu.Game.Rulesets.Catch.UI } caughtFruitContainer.RemoveAll(d => d.HitObject == drawableObject.HitObject); - droppedObjectTarget.RemoveAll(d => (d as CaughtObject)?.HitObject == drawableObject.HitObject); + droppedObjectTarget.RemoveAll(d => d.HitObject == drawableObject.HitObject); hitExplosionContainer.RemoveAll(d => d.HitObject == drawableObject.HitObject); } diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 857d9141c9..44adbd5512 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Catch.UI public readonly Catcher MovableCatcher; private readonly CatchComboDisplay comboDisplay; - public CatcherArea(Container droppedObjectContainer, BeatmapDifficulty difficulty = null) + public CatcherArea(Container droppedObjectContainer, BeatmapDifficulty difficulty = null) { Size = new Vector2(CatchPlayfield.WIDTH, CATCHER_SIZE); Children = new Drawable[] From c8b0934573c029e7568d63927533dcbddcde9099 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 9 Dec 2020 10:35:36 +0900 Subject: [PATCH 14/37] Rename caughtFruitContainer -> caughtObjectContainer --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 2893fcee92..475fca7f1c 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Catch.UI /// /// Contains caught objects on the plate. /// - private readonly Container caughtFruitContainer; + private readonly Container caughtObjectContainer; /// /// Contains objects dropped from the plate. @@ -138,7 +138,7 @@ namespace osu.Game.Rulesets.Catch.UI caughtBananaPool = new DrawablePool(100), // less capacity is needed compared to fruit because droplet is not stacked caughtDropletPool = new DrawablePool(25), - caughtFruitContainer = new Container + caughtObjectContainer = new Container { Anchor = Anchor.TopCentre, Origin = Anchor.BottomCentre, @@ -186,7 +186,7 @@ namespace osu.Game.Rulesets.Catch.UI /// /// Creates proxied content to be displayed beneath hitobjects. /// - public Drawable CreateProxiedContent() => caughtFruitContainer.CreateProxy(); + public Drawable CreateProxiedContent() => caughtObjectContainer.CreateProxy(); /// /// Calculates the scale of the catcher based off the provided beatmap difficulty. @@ -279,7 +279,7 @@ namespace osu.Game.Rulesets.Catch.UI SetHyperDashState(); } - caughtFruitContainer.RemoveAll(d => d.HitObject == drawableObject.HitObject); + caughtObjectContainer.RemoveAll(d => d.HitObject == drawableObject.HitObject); droppedObjectTarget.RemoveAll(d => d.HitObject == drawableObject.HitObject); hitExplosionContainer.RemoveAll(d => d.HitObject == drawableObject.HitObject); } @@ -475,7 +475,7 @@ namespace osu.Game.Rulesets.Catch.UI caughtObject.Position = position; caughtObject.Scale /= 2; - caughtFruitContainer.Add(caughtObject); + caughtObjectContainer.Add(caughtObject); if (!caughtObject.StaysOnPlate) removeFromPlate(caughtObject, DroppedObjectAnimation.Explode); @@ -486,7 +486,7 @@ namespace osu.Game.Rulesets.Catch.UI const float radius_div_2 = CatchHitObject.OBJECT_RADIUS / 2; const float allowance = 10; - while (caughtFruitContainer.Any(f => Vector2Extensions.Distance(f.Position, position) < (displayRadius + radius_div_2) / (allowance / 2))) + while (caughtObjectContainer.Any(f => Vector2Extensions.Distance(f.Position, position) < (displayRadius + radius_div_2) / (allowance / 2))) { float diff = (displayRadius + radius_div_2) / allowance; @@ -533,17 +533,17 @@ namespace osu.Game.Rulesets.Catch.UI droppedObject.CopyStateFrom(caughtObject); droppedObject.Anchor = Anchor.TopLeft; - droppedObject.Position = caughtFruitContainer.ToSpaceOfOtherDrawable(caughtObject.DrawPosition, droppedObjectTarget); + droppedObject.Position = caughtObjectContainer.ToSpaceOfOtherDrawable(caughtObject.DrawPosition, droppedObjectTarget); return droppedObject; } private void clearPlate(DroppedObjectAnimation animation) { - var caughtObjects = caughtFruitContainer.Children.ToArray(); + var caughtObjects = caughtObjectContainer.Children.ToArray(); var droppedObjects = caughtObjects.Select(getDroppedObject).ToArray(); - caughtFruitContainer.Clear(false); + caughtObjectContainer.Clear(false); droppedObjectTarget.AddRange(droppedObjects); @@ -555,7 +555,7 @@ namespace osu.Game.Rulesets.Catch.UI { var droppedObject = getDroppedObject(caughtObject); - if (!caughtFruitContainer.Remove(caughtObject)) + if (!caughtObjectContainer.Remove(caughtObject)) throw new InvalidOperationException("Can only drop a caught object on the plate"); droppedObjectTarget.Add(droppedObject); @@ -573,7 +573,7 @@ namespace osu.Game.Rulesets.Catch.UI break; case DroppedObjectAnimation.Explode: - var originalX = droppedObjectTarget.ToSpaceOfOtherDrawable(d.DrawPosition, caughtFruitContainer).X * Scale.X; + var originalX = droppedObjectTarget.ToSpaceOfOtherDrawable(d.DrawPosition, caughtObjectContainer).X * Scale.X; d.MoveToY(d.Y - 50, 250, Easing.OutSine).Then().MoveToY(d.Y + 50, 500, Easing.InSine); d.MoveToX(d.X + originalX * 6, 1000); d.FadeOut(750); From 86445e7c2304eb54193c2278ac306e938b0a525e Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 9 Dec 2020 10:36:54 +0900 Subject: [PATCH 15/37] Remove unnecessary copy --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 475fca7f1c..e885881c42 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -540,8 +540,7 @@ namespace osu.Game.Rulesets.Catch.UI private void clearPlate(DroppedObjectAnimation animation) { - var caughtObjects = caughtObjectContainer.Children.ToArray(); - var droppedObjects = caughtObjects.Select(getDroppedObject).ToArray(); + var droppedObjects = caughtObjectContainer.Children.Select(getDroppedObject).ToArray(); caughtObjectContainer.Clear(false); From b52e279702a879eebd333efa734aae9b3b1a2820 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 9 Dec 2020 10:38:11 +0900 Subject: [PATCH 16/37] Reword exception message --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index e885881c42..125f735a60 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -555,7 +555,7 @@ namespace osu.Game.Rulesets.Catch.UI var droppedObject = getDroppedObject(caughtObject); if (!caughtObjectContainer.Remove(caughtObject)) - throw new InvalidOperationException("Can only drop a caught object on the plate"); + throw new InvalidOperationException("Can only drop objects that were previously caught on the plate"); droppedObjectTarget.Add(droppedObject); From da2f3d4473a0583b53cfc76671b48e209f21f321 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 9 Dec 2020 10:40:42 +0900 Subject: [PATCH 17/37] Move classes to separate files --- .../Objects/Drawables/CaughtBanana.cs | 18 +++++++++ .../Objects/Drawables/CaughtDroplet.cs | 20 ++++++++++ .../Objects/Drawables/CaughtFruit.cs | 29 +++++++++++++++ .../Objects/Drawables/CaughtObject.cs | 37 ------------------- .../Objects/Drawables/IHasCatchObjectState.cs | 8 ---- .../Objects/Drawables/IHasFruitState.cs | 15 ++++++++ 6 files changed, 82 insertions(+), 45 deletions(-) create mode 100644 osu.Game.Rulesets.Catch/Objects/Drawables/CaughtBanana.cs create mode 100644 osu.Game.Rulesets.Catch/Objects/Drawables/CaughtDroplet.cs create mode 100644 osu.Game.Rulesets.Catch/Objects/Drawables/CaughtFruit.cs create mode 100644 osu.Game.Rulesets.Catch/Objects/Drawables/IHasFruitState.cs diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtBanana.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtBanana.cs new file mode 100644 index 0000000000..8a91f82437 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtBanana.cs @@ -0,0 +1,18 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Rulesets.Catch.Skinning.Default; + +namespace osu.Game.Rulesets.Catch.Objects.Drawables +{ + /// + /// Represents a caught by the catcher. + /// + public class CaughtBanana : CaughtObject + { + public CaughtBanana() + : base(CatchSkinComponents.Banana, _ => new BananaPiece()) + { + } + } +} diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtDroplet.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtDroplet.cs new file mode 100644 index 0000000000..4a3397feff --- /dev/null +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtDroplet.cs @@ -0,0 +1,20 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Rulesets.Catch.Skinning.Default; + +namespace osu.Game.Rulesets.Catch.Objects.Drawables +{ + /// + /// Represents a caught by the catcher. + /// + public class CaughtDroplet : CaughtObject + { + public override bool StaysOnPlate => false; + + public CaughtDroplet() + : base(CatchSkinComponents.Droplet, _ => new DropletPiece()) + { + } + } +} diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtFruit.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtFruit.cs new file mode 100644 index 0000000000..140b411c88 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtFruit.cs @@ -0,0 +1,29 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Bindables; +using osu.Game.Rulesets.Catch.Skinning.Default; + +namespace osu.Game.Rulesets.Catch.Objects.Drawables +{ + /// + /// Represents a caught by the catcher. + /// + public class CaughtFruit : CaughtObject, IHasFruitState + { + public Bindable VisualRepresentation { get; } = new Bindable(); + + public CaughtFruit() + : base(CatchSkinComponents.Fruit, _ => new FruitPiece()) + { + } + + public override void CopyStateFrom(IHasCatchObjectState objectState) + { + base.CopyStateFrom(objectState); + + var fruitState = (IHasFruitState)objectState; + VisualRepresentation.Value = fruitState.VisualRepresentation.Value; + } + } +} diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs index 87319a4498..e597d1a4cd 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs @@ -5,7 +5,6 @@ using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Game.Rulesets.Catch.Skinning.Default; using osu.Game.Skinning; using osuTK; using osuTK.Graphics; @@ -61,40 +60,4 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables base.FreeAfterUse(); } } - - public class CaughtFruit : CaughtObject, IHasFruitState - { - public Bindable VisualRepresentation { get; } = new Bindable(); - - public CaughtFruit() - : base(CatchSkinComponents.Fruit, _ => new FruitPiece()) - { - } - - public override void CopyStateFrom(IHasCatchObjectState objectState) - { - base.CopyStateFrom(objectState); - - var fruitState = (IHasFruitState)objectState; - VisualRepresentation.Value = fruitState.VisualRepresentation.Value; - } - } - - public class CaughtBanana : CaughtObject - { - public CaughtBanana() - : base(CatchSkinComponents.Banana, _ => new BananaPiece()) - { - } - } - - public class CaughtDroplet : CaughtObject - { - public override bool StaysOnPlate => false; - - public CaughtDroplet() - : base(CatchSkinComponents.Droplet, _ => new DropletPiece()) - { - } - } } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs index 0a75fb2224..55ca502877 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs @@ -19,12 +19,4 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables float Rotation { get; } Vector2 Scale { get; } } - - /// - /// Provides a visual state of a . - /// - public interface IHasFruitState : IHasCatchObjectState - { - Bindable VisualRepresentation { get; } - } } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/IHasFruitState.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasFruitState.cs new file mode 100644 index 0000000000..2d4de543c3 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasFruitState.cs @@ -0,0 +1,15 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Bindables; + +namespace osu.Game.Rulesets.Catch.Objects.Drawables +{ + /// + /// Provides a visual state of a . + /// + public interface IHasFruitState : IHasCatchObjectState + { + Bindable VisualRepresentation { get; } + } +} From 775c4bad973c3f9afba809851b0b63add97929a1 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 9 Dec 2020 10:47:04 +0900 Subject: [PATCH 18/37] Remove unneeded lifetime assignment --- osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs index e597d1a4cd..fb83b73212 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs @@ -52,10 +52,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables protected override void FreeAfterUse() { ClearTransforms(); - Alpha = 1; - LifetimeStart = double.MinValue; - LifetimeEnd = double.MaxValue; base.FreeAfterUse(); } From a8e2f35b622a5797837115eeef7f2b96fd4a9169 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 9 Dec 2020 10:50:35 +0900 Subject: [PATCH 19/37] Remove unneeded check of caught object removal The logic was public but now it is private so the condition is ensured by the caller --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 125f735a60..64dacd09e6 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -554,8 +554,7 @@ namespace osu.Game.Rulesets.Catch.UI { var droppedObject = getDroppedObject(caughtObject); - if (!caughtObjectContainer.Remove(caughtObject)) - throw new InvalidOperationException("Can only drop objects that were previously caught on the plate"); + caughtObjectContainer.Remove(caughtObject); droppedObjectTarget.Add(droppedObject); From cc996ec7fcbe360b451ec786c81f6a243703af7d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 10 Dec 2020 16:32:14 +0900 Subject: [PATCH 20/37] Ensure player is consumed at the point of scheduled push running the first time --- osu.Game/Screens/Play/PlayerLoader.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 42074ac241..c26195b09c 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -274,6 +274,13 @@ namespace osu.Game.Screens.Play } } + private Player consumePlayer() + { + var consumed = player; + player = null; + return consumed; + } + private void prepareNewPlayer() { if (!this.IsCurrentScreen()) @@ -331,6 +338,8 @@ namespace osu.Game.Screens.Play scheduledPushPlayer = Scheduler.AddDelayed(() => { contentOut(); + // ensure that once we have reached this "point of no return", readyForPush will be false for all future checks (until a new player instance is prepared). + var consumedPlayer = consumePlayer(); TransformSequence pushSequence = this.Delay(250); @@ -362,8 +371,6 @@ namespace osu.Game.Screens.Play // Note that this may change if the player we load requested a re-run. ValidForResume = false; - if (player.LoadedBeatmapSuccessfully) - this.Push(player); else this.Exit(); }); @@ -373,6 +380,8 @@ namespace osu.Game.Screens.Play { Schedule(pushWhenLoaded); } + if (consumedPlayer.LoadedBeatmapSuccessfully) + this.Push(consumedPlayer); } private void cancelLoad() From 491ab7405942009e17012d326aa90ae88fea46ff Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 10 Dec 2020 16:33:28 +0900 Subject: [PATCH 21/37] Schedule pushWhenLoaded once ever Previously it was being scheduled another time each OnResume, resulting in more and more calls as a user retries the same beatmap multiple times. To simplify things I've decided to just schedule once ever. This means that on resuming there's no 400ms delay any more, but in testing this isn't really an issue (load time is still high enough that it will never really be below that anyway). Even if gameplay was to load faster, the animation should gracefully proceed. --- osu.Game/Screens/Play/PlayerLoader.cs | 95 +++++++++++++-------------- 1 file changed, 46 insertions(+), 49 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index c26195b09c..d9c7afdc3c 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -179,7 +179,10 @@ namespace osu.Game.Screens.Play contentIn(); MetadataInfo.Delay(750).FadeIn(500); - this.Delay(1800).Schedule(pushWhenLoaded); + + // after an initial delay, start the debounced load check. + // this will continue to execute even after resuming back on restart. + Scheduler.Add(new ScheduledDelegate(pushWhenLoaded, 1800, 0)); showMuteWarningIfNeeded(); } @@ -189,8 +192,6 @@ namespace osu.Game.Screens.Play base.OnResuming(last); contentIn(); - - this.Delay(400).Schedule(pushWhenLoaded); } public override void OnSuspending(IScreen next) @@ -322,66 +323,62 @@ namespace osu.Game.Screens.Play { if (!this.IsCurrentScreen()) return; - try + if (!readyForPush) { - if (!readyForPush) - { - // as the pushDebounce below has a delay, we need to keep checking and cancel a future debounce - // if we become unready for push during the delay. - cancelLoad(); - return; - } + // as the pushDebounce below has a delay, we need to keep checking and cancel a future debounce + // if we become unready for push during the delay. + cancelLoad(); + return; + } - if (scheduledPushPlayer != null) - return; + // if a push has already been scheduled, no further action is required. + // this value is reset via cancelLoad() to allow a second usage of the same PlayerLoader screen. + if (scheduledPushPlayer != null) + return; - scheduledPushPlayer = Scheduler.AddDelayed(() => - { - contentOut(); + scheduledPushPlayer = Scheduler.AddDelayed(() => + { // ensure that once we have reached this "point of no return", readyForPush will be false for all future checks (until a new player instance is prepared). var consumedPlayer = consumePlayer(); - TransformSequence pushSequence = this.Delay(250); + contentOut(); - // only show if the warning was created (i.e. the beatmap needs it) - // and this is not a restart of the map (the warning expires after first load). - if (epilepsyWarning?.IsAlive == true) - { - const double epilepsy_display_length = 3000; + TransformSequence pushSequence = this.Delay(250); - pushSequence - .Schedule(() => epilepsyWarning.State.Value = Visibility.Visible) - .TransformBindableTo(volumeAdjustment, 0.25, EpilepsyWarning.FADE_DURATION, Easing.OutQuint) - .Delay(epilepsy_display_length) - .Schedule(() => - { - epilepsyWarning.Hide(); - epilepsyWarning.Expire(); - }) - .Delay(EpilepsyWarning.FADE_DURATION); - } + // only show if the warning was created (i.e. the beatmap needs it) + // and this is not a restart of the map (the warning expires after first load). + if (epilepsyWarning?.IsAlive == true) + { + const double epilepsy_display_length = 3000; - pushSequence.Schedule(() => - { - if (!this.IsCurrentScreen()) return; + pushSequence + .Schedule(() => epilepsyWarning.State.Value = Visibility.Visible) + .TransformBindableTo(volumeAdjustment, 0.25, EpilepsyWarning.FADE_DURATION, Easing.OutQuint) + .Delay(epilepsy_display_length) + .Schedule(() => + { + epilepsyWarning.Hide(); + epilepsyWarning.Expire(); + }) + .Delay(EpilepsyWarning.FADE_DURATION); + } - LoadTask = null; + pushSequence.Schedule(() => + { + if (!this.IsCurrentScreen()) return; - // By default, we want to load the player and never be returned to. - // Note that this may change if the player we load requested a re-run. - ValidForResume = false; + LoadTask = null; + + // By default, we want to load the player and never be returned to. + // Note that this may change if the player we load requested a re-run. + ValidForResume = false; - else - this.Exit(); - }); - }, 500); - } - finally - { - Schedule(pushWhenLoaded); - } if (consumedPlayer.LoadedBeatmapSuccessfully) this.Push(consumedPlayer); + else + this.Exit(); + }); + }, 500); } private void cancelLoad() From 67dd7be71a549822b47562be9a4cfc106b338a12 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 10 Dec 2020 16:34:58 +0900 Subject: [PATCH 22/37] Move cancelLoad call to OnResuming This has no real effect; it just feels more readable to me. --- osu.Game/Screens/Play/PlayerLoader.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index d9c7afdc3c..f3575b01b4 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -191,6 +191,7 @@ namespace osu.Game.Screens.Play { base.OnResuming(last); + cancelLoad(); contentIn(); } @@ -198,8 +199,6 @@ namespace osu.Game.Screens.Play { base.OnSuspending(next); - cancelLoad(); - BackgroundBrightnessReduction = false; // we're moving to player, so a period of silence is upcoming. From 437c0506cec8ee2b2ba1ee3ed71bb57ea50dd04b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 10 Dec 2020 16:56:56 +0900 Subject: [PATCH 23/37] Refactor to allow for special disposal handling to still work --- osu.Game/Screens/Play/PlayerLoader.cs | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index f3575b01b4..729119fa36 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Diagnostics; using System.Linq; using System.Threading.Tasks; using JetBrains.Annotations; @@ -71,8 +72,9 @@ namespace osu.Game.Screens.Play } private bool readyForPush => + !playerConsumed // don't push unless the player is completely loaded - player?.LoadState == LoadState.Ready + && player?.LoadState == LoadState.Ready // don't push if the user is hovering one of the panes, unless they are idle. && (IsHovered || idleTracker.IsIdle.Value) // don't push if the user is dragging a slider or otherwise. @@ -84,6 +86,11 @@ namespace osu.Game.Screens.Play private Player player; + /// + /// Whether the curent player instance has been consumed via . + /// + private bool playerConsumed; + private LogoTrackingContainer content; private bool hideOverlays; @@ -191,7 +198,11 @@ namespace osu.Game.Screens.Play { base.OnResuming(last); + // prepare for a retry. + player = null; + playerConsumed = false; cancelLoad(); + contentIn(); } @@ -276,9 +287,10 @@ namespace osu.Game.Screens.Play private Player consumePlayer() { - var consumed = player; - player = null; - return consumed; + Debug.Assert(!playerConsumed); + + playerConsumed = true; + return player; } private void prepareNewPlayer() @@ -395,7 +407,7 @@ namespace osu.Game.Screens.Play if (isDisposing) { // if the player never got pushed, we should explicitly dispose it. - DisposalTask = LoadTask?.ContinueWith(_ => player.Dispose()); + DisposalTask = LoadTask?.ContinueWith(_ => player?.Dispose()); } } From 679a550d833c9d6e12160c4a77ac81fa2662cca5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 10 Dec 2020 17:42:28 +0900 Subject: [PATCH 24/37] Fix single threaded seeking not working due to unnecessary seek call --- osu.Game/Screens/Play/GameplayClockContainer.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 2c83161614..5d11cdf21d 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -154,13 +154,16 @@ namespace osu.Game.Screens.Play public void Start() { - // Seeking the decoupled clock to its current time ensures that its source clock will be seeked to the same time - // This accounts for the audio clock source potentially taking time to enter a completely stopped state - Seek(GameplayClock.CurrentTime); - adjustableClock.Start(); - IsPaused.Value = false; + if (!adjustableClock.IsRunning) + { + // Seeking the decoupled clock to its current time ensures that its source clock will be seeked to the same time + // This accounts for the audio clock source potentially taking time to enter a completely stopped state + Seek(GameplayClock.CurrentTime); this.TransformBindableTo(pauseFreqAdjust, 1, 200, Easing.In); + adjustableClock.Start(); + } + IsPaused.Value = false; } /// From 01bd765384e9f9d2b61118fcab7e75132a7c3cf0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 10 Dec 2020 17:42:47 +0900 Subject: [PATCH 25/37] Simplify pause handling by moving transform logic to bindable change event --- osu.Game/Screens/Play/GameplayClockContainer.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 5d11cdf21d..0bbdc980db 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -95,6 +95,16 @@ namespace osu.Game.Screens.Play localGameplayClock = new LocalGameplayClock(userOffsetClock); GameplayClock.IsPaused.BindTo(IsPaused); + + IsPaused.BindValueChanged(onPaused); + } + + private void onPaused(ValueChangedEvent isPaused) + { + if (isPaused.NewValue) + this.TransformBindableTo(pauseFreqAdjust, 0, 200, Easing.Out).OnComplete(_ => adjustableClock.Stop()); + else + this.TransformBindableTo(pauseFreqAdjust, 1, 200, Easing.In); } private double totalOffset => userOffsetClock.Offset + platformOffsetClock.Offset; @@ -160,9 +170,9 @@ namespace osu.Game.Screens.Play // This accounts for the audio clock source potentially taking time to enter a completely stopped state Seek(GameplayClock.CurrentTime); - this.TransformBindableTo(pauseFreqAdjust, 1, 200, Easing.In); adjustableClock.Start(); } + IsPaused.Value = false; } @@ -202,8 +212,6 @@ namespace osu.Game.Screens.Play public void Stop() { - this.TransformBindableTo(pauseFreqAdjust, 0, 200, Easing.Out).OnComplete(_ => adjustableClock.Stop()); - IsPaused.Value = true; } From e097b6e61c2796f0c5071704d90b7119134fca0c Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 10 Dec 2020 19:42:01 +0900 Subject: [PATCH 26/37] Add ScalingContainer back Don't want to set DHO.Scale or DHO.Rotation because because DHO may be transformed by mods. DHO.Size is also assigned for drawable visualizer --- .../Objects/Drawables/CaughtObject.cs | 4 +++- .../Objects/Drawables/DrawableBanana.cs | 14 ++++++------- .../Objects/Drawables/DrawableDroplet.cs | 6 +++--- .../Objects/Drawables/DrawableFruit.cs | 6 +++--- .../DrawablePalpableCatchHitObject.cs | 20 ++++++++++++++++++- .../Objects/Drawables/IHasCatchObjectState.cs | 3 +-- 6 files changed, 36 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs index fb83b73212..22fb338229 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs @@ -21,6 +21,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables public Bindable AccentColour { get; } = new Bindable(); public Bindable HyperDash { get; } = new Bindable(); + float IHasCatchObjectState.Scale => Scale.X; + /// /// Whether this hit object should stay on the catcher plate when the object is caught by the catcher. /// @@ -43,7 +45,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables public virtual void CopyStateFrom(IHasCatchObjectState objectState) { HitObject = objectState.HitObject; - Scale = objectState.Scale; + Scale = new Vector2(objectState.Scale); Rotation = objectState.Rotation; AccentColour.Value = objectState.AccentColour.Value; HyperDash.Value = objectState.HyperDash.Value; diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs index 4bc5da2396..c1b41a7afc 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs @@ -24,9 +24,9 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables [BackgroundDependencyLoader] private void load() { - AddInternal(new SkinnableDrawable( + ScalingContainer.Child = new SkinnableDrawable( new CatchSkinComponent(CatchSkinComponents.Banana), - _ => new BananaPiece())); + _ => new BananaPiece()); } protected override void LoadComplete() @@ -44,12 +44,12 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables const float end_scale = 0.6f; const float random_scale_range = 1.6f; - this.ScaleTo(HitObject.Scale * (end_scale + random_scale_range * RandomSingle(3))) - .Then().ScaleTo(HitObject.Scale * end_scale, HitObject.TimePreempt); + ScalingContainer.ScaleTo(HitObject.Scale * (end_scale + random_scale_range * RandomSingle(3))) + .Then().ScaleTo(HitObject.Scale * end_scale, HitObject.TimePreempt); - this.RotateTo(getRandomAngle(1)) - .Then() - .RotateTo(getRandomAngle(2), HitObject.TimePreempt); + ScalingContainer.RotateTo(getRandomAngle(1)) + .Then() + .RotateTo(getRandomAngle(2), HitObject.TimePreempt); float getRandomAngle(int series) => 180 * (RandomSingle(series) * 2 - 1); } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs index c5f0bb8b18..2dce9507a5 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableDroplet.cs @@ -24,9 +24,9 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables [BackgroundDependencyLoader] private void load() { - AddInternal(new SkinnableDrawable( + ScalingContainer.Child = new SkinnableDrawable( new CatchSkinComponent(CatchSkinComponents.Droplet), - _ => new DropletPiece())); + _ => new DropletPiece()); } protected override void UpdateInitialTransforms() @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables float startRotation = RandomSingle(1) * 20; double duration = HitObject.TimePreempt + 2000; - this.RotateTo(startRotation).RotateTo(startRotation + 720, duration); + ScalingContainer.RotateTo(startRotation).RotateTo(startRotation + 720, duration); } } } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs index 57f27d01b8..0b89c46480 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableFruit.cs @@ -32,16 +32,16 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables VisualRepresentation.Value = (FruitVisualRepresentation)(change.NewValue % 4); }, true); - AddInternal(new SkinnableDrawable( + ScalingContainer.Child = new SkinnableDrawable( new CatchSkinComponent(CatchSkinComponents.Fruit), - _ => new FruitPiece())); + _ => new FruitPiece()); } protected override void UpdateInitialTransforms() { base.UpdateInitialTransforms(); - this.RotateTo((RandomSingle(1) - 0.5f) * 40); + ScalingContainer.RotateTo((RandomSingle(1) - 0.5f) * 40); } } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs index aea9d2c082..9ddf56a9cd 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs @@ -5,6 +5,7 @@ using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osuTK; using osuTK.Graphics; @@ -30,11 +31,27 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables public float DisplayRadius => CatchHitObject.OBJECT_RADIUS * HitObject.Scale * ScaleFactor; + /// + /// The container internal transforms (such as scaling based on the circle size) are applied to. + /// + protected readonly Container ScalingContainer; + + float IHasCatchObjectState.Scale => HitObject.Scale * ScaleFactor; + + float IHasCatchObjectState.Rotation => ScalingContainer.Rotation; + protected DrawablePalpableCatchHitObject([CanBeNull] CatchHitObject h) : base(h) { Origin = Anchor.Centre; Size = new Vector2(CatchHitObject.OBJECT_RADIUS * 2); + + AddInternal(ScalingContainer = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(CatchHitObject.OBJECT_RADIUS * 2) + }); } [BackgroundDependencyLoader] @@ -47,7 +64,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables ScaleBindable.BindValueChanged(scale => { - Scale = new Vector2(scale.NewValue * ScaleFactor); + ScalingContainer.Scale = new Vector2(scale.NewValue * ScaleFactor); + Size = ScalingContainer.Size * ScalingContainer.Scale; }, true); IndexInBeatmap.BindValueChanged(_ => UpdateComboColour()); diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs index 55ca502877..fac0249985 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Bindables; -using osuTK; using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.Objects.Drawables @@ -17,6 +16,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables Bindable HyperDash { get; } float Rotation { get; } - Vector2 Scale { get; } + float Scale { get; } } } From 2634c6b8d9dc18f34ae7eb81dd114d36a71adb21 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 10 Dec 2020 20:43:01 +0900 Subject: [PATCH 27/37] Combine DisplayRadius and Scale to DisplaySize --- .../TestSceneFruitRandomness.cs | 23 ++++++++++--------- .../Objects/Drawables/CaughtObject.cs | 8 ++++--- .../DrawablePalpableCatchHitObject.cs | 8 +++---- .../Objects/Drawables/IHasCatchObjectState.cs | 5 ++-- osu.Game.Rulesets.Catch/UI/Catcher.cs | 2 +- 5 files changed, 24 insertions(+), 22 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneFruitRandomness.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneFruitRandomness.cs index e1d817b314..c888dc0a65 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneFruitRandomness.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneFruitRandomness.cs @@ -5,6 +5,7 @@ using NUnit.Framework; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects.Drawables; using osu.Game.Tests.Visual; +using osuTK; using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.Tests @@ -37,16 +38,16 @@ namespace osu.Game.Rulesets.Catch.Tests float fruitRotation = 0; float bananaRotation = 0; - float bananaScale = 0; + Vector2 bananaSize = new Vector2(); Color4 bananaColour = new Color4(); AddStep("Initialize start time", () => { drawableFruit.HitObject.StartTime = drawableBanana.HitObject.StartTime = initial_start_time; - fruitRotation = drawableFruit.Rotation; - bananaRotation = drawableBanana.Rotation; - bananaScale = drawableBanana.Scale.X; + fruitRotation = drawableFruit.DisplayRotation; + bananaRotation = drawableBanana.DisplayRotation; + bananaSize = drawableBanana.DisplaySize; bananaColour = drawableBanana.AccentColour.Value; }); @@ -55,9 +56,9 @@ namespace osu.Game.Rulesets.Catch.Tests drawableFruit.HitObject.StartTime = drawableBanana.HitObject.StartTime = another_start_time; }); - AddAssert("fruit rotation is changed", () => drawableFruit.Rotation != fruitRotation); - AddAssert("banana rotation is changed", () => drawableBanana.Rotation != bananaRotation); - AddAssert("banana scale is changed", () => drawableBanana.Scale.X != bananaScale); + AddAssert("fruit rotation is changed", () => drawableFruit.DisplayRotation != fruitRotation); + AddAssert("banana rotation is changed", () => drawableBanana.DisplayRotation != bananaRotation); + AddAssert("banana size is changed", () => drawableBanana.DisplaySize != bananaSize); AddAssert("banana colour is changed", () => drawableBanana.AccentColour.Value != bananaColour); AddStep("reset start time", () => @@ -65,10 +66,10 @@ namespace osu.Game.Rulesets.Catch.Tests drawableFruit.HitObject.StartTime = drawableBanana.HitObject.StartTime = initial_start_time; }); - AddAssert("rotation and scale restored", () => - drawableFruit.Rotation == fruitRotation && - drawableBanana.Rotation == bananaRotation && - drawableBanana.Scale.X == bananaScale && + AddAssert("rotation and size restored", () => + drawableFruit.DisplayRotation == fruitRotation && + drawableBanana.DisplayRotation == bananaRotation && + drawableBanana.DisplaySize == bananaSize && drawableBanana.AccentColour.Value == bananaColour); } } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs index 22fb338229..524505d588 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/CaughtObject.cs @@ -21,7 +21,9 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables public Bindable AccentColour { get; } = new Bindable(); public Bindable HyperDash { get; } = new Bindable(); - float IHasCatchObjectState.Scale => Scale.X; + public Vector2 DisplaySize => Size * Scale; + + public float DisplayRotation => Rotation; /// /// Whether this hit object should stay on the catcher plate when the object is caught by the catcher. @@ -45,8 +47,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables public virtual void CopyStateFrom(IHasCatchObjectState objectState) { HitObject = objectState.HitObject; - Scale = new Vector2(objectState.Scale); - Rotation = objectState.Rotation; + Scale = Vector2.Divide(objectState.DisplaySize, Size); + Rotation = objectState.DisplayRotation; AccentColour.Value = objectState.AccentColour.Value; HyperDash.Value = objectState.HyperDash.Value; } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs index 9ddf56a9cd..7df06bd92d 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs @@ -29,16 +29,14 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables /// protected virtual float ScaleFactor => 1; - public float DisplayRadius => CatchHitObject.OBJECT_RADIUS * HitObject.Scale * ScaleFactor; - /// /// The container internal transforms (such as scaling based on the circle size) are applied to. /// protected readonly Container ScalingContainer; - float IHasCatchObjectState.Scale => HitObject.Scale * ScaleFactor; + public Vector2 DisplaySize => ScalingContainer.Size * ScalingContainer.Scale; - float IHasCatchObjectState.Rotation => ScalingContainer.Rotation; + public float DisplayRotation => ScalingContainer.Rotation; protected DrawablePalpableCatchHitObject([CanBeNull] CatchHitObject h) : base(h) @@ -65,7 +63,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables ScaleBindable.BindValueChanged(scale => { ScalingContainer.Scale = new Vector2(scale.NewValue * ScaleFactor); - Size = ScalingContainer.Size * ScalingContainer.Scale; + Size = DisplaySize; }, true); IndexInBeatmap.BindValueChanged(_ => UpdateComboColour()); diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs index fac0249985..d68e32aca9 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Bindables; +using osuTK; using osuTK.Graphics; namespace osu.Game.Rulesets.Catch.Objects.Drawables @@ -15,7 +16,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables Bindable AccentColour { get; } Bindable HyperDash { get; } - float Rotation { get; } - float Scale { get; } + Vector2 DisplaySize { get; } + float DisplayRotation { get; } } } diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 64dacd09e6..f164c2655a 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -235,7 +235,7 @@ namespace osu.Game.Rulesets.Catch.UI if (result.IsHit) { - var positionInStack = computePositionInStack(new Vector2(palpableObject.X - X, 0), palpableObject.DisplayRadius); + var positionInStack = computePositionInStack(new Vector2(palpableObject.X - X, 0), palpableObject.DisplaySize.X / 2); placeCaughtObject(palpableObject, positionInStack); From 2dd591125673b4b4d6b130204995bfe41cf54de9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 11 Dec 2020 14:44:01 +0900 Subject: [PATCH 28/37] Rename method to better match purpose --- osu.Game/Screens/Play/GameplayClockContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 0bbdc980db..0248432917 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -96,10 +96,10 @@ namespace osu.Game.Screens.Play GameplayClock.IsPaused.BindTo(IsPaused); - IsPaused.BindValueChanged(onPaused); + IsPaused.BindValueChanged(onPauseChanged); } - private void onPaused(ValueChangedEvent isPaused) + private void onPauseChanged(ValueChangedEvent isPaused) { if (isPaused.NewValue) this.TransformBindableTo(pauseFreqAdjust, 0, 200, Easing.Out).OnComplete(_ => adjustableClock.Stop()); From c0d20d8ce430a2ee7257c7ac8cf34eeefd54489f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 11 Dec 2020 16:43:00 +0900 Subject: [PATCH 29/37] Add some spacing to interface class --- .../Objects/Drawables/IHasCatchObjectState.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs index d68e32aca9..81b61f0959 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/IHasCatchObjectState.cs @@ -13,10 +13,13 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables public interface IHasCatchObjectState { PalpableCatchHitObject HitObject { get; } + Bindable AccentColour { get; } + Bindable HyperDash { get; } Vector2 DisplaySize { get; } + float DisplayRotation { get; } } } From a35060ea7a3c7c825b826b4ad7a65a898249d8bb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 11 Dec 2020 17:56:00 +0900 Subject: [PATCH 30/37] Add a simple cache-busting query string to online.db retrieval As we are finally pushing updates for this database, this adds a minimum level of guarantee that a client will request a new version (without having to worry about multiple levels of server-side caching). --- osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs b/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs index c4563d5844..e90ccbb805 100644 --- a/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs +++ b/osu.Game/Beatmaps/BeatmapManager_BeatmapOnlineLookupQueue.cs @@ -104,7 +104,7 @@ namespace osu.Game.Beatmaps string cacheFilePath = storage.GetFullPath(cache_database_name); string compressedCacheFilePath = $"{cacheFilePath}.bz2"; - cacheDownloadRequest = new FileWebRequest(compressedCacheFilePath, $"https://assets.ppy.sh/client-resources/{cache_database_name}.bz2"); + cacheDownloadRequest = new FileWebRequest(compressedCacheFilePath, $"https://assets.ppy.sh/client-resources/{cache_database_name}.bz2?{DateTimeOffset.UtcNow:yyyyMMdd}"); cacheDownloadRequest.Failed += ex => { From b5f6baf3412128bc142d966affffc433abfbe490 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 11 Dec 2020 18:03:48 +0900 Subject: [PATCH 31/37] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 9a3d42d6b7..e0bf1867b9 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 9d37ceee6c..bd8a7c73b2 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -26,7 +26,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index ab03393836..c5e7c45eda 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -88,7 +88,7 @@ - + From f20c5a2bda5f3d4a199af516e22b8caf1e97526a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 12 Dec 2020 15:29:21 +0900 Subject: [PATCH 32/37] Update framework (again) --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index e0bf1867b9..eaedcb7bc3 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index bd8a7c73b2..b4c7dca12f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -26,7 +26,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index c5e7c45eda..7542aded86 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -88,7 +88,7 @@ - + From 8292c746ea82e35412f717fe8d6db6921661e54e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 12 Dec 2020 22:42:40 +0100 Subject: [PATCH 33/37] Leverage hitobject model for strong hit instead of creating own --- osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs index e4c0766844..c1b422452e 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Utils; @@ -9,7 +10,6 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Judgements; using osu.Game.Rulesets.Taiko.Objects; @@ -126,7 +126,7 @@ namespace osu.Game.Rulesets.Taiko.Tests var cpi = new ControlPointInfo(); cpi.Add(0, new EffectControlPoint { KiaiMode = kiai }); - Hit hit = new Hit(); + Hit hit = new Hit { IsStrong = true }; hit.ApplyDefaults(cpi, new BeatmapDifficulty()); var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Ok ? -0.1f : -0.05f, hitResult == HitResult.Ok ? 0.1f : 0.05f) }; @@ -134,7 +134,7 @@ namespace osu.Game.Rulesets.Taiko.Tests DrawableRuleset.Playfield.Add(h); ((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h, new JudgementResult(new HitObject(), new TaikoJudgement()) { Type = hitResult }); - ((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(new TestStrongNestedHit(h), new JudgementResult(new HitObject(), new TaikoStrongJudgement()) { Type = HitResult.Great }); + ((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h.NestedHitObjects.Single(), new JudgementResult(new HitObject(), new TaikoStrongJudgement()) { Type = HitResult.Great }); } private void addMissJudgement() @@ -210,15 +210,5 @@ namespace osu.Game.Rulesets.Taiko.Tests DrawableRuleset.Playfield.Add(new DrawableHit(h)); } - - private class TestStrongNestedHit : DrawableStrongNestedHit - { - public TestStrongNestedHit(DrawableHitObject mainObject) - : base(new StrongHitObject { StartTime = mainObject.HitObject.StartTime }, mainObject) - { - } - - public override bool OnPressed(TaikoAction action) => false; - } } } From 34e7a36b382ba88d4e2ae74fceb8ad7833133090 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 12 Dec 2020 22:48:22 +0100 Subject: [PATCH 34/37] Fix kiai hit steps not working correctly --- osu.Game.Rulesets.Taiko.Tests/DrawableTestHit.cs | 7 +++++-- osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs | 14 ++------------ 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/DrawableTestHit.cs b/osu.Game.Rulesets.Taiko.Tests/DrawableTestHit.cs index fb0917341e..f048cad18c 100644 --- a/osu.Game.Rulesets.Taiko.Tests/DrawableTestHit.cs +++ b/osu.Game.Rulesets.Taiko.Tests/DrawableTestHit.cs @@ -13,12 +13,15 @@ namespace osu.Game.Rulesets.Taiko.Tests { public readonly HitResult Type; - public DrawableTestHit(Hit hit, HitResult type = HitResult.Great) + public DrawableTestHit(Hit hit, HitResult type = HitResult.Great, bool kiai = false) : base(hit) { Type = type; - HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + var controlPoints = new ControlPointInfo(); + controlPoints.Add(0, new EffectControlPoint { KiaiMode = kiai }); + + HitObject.ApplyDefaults(controlPoints, new BeatmapDifficulty()); } protected override void UpdateInitialTransforms() diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs index c1b422452e..14a127ac82 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs @@ -106,13 +106,8 @@ namespace osu.Game.Rulesets.Taiko.Tests { HitResult hitResult = RNG.Next(2) == 0 ? HitResult.Ok : HitResult.Great; - var cpi = new ControlPointInfo(); - cpi.Add(0, new EffectControlPoint { KiaiMode = kiai }); - Hit hit = new Hit(); - hit.ApplyDefaults(cpi, new BeatmapDifficulty()); - - var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Ok ? -0.1f : -0.05f, hitResult == HitResult.Ok ? 0.1f : 0.05f) }; + var h = new DrawableTestHit(hit, kiai: kiai) { X = RNG.NextSingle(hitResult == HitResult.Ok ? -0.1f : -0.05f, hitResult == HitResult.Ok ? 0.1f : 0.05f) }; DrawableRuleset.Playfield.Add(h); @@ -123,13 +118,8 @@ namespace osu.Game.Rulesets.Taiko.Tests { HitResult hitResult = RNG.Next(2) == 0 ? HitResult.Ok : HitResult.Great; - var cpi = new ControlPointInfo(); - cpi.Add(0, new EffectControlPoint { KiaiMode = kiai }); - Hit hit = new Hit { IsStrong = true }; - hit.ApplyDefaults(cpi, new BeatmapDifficulty()); - - var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Ok ? -0.1f : -0.05f, hitResult == HitResult.Ok ? 0.1f : 0.05f) }; + var h = new DrawableTestHit(hit, kiai: kiai) { X = RNG.NextSingle(hitResult == HitResult.Ok ? -0.1f : -0.05f, hitResult == HitResult.Ok ? 0.1f : 0.05f) }; DrawableRuleset.Playfield.Add(h); From 76193e221754455ac9638f7974437c2259b9fc5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 12 Dec 2020 22:52:01 +0100 Subject: [PATCH 35/37] Fix miss step not working --- osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs index 14a127ac82..d257712553 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs @@ -130,8 +130,11 @@ namespace osu.Game.Rulesets.Taiko.Tests private void addMissJudgement() { DrawableTestHit h; - DrawableRuleset.Playfield.Add(h = new DrawableTestHit(new Hit(), HitResult.Miss)); - ((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h, new JudgementResult(new HitObject(), new TaikoJudgement()) { Type = HitResult.Miss }); + DrawableRuleset.Playfield.Add(h = new DrawableTestHit(new Hit { StartTime = DrawableRuleset.Playfield.Time.Current }, HitResult.Miss) + { + Alpha = 0 + }); + ((TaikoPlayfield)DrawableRuleset.Playfield).OnNewResult(h, new JudgementResult(h.HitObject, new TaikoJudgement()) { Type = HitResult.Miss }); } private void addBarLine(bool major, double delay = scroll_time) From 43c0e2191deaad07687704619d69b2c10056c0a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 12 Dec 2020 23:50:12 +0100 Subject: [PATCH 36/37] Apply local fix for strong/colour not being applied correctly --- .../TestSceneHits.cs | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs index d257712553..1d51020a6f 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs @@ -2,10 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Utils; +using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Judgements; @@ -118,7 +120,11 @@ namespace osu.Game.Rulesets.Taiko.Tests { HitResult hitResult = RNG.Next(2) == 0 ? HitResult.Ok : HitResult.Great; - Hit hit = new Hit { IsStrong = true }; + Hit hit = new Hit + { + IsStrong = true, + Samples = createSamples(strong: true) + }; var h = new DrawableTestHit(hit, kiai: kiai) { X = RNG.NextSingle(hitResult == HitResult.Ok ? -0.1f : -0.05f, hitResult == HitResult.Ok ? 0.1f : 0.05f) }; DrawableRuleset.Playfield.Add(h); @@ -166,6 +172,7 @@ namespace osu.Game.Rulesets.Taiko.Tests { StartTime = DrawableRuleset.Playfield.Time.Current + scroll_time, IsStrong = strong, + Samples = createSamples(strong: strong), Duration = duration, TickRate = 8, }; @@ -183,7 +190,8 @@ namespace osu.Game.Rulesets.Taiko.Tests Hit h = new Hit { StartTime = DrawableRuleset.Playfield.Time.Current + scroll_time, - IsStrong = strong + IsStrong = strong, + Samples = createSamples(HitType.Centre, strong) }; h.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); @@ -196,12 +204,27 @@ namespace osu.Game.Rulesets.Taiko.Tests Hit h = new Hit { StartTime = DrawableRuleset.Playfield.Time.Current + scroll_time, - IsStrong = strong + IsStrong = strong, + Samples = createSamples(HitType.Rim, strong) }; h.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); DrawableRuleset.Playfield.Add(new DrawableHit(h)); } + + // TODO: can be removed if a better way of handling colour/strong type and samples is developed + private IList createSamples(HitType? hitType = null, bool strong = false) + { + var samples = new List(); + + if (hitType == HitType.Rim) + samples.Add(new HitSampleInfo(HitSampleInfo.HIT_CLAP)); + + if (strong) + samples.Add(new HitSampleInfo(HitSampleInfo.HIT_FINISH)); + + return samples; + } } } From 3a3b32186ee62acc40a66f9047a777d4d2b72b2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 13 Dec 2020 00:00:37 +0100 Subject: [PATCH 37/37] Make height test steps work better --- .../DrawableTaikoRulesetTestScene.cs | 8 +++++--- osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/DrawableTaikoRulesetTestScene.cs b/osu.Game.Rulesets.Taiko.Tests/DrawableTaikoRulesetTestScene.cs index d1c4a1c56d..783636a62d 100644 --- a/osu.Game.Rulesets.Taiko.Tests/DrawableTaikoRulesetTestScene.cs +++ b/osu.Game.Rulesets.Taiko.Tests/DrawableTaikoRulesetTestScene.cs @@ -16,6 +16,8 @@ namespace osu.Game.Rulesets.Taiko.Tests { public abstract class DrawableTaikoRulesetTestScene : OsuTestScene { + protected const int DEFAULT_PLAYFIELD_CONTAINER_HEIGHT = 768; + protected DrawableTaikoRuleset DrawableRuleset { get; private set; } protected Container PlayfieldContainer { get; private set; } @@ -44,10 +46,10 @@ namespace osu.Game.Rulesets.Taiko.Tests Add(PlayfieldContainer = new Container { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, RelativeSizeAxes = Axes.X, - Height = 768, + Height = DEFAULT_PLAYFIELD_CONTAINER_HEIGHT, Children = new[] { DrawableRuleset = new DrawableTaikoRuleset(new TaikoRuleset(), beatmap.GetPlayableBeatmap(new TaikoRuleset().RulesetInfo)) } }); } diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs index 1d51020a6f..c3fa03d404 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneHits.cs @@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.Taiko.Tests break; case 6: - PlayfieldContainer.Delay(delay).ResizeTo(new Vector2(1, TaikoPlayfield.DEFAULT_HEIGHT), 500); + PlayfieldContainer.Delay(delay).ResizeTo(new Vector2(1, DEFAULT_PLAYFIELD_CONTAINER_HEIGHT), 500); break; } }