From 8d7453c251b68243ed6a082f815a1d2133e71aa4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 16 Oct 2019 22:10:50 +0900 Subject: [PATCH] Rework construction of nested hitobjects --- .../Objects/Drawables/DrawableSlider.cs | 124 +++++++++++------- .../Objects/Drawables/DrawableHitObject.cs | 44 +++++-- 2 files changed, 111 insertions(+), 57 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 09157d4fdc..21411259ae 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -5,7 +5,6 @@ using osuTK; using osu.Framework.Graphics; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; -using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -21,15 +20,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { public class DrawableSlider : DrawableOsuHitObject, IDrawableHitObjectWithProxiedApproach { - private readonly Slider slider; - private readonly List components = new List(); - - public readonly DrawableHitCircle HeadCircle; - public readonly DrawableSliderTail TailCircle; + public DrawableSliderHead HeadCircle { get; private set; } + public DrawableSliderTail TailCircle { get; private set; } public readonly SnakingSliderBody Body; public readonly SliderBall Ball; + private readonly Container headContainer; + private readonly Container tailContainer; + private readonly Container tickContainer; + private readonly Container repeatContainer; + + private readonly Slider slider; + private readonly IBindable positionBindable = new Bindable(); private readonly IBindable scaleBindable = new Bindable(); private readonly IBindable pathBindable = new Bindable(); @@ -44,14 +47,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Position = s.StackedPosition; - Container ticks; - Container repeatPoints; - InternalChildren = new Drawable[] { Body = new SnakingSliderBody(s), - ticks = new Container { RelativeSizeAxes = Axes.Both }, - repeatPoints = new Container { RelativeSizeAxes = Axes.Both }, + tickContainer = new Container { RelativeSizeAxes = Axes.Both }, + repeatContainer = new Container { RelativeSizeAxes = Axes.Both }, Ball = new SliderBall(s, this) { GetInitialHitAction = () => HeadCircle.HitAction, @@ -60,38 +60,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables AlwaysPresent = true, Alpha = 0 }, - HeadCircle = new DrawableSliderHead(s, s.HeadCircle) - { - OnShake = Shake - }, - TailCircle = new DrawableSliderTail(s, s.TailCircle) + headContainer = new Container { RelativeSizeAxes = Axes.Both }, + tailContainer = new Container { RelativeSizeAxes = Axes.Both }, }; - - components.Add(Body); - components.Add(Ball); - - AddNested(HeadCircle); - - AddNested(TailCircle); - components.Add(TailCircle); - - foreach (var tick in s.NestedHitObjects.OfType()) - { - var drawableTick = new DrawableSliderTick(tick) { Position = tick.Position - s.Position }; - - ticks.Add(drawableTick); - components.Add(drawableTick); - AddNested(drawableTick); - } - - foreach (var repeatPoint in s.NestedHitObjects.OfType()) - { - var drawableRepeatPoint = new DrawableRepeatPoint(repeatPoint, this) { Position = repeatPoint.Position - s.Position }; - - repeatPoints.Add(drawableRepeatPoint); - components.Add(drawableRepeatPoint); - AddNested(drawableRepeatPoint); - } } [BackgroundDependencyLoader] @@ -122,6 +93,60 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables }, true); } + protected override void AddNested(DrawableHitObject h) + { + base.AddNested(h); + + switch (h) + { + case DrawableSliderHead head: + headContainer.Child = HeadCircle = head; + break; + + case DrawableSliderTail tail: + tailContainer.Child = TailCircle = tail; + break; + + case DrawableSliderTick tick: + tickContainer.Add(tick); + break; + + case DrawableRepeatPoint repeat: + repeatContainer.Add(repeat); + break; + } + } + + protected override void ClearNested() + { + base.ClearNested(); + + headContainer.Clear(); + tailContainer.Clear(); + repeatContainer.Clear(); + tickContainer.Clear(); + } + + protected override DrawableHitObject CreateNested(HitObject hitObject) + { + switch (hitObject) + { + case SliderTailCircle tail: + return new DrawableSliderTail(slider, tail); + + case HitCircle head: + return new DrawableSliderHead(slider, head) { OnShake = Shake }; + + case SliderTick tick: + return new DrawableSliderTick(tick) { Position = tick.Position - slider.Position }; + + case RepeatPoint repeat: + return new DrawableRepeatPoint(repeat, this) { Position = repeat.Position - slider.Position }; + } + + return base.CreateNested(hitObject); + } + protected override void UpdateInitialTransforms() { base.UpdateInitialTransforms(); @@ -139,9 +164,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables double completionProgress = MathHelper.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1); - foreach (var c in components.OfType()) c.UpdateProgress(completionProgress); - foreach (var c in components.OfType()) c.UpdateSnakingPosition(slider.Path.PositionAt(Body.SnakedStart ?? 0), slider.Path.PositionAt(Body.SnakedEnd ?? 0)); - foreach (var t in components.OfType()) t.Tracking = Ball.Tracking; + Ball.UpdateProgress(completionProgress); + Body.UpdateProgress(completionProgress); + + foreach (DrawableHitObject hitObject in NestedHitObjects) + { + if (hitObject is ITrackSnaking s) s.UpdateSnakingPosition(slider.Path.PositionAt(Body.SnakedStart ?? 0), slider.Path.PositionAt(Body.SnakedEnd ?? 0)); + if (hitObject is IRequireTracking t) t.Tracking = Ball.Tracking; + } Size = Body.Size; OriginPosition = Body.PathOffset; @@ -187,7 +217,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables ApplyResult(r => { - var judgementsCount = NestedHitObjects.Count(); + var judgementsCount = NestedHitObjects.Count; var judgementsHit = NestedHitObjects.Count(h => h.IsHit); var hitFraction = (double)judgementsHit / judgementsCount; @@ -228,7 +258,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } } - public Drawable ProxiedLayer => HeadCircle.ApproachCircle; + public Drawable ProxiedLayer => new Container(); // Todo: public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Body.ReceivePositionalInputAt(screenSpacePos); } diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 7f3bfd3b5c..6bc2ccb889 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Objects.Drawables protected virtual IEnumerable GetSamples() => HitObject.Samples; private readonly Lazy> nestedHitObjects = new Lazy>(); - public IEnumerable NestedHitObjects => nestedHitObjects.IsValueCreated ? nestedHitObjects.Value : Enumerable.Empty(); + public IReadOnlyList NestedHitObjects => nestedHitObjects.IsValueCreated ? nestedHitObjects.Value : (IReadOnlyList)Array.Empty(); /// /// Invoked when a has been applied by this or a nested . @@ -125,6 +125,8 @@ namespace osu.Game.Rulesets.Objects.Drawables { base.LoadComplete(); + Apply(HitObject); + if (HitObject is IHasComboInformation combo) { comboIndexBindable = combo.ComboIndexBindable.GetBoundCopy(); @@ -134,6 +136,37 @@ namespace osu.Game.Rulesets.Objects.Drawables updateState(ArmedState.Idle, true); } + protected void Apply(HitObject hitObject) + { + if (nestedHitObjects.IsValueCreated) + { + nestedHitObjects.Value.Clear(); + ClearNested(); + } + + foreach (var h in hitObject.NestedHitObjects) + { + var drawableNested = CreateNested(h) ?? throw new InvalidOperationException($"{nameof(CreateNested)} returned null for {h.GetType().ReadableName()}."); + + drawableNested.OnNewResult += (d, r) => OnNewResult?.Invoke(d, r); + drawableNested.OnRevertResult += (d, r) => OnRevertResult?.Invoke(d, r); + drawableNested.ApplyCustomUpdateState += (d, j) => ApplyCustomUpdateState?.Invoke(d, j); + + nestedHitObjects.Value.Add(drawableNested); + AddNested(drawableNested); + } + } + + protected virtual void AddNested(DrawableHitObject h) + { + } + + protected virtual void ClearNested() + { + } + + protected virtual DrawableHitObject CreateNested(HitObject hitObject) => null; + #region State / Transform Management /// @@ -356,15 +389,6 @@ namespace osu.Game.Rulesets.Objects.Drawables UpdateResult(false); } - protected virtual void AddNested(DrawableHitObject h) - { - h.OnNewResult += (d, r) => OnNewResult?.Invoke(d, r); - h.OnRevertResult += (d, r) => OnRevertResult?.Invoke(d, r); - h.ApplyCustomUpdateState += (d, j) => ApplyCustomUpdateState?.Invoke(d, j); - - nestedHitObjects.Value.Add(h); - } - /// /// Applies the of this , notifying responders such as /// the of the .