diff --git a/osu.Android.props b/osu.Android.props
index e3285222f8..bbe8426316 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -52,6 +52,6 @@
-
+
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleApplication.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleApplication.cs
new file mode 100644
index 0000000000..8b3fead366
--- /dev/null
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleApplication.cs
@@ -0,0 +1,44 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using NUnit.Framework;
+using osu.Framework.Timing;
+using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
+using osu.Game.Tests.Visual;
+using osuTK;
+
+namespace osu.Game.Rulesets.Osu.Tests
+{
+ public class TestSceneHitCircleApplication : OsuTestScene
+ {
+ [Test]
+ public void TestApplyNewCircle()
+ {
+ DrawableHitCircle dho = null;
+
+ AddStep("create circle", () => Child = dho = new DrawableHitCircle(prepareObject(new HitCircle
+ {
+ Position = new Vector2(256, 192),
+ IndexInCurrentCombo = 0
+ }))
+ {
+ Clock = new FramedClock(new StopwatchClock())
+ });
+
+ AddStep("apply new circle", () => dho.Apply(prepareObject(new HitCircle
+ {
+ Position = new Vector2(128, 128),
+ ComboIndex = 1,
+ })));
+ }
+
+ private HitCircle prepareObject(HitCircle circle)
+ {
+ circle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
+ return circle;
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs
new file mode 100644
index 0000000000..f76c7e2a3e
--- /dev/null
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs
@@ -0,0 +1,59 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using NUnit.Framework;
+using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Objects.Types;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
+using osu.Game.Tests.Visual;
+using osuTK;
+
+namespace osu.Game.Rulesets.Osu.Tests
+{
+ public class TestSceneSliderApplication : OsuTestScene
+ {
+ [Test]
+ public void TestApplyNewSlider()
+ {
+ DrawableSlider dho = null;
+
+ AddStep("create slider", () => Child = dho = new DrawableSlider(prepareObject(new Slider
+ {
+ Position = new Vector2(256, 192),
+ IndexInCurrentCombo = 0,
+ StartTime = Time.Current,
+ Path = new SliderPath(PathType.Linear, new[]
+ {
+ Vector2.Zero,
+ new Vector2(150, 100),
+ new Vector2(300, 0),
+ })
+ })));
+
+ AddWaitStep("wait for progression", 1);
+
+ AddStep("apply new slider", () => dho.Apply(prepareObject(new Slider
+ {
+ Position = new Vector2(256, 192),
+ ComboIndex = 1,
+ StartTime = dho.HitObject.StartTime,
+ Path = new SliderPath(PathType.Bezier, new[]
+ {
+ Vector2.Zero,
+ new Vector2(150, 100),
+ new Vector2(300, 0),
+ }),
+ RepeatCount = 1
+ })));
+ }
+
+ private Slider prepareObject(Slider slider)
+ {
+ slider.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
+ return slider;
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerApplication.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerApplication.cs
new file mode 100644
index 0000000000..5951574079
--- /dev/null
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerApplication.cs
@@ -0,0 +1,46 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using NUnit.Framework;
+using osu.Framework.Timing;
+using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
+using osu.Game.Tests.Visual;
+using osuTK;
+
+namespace osu.Game.Rulesets.Osu.Tests
+{
+ public class TestSceneSpinnerApplication : OsuTestScene
+ {
+ [Test]
+ public void TestApplyNewCircle()
+ {
+ DrawableSpinner dho = null;
+
+ AddStep("create spinner", () => Child = dho = new DrawableSpinner(prepareObject(new Spinner
+ {
+ Position = new Vector2(256, 192),
+ IndexInCurrentCombo = 0,
+ Duration = 0,
+ }))
+ {
+ Clock = new FramedClock(new StopwatchClock())
+ });
+
+ AddStep("apply new spinner", () => dho.Apply(prepareObject(new Spinner
+ {
+ Position = new Vector2(256, 192),
+ ComboIndex = 1,
+ Duration = 1000,
+ })));
+ }
+
+ private Spinner prepareObject(Spinner circle)
+ {
+ circle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
+ return circle;
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
index b0c4e3758d..77d24db084 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
@@ -3,6 +3,7 @@
using System;
using System.Diagnostics;
+using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -30,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private Container scaleContainer;
private InputManager inputManager;
- public DrawableHitCircle(HitCircle h)
+ public DrawableHitCircle([CanBeNull] HitCircle h = null)
: base(h)
{
}
@@ -72,10 +73,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Size = HitArea.DrawSize;
- PositionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition, true);
- StackHeightBindable.BindValueChanged(_ => Position = HitObject.StackedPosition, true);
- ScaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue), true);
- AccentColour.BindValueChanged(accent => ApproachCircle.Colour = accent.NewValue, true);
+ PositionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition);
+ StackHeightBindable.BindValueChanged(_ => Position = HitObject.StackedPosition);
+ ScaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue));
+ AccentColour.BindValueChanged(accent => ApproachCircle.Colour = accent.NewValue);
}
protected override void LoadComplete()
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
index 208f79f165..d17bf93fa0 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
@@ -9,6 +9,7 @@ using osu.Framework.Graphics;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Graphics.Containers;
+using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.UI;
using osuTK;
@@ -49,6 +50,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
ShakeDuration = 30,
RelativeSizeAxes = Axes.Both
});
+ }
+
+ protected override void OnApply(HitObject hitObject)
+ {
+ base.OnApply(hitObject);
IndexInCurrentComboBindable.BindTo(HitObject.IndexInCurrentComboBindable);
PositionBindable.BindTo(HitObject.PositionBindable);
@@ -56,6 +62,16 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
ScaleBindable.BindTo(HitObject.ScaleBindable);
}
+ protected override void OnFree(HitObject hitObject)
+ {
+ base.OnFree(hitObject);
+
+ IndexInCurrentComboBindable.UnbindFrom(HitObject.IndexInCurrentComboBindable);
+ PositionBindable.UnbindFrom(HitObject.PositionBindable);
+ StackHeightBindable.UnbindFrom(HitObject.StackHeightBindable);
+ ScaleBindable.UnbindFrom(HitObject.ScaleBindable);
+ }
+
// Forward all internal management to shakeContainer.
// This is a bit ugly but we don't have the concept of InternalContent so it'll have to do for now. (https://github.com/ppy/osu-framework/issues/1690)
protected override void AddInternal(Drawable drawable) => shakeContainer.Add(drawable);
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
index d8dd0d7471..3f91a31066 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
@@ -3,6 +3,7 @@
using System;
using System.Linq;
+using JetBrains.Annotations;
using osuTK;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
@@ -32,14 +33,15 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private PlaySliderBody sliderBody => Body.Drawable as PlaySliderBody;
- public readonly IBindable PathVersion = new Bindable();
+ public IBindable PathVersion => pathVersion;
+ private readonly Bindable pathVersion = new Bindable();
private Container headContainer;
private Container tailContainer;
private Container tickContainer;
private Container repeatContainer;
- public DrawableSlider(Slider s)
+ public DrawableSlider([CanBeNull] Slider s = null)
: base(s)
{
}
@@ -63,11 +65,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
headContainer = new Container { RelativeSizeAxes = Axes.Both },
};
- PathVersion.BindTo(HitObject.Path.Version);
-
- PositionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition, true);
- StackHeightBindable.BindValueChanged(_ => Position = HitObject.StackedPosition, true);
- ScaleBindable.BindValueChanged(scale => Ball.Scale = new Vector2(scale.NewValue), true);
+ PositionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition);
+ StackHeightBindable.BindValueChanged(_ => Position = HitObject.StackedPosition);
+ ScaleBindable.BindValueChanged(scale => Ball.Scale = new Vector2(scale.NewValue));
AccentColour.BindValueChanged(colour =>
{
@@ -78,6 +78,22 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Tracking.BindValueChanged(updateSlidingSample);
}
+ protected override void OnApply(HitObject hitObject)
+ {
+ base.OnApply(hitObject);
+
+ // Ensure that the version will change after the upcoming BindTo().
+ pathVersion.Value = int.MaxValue;
+ PathVersion.BindTo(HitObject.Path.Version);
+ }
+
+ protected override void OnFree(HitObject hitObject)
+ {
+ base.OnFree(hitObject);
+
+ PathVersion.UnbindFrom(HitObject.Path.Version);
+ }
+
private PausableSkinnableSound slidingSample;
protected override void LoadSamples()
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
index 77fbff9c51..eb125969b0 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
@@ -3,6 +3,7 @@
using System;
using System.Linq;
+using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Bindables;
@@ -32,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private Bindable isSpinning;
private bool spinnerFrequencyModulate;
- public DrawableSpinner(Spinner s)
+ public DrawableSpinner([CanBeNull] Spinner s = null)
: base(s)
{
}
@@ -72,7 +73,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
}
};
- PositionBindable.BindValueChanged(pos => Position = pos.NewValue, true);
+ PositionBindable.BindValueChanged(pos => Position = pos.NewValue);
}
protected override void LoadComplete()
diff --git a/osu.Game.Rulesets.Taiko.Tests/DrawableTestHit.cs b/osu.Game.Rulesets.Taiko.Tests/DrawableTestHit.cs
index 4eeb4a1475..fb0917341e 100644
--- a/osu.Game.Rulesets.Taiko.Tests/DrawableTestHit.cs
+++ b/osu.Game.Rulesets.Taiko.Tests/DrawableTestHit.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 osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Scoring;
@@ -28,9 +27,10 @@ namespace osu.Game.Rulesets.Taiko.Tests
// suppress locally to allow hiding the visuals wherever necessary.
}
- [BackgroundDependencyLoader]
- private void load()
+ protected override void LoadComplete()
{
+ base.LoadComplete();
+
Result.Type = Type;
}
diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
index 5c3f57c2d0..7a4e136553 100644
--- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
+++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
+using System.Collections.Specialized;
using System.Linq;
using JetBrains.Annotations;
using osu.Framework.Allocation;
@@ -27,7 +28,10 @@ namespace osu.Game.Rulesets.Objects.Drawables
{
public event Action DefaultsApplied;
- public readonly HitObject HitObject;
+ ///
+ /// The currently represented by this .
+ ///
+ public HitObject HitObject { get; private set; }
///
/// The colour used for various elements of this DrawableHitObject.
@@ -96,10 +100,10 @@ namespace osu.Game.Rulesets.Objects.Drawables
///
protected virtual float SamplePlaybackPosition => 0.5f;
- private BindableList samplesBindable;
- private Bindable startTimeBindable;
- private Bindable userPositionalHitSounds;
- private Bindable comboIndexBindable;
+ private readonly Bindable startTimeBindable = new Bindable();
+ private readonly BindableList samplesBindable = new BindableList();
+ private readonly Bindable userPositionalHitSounds = new Bindable();
+ private readonly Bindable comboIndexBindable = new Bindable();
public override bool RemoveWhenNotAlive => false;
public override bool RemoveCompletedTransforms => false;
@@ -111,52 +115,157 @@ namespace osu.Game.Rulesets.Objects.Drawables
public IBindable State => state;
- protected DrawableHitObject([NotNull] HitObject hitObject)
+ ///
+ /// Whether is currently applied.
+ ///
+ private bool hasHitObjectApplied;
+
+ ///
+ /// Creates a new .
+ ///
+ ///
+ /// The to be initially applied to this .
+ /// If null, a hitobject is expected to be later applied via (or automatically via pooling).
+ ///
+ protected DrawableHitObject([CanBeNull] HitObject initialHitObject = null)
{
- HitObject = hitObject ?? throw new ArgumentNullException(nameof(hitObject));
+ HitObject = initialHitObject;
}
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
- userPositionalHitSounds = config.GetBindable(OsuSetting.PositionalHitSounds);
- var judgement = HitObject.CreateJudgement();
-
- Result = CreateResult(judgement);
- if (Result == null)
- throw new InvalidOperationException($"{GetType().ReadableName()} must provide a {nameof(JudgementResult)} through {nameof(CreateResult)}.");
+ config.BindWith(OsuSetting.PositionalHitSounds, userPositionalHitSounds);
}
protected override void LoadAsyncComplete()
{
base.LoadAsyncComplete();
- LoadSamples();
-
- HitObject.DefaultsApplied += onDefaultsApplied;
-
- startTimeBindable = HitObject.StartTimeBindable.GetBoundCopy();
- startTimeBindable.BindValueChanged(_ => updateState(State.Value, true));
-
- if (HitObject is IHasComboInformation combo)
- {
- comboIndexBindable = combo.ComboIndexBindable.GetBoundCopy();
- comboIndexBindable.BindValueChanged(_ => updateComboColour(), true);
- }
-
- samplesBindable = HitObject.SamplesBindable.GetBoundCopy();
- samplesBindable.CollectionChanged += (_, __) => LoadSamples();
-
- apply(HitObject);
+ if (HitObject != null)
+ Apply(HitObject);
}
protected override void LoadComplete()
{
base.LoadComplete();
+ startTimeBindable.BindValueChanged(_ => updateState(State.Value, true));
+ comboIndexBindable.BindValueChanged(_ => updateComboColour(), true);
+
updateState(ArmedState.Idle, true);
}
+ ///
+ /// Applies a new to be represented by this .
+ ///
+ /// The to apply.
+ public void Apply(HitObject hitObject)
+ {
+ free();
+
+ HitObject = hitObject ?? throw new InvalidOperationException($"Cannot apply a null {nameof(HitObject)}.");
+
+ // Ensure this DHO has a result.
+ Result ??= CreateResult(HitObject.CreateJudgement())
+ ?? throw new InvalidOperationException($"{GetType().ReadableName()} must provide a {nameof(JudgementResult)} through {nameof(CreateResult)}.");
+
+ foreach (var h in HitObject.NestedHitObjects)
+ {
+ var drawableNested = CreateNestedHitObject(h) ?? throw new InvalidOperationException($"{nameof(CreateNestedHitObject)} returned null for {h.GetType().ReadableName()}.");
+
+ drawableNested.OnNewResult += onNewResult;
+ drawableNested.OnRevertResult += onRevertResult;
+ drawableNested.ApplyCustomUpdateState += onApplyCustomUpdateState;
+
+ nestedHitObjects.Value.Add(drawableNested);
+ AddNestedHitObject(drawableNested);
+ }
+
+ startTimeBindable.BindTo(HitObject.StartTimeBindable);
+ if (HitObject is IHasComboInformation combo)
+ comboIndexBindable.BindTo(combo.ComboIndexBindable);
+
+ samplesBindable.BindTo(HitObject.SamplesBindable);
+ samplesBindable.BindCollectionChanged(onSamplesChanged, true);
+
+ HitObject.DefaultsApplied += onDefaultsApplied;
+
+ OnApply(hitObject);
+
+ // If not loaded, the state update happens in LoadComplete(). Otherwise, the update is scheduled to allow for lifetime updates.
+ if (IsLoaded)
+ Schedule(() => updateState(ArmedState.Idle, true));
+
+ hasHitObjectApplied = true;
+ }
+
+ ///
+ /// Removes the currently applied
+ ///
+ private void free()
+ {
+ if (!hasHitObjectApplied)
+ return;
+
+ startTimeBindable.UnbindFrom(HitObject.StartTimeBindable);
+ if (HitObject is IHasComboInformation combo)
+ comboIndexBindable.UnbindFrom(combo.ComboIndexBindable);
+
+ samplesBindable.UnbindFrom(HitObject.SamplesBindable);
+
+ // When a new hitobject is applied, the samples will be cleared before re-populating.
+ // In order to stop this needless update, the event is unbound and re-bound as late as possible in Apply().
+ samplesBindable.CollectionChanged -= onSamplesChanged;
+
+ if (nestedHitObjects.IsValueCreated)
+ {
+ foreach (var obj in nestedHitObjects.Value)
+ {
+ obj.OnNewResult -= onNewResult;
+ obj.OnRevertResult -= onRevertResult;
+ obj.ApplyCustomUpdateState -= onApplyCustomUpdateState;
+ }
+
+ nestedHitObjects.Value.Clear();
+ ClearNestedHitObjects();
+ }
+
+ HitObject.DefaultsApplied -= onDefaultsApplied;
+
+ OnFree(HitObject);
+
+ HitObject = null;
+ hasHitObjectApplied = false;
+ }
+
+ protected sealed override void FreeAfterUse()
+ {
+ base.FreeAfterUse();
+
+ // Freeing while not in a pool would cause the DHO to not be usable elsewhere in the hierarchy without being re-applied.
+ if (!IsInPool)
+ return;
+
+ free();
+ }
+
+ ///
+ /// Invoked for this to take on any values from a newly-applied .
+ ///
+ /// The being applied.
+ protected virtual void OnApply(HitObject hitObject)
+ {
+ }
+
+ ///
+ /// Invoked for this to revert any values previously taken on from the currently-applied .
+ ///
+ /// The currently-applied .
+ protected virtual void OnFree(HitObject hitObject)
+ {
+ }
+
///
/// Invoked by the base to populate samples, once on initial load and potentially again on any change to the samples collection.
///
@@ -183,34 +292,20 @@ namespace osu.Game.Rulesets.Objects.Drawables
AddInternal(Samples);
}
+ private void onSamplesChanged(object sender, NotifyCollectionChangedEventArgs e) => LoadSamples();
+
+ private void onNewResult(DrawableHitObject drawableHitObject, JudgementResult result) => OnNewResult?.Invoke(drawableHitObject, result);
+
+ private void onRevertResult(DrawableHitObject drawableHitObject, JudgementResult result) => OnRevertResult?.Invoke(drawableHitObject, result);
+
+ private void onApplyCustomUpdateState(DrawableHitObject drawableHitObject, ArmedState state) => ApplyCustomUpdateState?.Invoke(drawableHitObject, state);
+
private void onDefaultsApplied(HitObject hitObject)
{
- apply(hitObject);
- updateState(state.Value, true);
+ Apply(hitObject);
DefaultsApplied?.Invoke(this);
}
- private void apply(HitObject hitObject)
- {
- if (nestedHitObjects.IsValueCreated)
- {
- nestedHitObjects.Value.Clear();
- ClearNestedHitObjects();
- }
-
- foreach (var h in hitObject.NestedHitObjects)
- {
- var drawableNested = CreateNestedHitObject(h) ?? throw new InvalidOperationException($"{nameof(CreateNestedHitObject)} 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);
- AddNestedHitObject(drawableNested);
- }
- }
-
///
/// Invoked by the base to add nested s to the hierarchy.
///
@@ -600,19 +695,20 @@ namespace osu.Game.Rulesets.Objects.Drawables
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
- HitObject.DefaultsApplied -= onDefaultsApplied;
+
+ if (HitObject != null)
+ HitObject.DefaultsApplied -= onDefaultsApplied;
}
}
public abstract class DrawableHitObject : DrawableHitObject
where TObject : HitObject
{
- public new readonly TObject HitObject;
+ public new TObject HitObject => (TObject)base.HitObject;
protected DrawableHitObject(TObject hitObject)
: base(hitObject)
{
- HitObject = hitObject;
}
}
}
diff --git a/osu.Game/Skinning/SkinReloadableDrawable.cs b/osu.Game/Skinning/SkinReloadableDrawable.cs
index 4a1aaa62bf..cc9cbf7b59 100644
--- a/osu.Game/Skinning/SkinReloadableDrawable.cs
+++ b/osu.Game/Skinning/SkinReloadableDrawable.cs
@@ -3,14 +3,14 @@
using System;
using osu.Framework.Allocation;
-using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Pooling;
namespace osu.Game.Skinning
{
///
/// A drawable which has a callback when the skin changes.
///
- public abstract class SkinReloadableDrawable : CompositeDrawable
+ public abstract class SkinReloadableDrawable : PoolableDrawable
{
///
/// Invoked when has changed.
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 832722c729..8f0cc58594 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 ad6dd2a0b5..f766e0ec03 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -70,7 +70,7 @@
-
+
@@ -88,7 +88,7 @@
-
+