From 4afe83e74e9eb8978fc5fe77bae67badb1bff5b8 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Fri, 16 Jun 2017 19:21:54 +0900 Subject: [PATCH 1/6] Rework DrawableHitObject to provide default life times and proper DrawableTimingSection autosizing. This exposes LifetimeOffset from DrawableHitObject which is used by the XSRG rulesets to adjust the life time range by the VisibleTimeRange. --- .../Tests/TestCaseScrollingHitObjects.cs | 16 +--- .../Drawables/DrawableManiaHitObject.cs | 7 -- .../Objects/Drawables/DrawableNote.cs | 2 +- osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs | 8 +- .../Objects/Drawables/DrawableHitObject.cs | 34 +++++++++ .../Rulesets/Timing/DrawableTimingSection.cs | 76 ++++++++++++------- .../Timing/SpeedAdjustmentContainer.cs | 16 +++- 7 files changed, 103 insertions(+), 56 deletions(-) diff --git a/osu.Desktop.VisualTests/Tests/TestCaseScrollingHitObjects.cs b/osu.Desktop.VisualTests/Tests/TestCaseScrollingHitObjects.cs index 5a4b3deb05..2d30d5603a 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseScrollingHitObjects.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseScrollingHitObjects.cs @@ -125,6 +125,8 @@ namespace osu.Desktop.VisualTests.Tests private class TestSpeedAdjustmentContainer : SpeedAdjustmentContainer { + public override bool RemoveWhenNotAlive => false; + public TestSpeedAdjustmentContainer(MultiplierControlPoint controlPoint) : base(controlPoint) { @@ -195,25 +197,11 @@ namespace osu.Desktop.VisualTests.Tests FadeInFromZero(250, EasingTypes.OutQuint); } - private bool hasExpired; protected override void Update() { base.Update(); if (Time.Current >= HitObject.StartTime) - { background.Colour = Color4.Red; - - if (!hasExpired) - { - using (BeginDelayedSequence(200)) - { - FadeOut(200); - Expire(); - } - - hasExpired = true; - } - } } } } diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs index e32e953404..c0eea3ce22 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs @@ -33,13 +33,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables Y = (float)HitObject.StartTime; } - protected override void LoadComplete() - { - base.LoadComplete(); - - LifetimeStart = HitObject.StartTime - ManiaPlayfield.TIME_SPAN_MAX; - } - public override Color4 AccentColour { get { return base.AccentColour; } diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs index 658d409bb8..9322fed3eb 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables public DrawableNote(Note hitObject, Bindable key = null) : base(hitObject, key) { - RelativeSizeAxes = Axes.Both; + RelativeSizeAxes = Axes.X; Height = 100; Add(headPiece = new NotePiece diff --git a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs index 5f33ac2cf1..205f8e152c 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs @@ -30,8 +30,8 @@ namespace osu.Game.Rulesets.Mania.UI public const float HIT_TARGET_POSITION = 50; private const double time_span_default = 1500; - public const double TIME_SPAN_MIN = 50; - public const double TIME_SPAN_MAX = 10000; + private const double time_span_min = 50; + private const double time_span_max = 10000; private const double time_span_step = 50; /// @@ -60,8 +60,8 @@ namespace osu.Game.Rulesets.Mania.UI private readonly BindableDouble visibleTimeRange = new BindableDouble(time_span_default) { - MinValue = TIME_SPAN_MIN, - MaxValue = TIME_SPAN_MAX + MinValue = time_span_min, + MaxValue = time_span_max }; private readonly SpeedAdjustmentCollection barLineContainer; diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index fcdcf672d5..68d2023109 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -12,11 +12,28 @@ using osu.Game.Rulesets.Objects.Types; using OpenTK.Graphics; using osu.Game.Audio; using System.Linq; +using osu.Framework.Configuration; namespace osu.Game.Rulesets.Objects.Drawables { public abstract class DrawableHitObject : Container { + private readonly BindableDouble lifetimeOffset = new BindableDouble(); + /// + /// Time offset before at which this becomes visible and the time offset + /// after or at which it expires. + /// + /// + /// This provides only a default life time range, however classes inheriting from should expire their state through + /// if more tight control over the life time is desired. + /// + /// + public BindableDouble LifetimeOffset + { + get { return lifetimeOffset; } + set { lifetimeOffset.BindTo(value); } + } + public readonly HitObject HitObject; /// @@ -28,6 +45,22 @@ namespace osu.Game.Rulesets.Objects.Drawables { HitObject = hitObject; } + + public override double LifetimeStart + { + get { return Math.Min(HitObject.StartTime - lifetimeOffset, base.LifetimeStart); } + set { base.LifetimeStart = value; } + } + + public override double LifetimeEnd + { + get + { + var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime; + return Math.Max(endTime + LifetimeOffset, base.LifetimeEnd); + } + set { base.LifetimeEnd = value; } + } } public abstract class DrawableHitObject : DrawableHitObject @@ -190,6 +223,7 @@ namespace osu.Game.Rulesets.Objects.Drawables nestedHitObjects = new List>(); h.OnJudgement += d => OnJudgement?.Invoke(d); + h.LifetimeOffset = LifetimeOffset; nestedHitObjects.Add(h); } diff --git a/osu.Game/Rulesets/Timing/DrawableTimingSection.cs b/osu.Game/Rulesets/Timing/DrawableTimingSection.cs index ef5daf0de8..1f1abc9bb4 100644 --- a/osu.Game/Rulesets/Timing/DrawableTimingSection.cs +++ b/osu.Game/Rulesets/Timing/DrawableTimingSection.cs @@ -10,6 +10,8 @@ using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using OpenTK; +using osu.Game.Rulesets.Objects.Types; +using System; namespace osu.Game.Rulesets.Timing { @@ -44,14 +46,17 @@ namespace osu.Game.Rulesets.Timing set { visibleTimeRange.BindTo(value); } } - protected override IComparer DepthComparer => new HitObjectReverseStartTimeComparer(); - /// - /// Axes through which this timing section scrolls. This is set from . + /// Axes through which this timing section scrolls. This is set by the . /// internal Axes ScrollingAxes; - private Cached layout = new Cached(); + /// + /// The control point that provides the speed adjustments for this container. This is set by the . + /// + internal MultiplierControlPoint ControlPoint; + + protected override IComparer DepthComparer => new HitObjectReverseStartTimeComparer(); /// /// Creates a new . @@ -71,41 +76,54 @@ namespace osu.Game.Rulesets.Timing return; } - layout.Invalidate(); + durationBacking.Invalidate(); base.InvalidateFromChild(invalidation); } - protected override void UpdateAfterChildren() + private Cached durationBacking = new Cached(); + /// + /// The maximum duration of any one hit object inside this . This is calculated as the maximum + /// end time between all hit objects relative to this 's . + /// + public double Duration => durationBacking.EnsureValid() + ? durationBacking.Value + : durationBacking.Refresh(() => { - base.UpdateAfterChildren(); + if (!Children.Any()) + return 0; - if (!layout.EnsureValid()) - { - layout.Refresh(() => - { - if (!Children.Any()) - return; + double baseDuration = Children.Max(c => (c.HitObject as IHasEndTime)?.EndTime ?? c.HitObject.StartTime) - ControlPoint.StartTime; - //double maxDuration = Children.Select(c => (c.HitObject as IHasEndTime)?.EndTime ?? c.HitObject.StartTime).Max(); - //float width = (float)maxDuration - RelativeChildOffset.X; - //float height = (float)maxDuration - RelativeChildOffset.Y; + if (baseDuration == 0) + baseDuration = 1000; + // Scrolling rulesets typically have anchors/origins set to their start time, but if an object has no end time and lies on the control point + // then the baseDuration above will be 0. This will cause problems with masking when it is set as the value for Size in Update(). + // + // Thus we _want_ the timing section to completely contain the hit object, and to do with we'll need to find a duration that corresponds + // to the absolute size of the element that extrudes beyond our bounds. For simplicity, we can approximate this by just using the largest + // absolute size available from our children. + float maxAbsoluteSize = Children.Where(c => (c.RelativeSizeAxes & ScrollingAxes) == 0) + .Select(c => (ScrollingAxes & Axes.X) > 0 ? c.Width : c.Height) + .DefaultIfEmpty().Max(); - // Auto-size to the total size of our children - // This ends up being the total duration of our children, however for now this is a more sure-fire way to calculate this - // than the above due to some undesired masking optimisations causing some hit objects to be culled... - // Todo: When this is investigated more we should use the above method as it is a little more exact - // Todo: This is not working correctly in the case that hit objects are absolutely-sized - needs a proper looking into in osu!framework - float width = Children.Select(child => child.X + child.Width).Max() - RelativeChildOffset.X; - float height = Children.Select(child => child.Y + child.Height).Max() - RelativeChildOffset.Y; + float ourAbsoluteSize = (ScrollingAxes & Axes.X) > 0 ? DrawWidth : DrawHeight; - // Consider that width/height are time values. To have ourselves span these time values 1:1, we first need to set our size - Size = new Vector2((ScrollingAxes & Axes.X) > 0 ? width : Size.X, (ScrollingAxes & Axes.Y) > 0 ? height : Size.Y); - // Then to make our position-space be time values again, we need our relative child size to follow our size - RelativeChildSize = Size; - }); - } + // Add the extra duration to account for the absolute size + baseDuration *= 1 + maxAbsoluteSize / ourAbsoluteSize; + + return baseDuration; + }); + + protected override void Update() + { + base.Update(); + + // Consider that width/height are time values. To have ourselves span these time values 1:1, we first need to set our size + Size = new Vector2((ScrollingAxes & Axes.X) > 0 ? (float)Duration : Size.X, (ScrollingAxes & Axes.Y) > 0 ? (float)Duration : Size.Y); + // Then to make our position-space be time values again, we need our relative child size to follow our size + RelativeChildSize = Size; } } } diff --git a/osu.Game/Rulesets/Timing/SpeedAdjustmentContainer.cs b/osu.Game/Rulesets/Timing/SpeedAdjustmentContainer.cs index af9a79adb5..b5973099d2 100644 --- a/osu.Game/Rulesets/Timing/SpeedAdjustmentContainer.cs +++ b/osu.Game/Rulesets/Timing/SpeedAdjustmentContainer.cs @@ -7,6 +7,8 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Objects.Drawables; using OpenTK; +using System.Linq; +using System; namespace osu.Game.Rulesets.Timing { @@ -42,6 +44,8 @@ namespace osu.Game.Rulesets.Timing public readonly MultiplierControlPoint ControlPoint; + private DrawableTimingSection timingSection; + /// /// Creates a new . /// @@ -56,9 +60,10 @@ namespace osu.Game.Rulesets.Timing [BackgroundDependencyLoader] private void load() { - DrawableTimingSection timingSection = CreateTimingSection(); + timingSection = CreateTimingSection(); timingSection.ScrollingAxes = ScrollingAxes; + timingSection.ControlPoint = ControlPoint; timingSection.VisibleTimeRange.BindTo(VisibleTimeRange); timingSection.RelativeChildOffset = new Vector2((ScrollingAxes & Axes.X) > 0 ? (float)ControlPoint.StartTime : 0, (ScrollingAxes & Axes.Y) > 0 ? (float)ControlPoint.StartTime : 0); @@ -74,6 +79,15 @@ namespace osu.Game.Rulesets.Timing RelativeChildSize = new Vector2((ScrollingAxes & Axes.X) > 0 ? (float)VisibleTimeRange : 1, (ScrollingAxes & Axes.Y) > 0 ? (float)VisibleTimeRange : 1); } + public override double LifetimeStart => ControlPoint.StartTime - VisibleTimeRange; + public override double LifetimeEnd => ControlPoint.StartTime + timingSection.Duration + VisibleTimeRange; + + public override void Add(DrawableHitObject drawable) + { + drawable.LifetimeOffset.BindTo(VisibleTimeRange); + base.Add(drawable); + } + /// /// Whether this speed adjustment can contain a hit object. This is true if the hit object occurs after this speed adjustment with respect to time. /// From 28e48eab2b491c73eb4a10517d39b5c3021c770a Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Fri, 16 Jun 2017 19:30:30 +0900 Subject: [PATCH 2/6] CI fixes. --- .../Objects/Drawables/DrawableManiaHitObject.cs | 1 - osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 4 ++-- osu.Game/Rulesets/Timing/DrawableTimingSection.cs | 3 +-- osu.Game/Rulesets/Timing/SpeedAdjustmentContainer.cs | 2 -- 4 files changed, 3 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs index c0eea3ce22..4e276fddb7 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs @@ -6,7 +6,6 @@ using OpenTK.Input; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Game.Rulesets.Mania.Judgements; -using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Objects.Drawables; namespace osu.Game.Rulesets.Mania.Objects.Drawables diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 68d2023109..f23fab6d1b 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -20,8 +20,8 @@ namespace osu.Game.Rulesets.Objects.Drawables { private readonly BindableDouble lifetimeOffset = new BindableDouble(); /// - /// Time offset before at which this becomes visible and the time offset - /// after or at which it expires. + /// Time offset before the hit object start time at which this becomes visible and the time offset + /// after the hit object's end time after which it expires. /// /// /// This provides only a default life time range, however classes inheriting from should expire their state through diff --git a/osu.Game/Rulesets/Timing/DrawableTimingSection.cs b/osu.Game/Rulesets/Timing/DrawableTimingSection.cs index 1f1abc9bb4..d759a7caa6 100644 --- a/osu.Game/Rulesets/Timing/DrawableTimingSection.cs +++ b/osu.Game/Rulesets/Timing/DrawableTimingSection.cs @@ -11,7 +11,6 @@ using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using OpenTK; using osu.Game.Rulesets.Objects.Types; -using System; namespace osu.Game.Rulesets.Timing { @@ -84,7 +83,7 @@ namespace osu.Game.Rulesets.Timing private Cached durationBacking = new Cached(); /// /// The maximum duration of any one hit object inside this . This is calculated as the maximum - /// end time between all hit objects relative to this 's . + /// end time between all hit objects relative to this 's . /// public double Duration => durationBacking.EnsureValid() ? durationBacking.Value diff --git a/osu.Game/Rulesets/Timing/SpeedAdjustmentContainer.cs b/osu.Game/Rulesets/Timing/SpeedAdjustmentContainer.cs index b5973099d2..0024b3c9c8 100644 --- a/osu.Game/Rulesets/Timing/SpeedAdjustmentContainer.cs +++ b/osu.Game/Rulesets/Timing/SpeedAdjustmentContainer.cs @@ -7,8 +7,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Objects.Drawables; using OpenTK; -using System.Linq; -using System; namespace osu.Game.Rulesets.Timing { From 9fea18778849e9cad2009231c2bb6b0024de6e40 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Fri, 16 Jun 2017 19:47:15 +0900 Subject: [PATCH 3/6] A bit more commenting. --- .../Rulesets/Timing/DrawableTimingSection.cs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/osu.Game/Rulesets/Timing/DrawableTimingSection.cs b/osu.Game/Rulesets/Timing/DrawableTimingSection.cs index d759a7caa6..c565fd73a6 100644 --- a/osu.Game/Rulesets/Timing/DrawableTimingSection.cs +++ b/osu.Game/Rulesets/Timing/DrawableTimingSection.cs @@ -94,15 +94,19 @@ namespace osu.Game.Rulesets.Timing double baseDuration = Children.Max(c => (c.HitObject as IHasEndTime)?.EndTime ?? c.HitObject.StartTime) - ControlPoint.StartTime; + // If we have a singular hit object at the timing section's start time, let's set a sane default duration if (baseDuration == 0) baseDuration = 1000; - // Scrolling rulesets typically have anchors/origins set to their start time, but if an object has no end time and lies on the control point - // then the baseDuration above will be 0. This will cause problems with masking when it is set as the value for Size in Update(). + // Scrolling ruleset hit objects typically have anchors+origins set to the hit object's start time, but if the hit object doesn't implement IHasEndTime and lies on the control point + // then the baseDuration above will be 0. This will cause problems with masking when it is further set as the value for Size in Update(). We _want_ the timing section bounds to + // completely enclose the hit object to avoid the masking optimisations. // - // Thus we _want_ the timing section to completely contain the hit object, and to do with we'll need to find a duration that corresponds - // to the absolute size of the element that extrudes beyond our bounds. For simplicity, we can approximate this by just using the largest - // absolute size available from our children. + // To do this we need to find a duration that corresponds to the absolute size of the element that extrudes beyond the timing section's bounds and add that to baseDuration. + // We can utilize the fact that the Size and RelativeChildSpace are 1:1, meaning that an change in duration for the timing section has no change to the hit object's positioning + // and simply find the largest absolutely-sized element in this timing section. This introduces a little bit of error, but will never under-estimate the duration. + + // Find the largest element that is absolutely-sized along ScrollingAxes float maxAbsoluteSize = Children.Where(c => (c.RelativeSizeAxes & ScrollingAxes) == 0) .Select(c => (ScrollingAxes & Axes.X) > 0 ? c.Width : c.Height) .DefaultIfEmpty().Max(); @@ -119,9 +123,9 @@ namespace osu.Game.Rulesets.Timing { base.Update(); - // Consider that width/height are time values. To have ourselves span these time values 1:1, we first need to set our size + // We want our size and position-space along ScrollingAxes to span our duration to completely enclose all the hit objects Size = new Vector2((ScrollingAxes & Axes.X) > 0 ? (float)Duration : Size.X, (ScrollingAxes & Axes.Y) > 0 ? (float)Duration : Size.Y); - // Then to make our position-space be time values again, we need our relative child size to follow our size + // And we need to make sure the hit object's position-space doesn't change due to our resizing RelativeChildSize = Size; } } From 38f2bd47c5fe94286c8d507b7b41880856be812c Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Fri, 16 Jun 2017 20:00:16 +0900 Subject: [PATCH 4/6] Even saner default. --- osu.Game/Rulesets/Timing/DrawableTimingSection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Timing/DrawableTimingSection.cs b/osu.Game/Rulesets/Timing/DrawableTimingSection.cs index c565fd73a6..3ad9f23605 100644 --- a/osu.Game/Rulesets/Timing/DrawableTimingSection.cs +++ b/osu.Game/Rulesets/Timing/DrawableTimingSection.cs @@ -96,7 +96,7 @@ namespace osu.Game.Rulesets.Timing // If we have a singular hit object at the timing section's start time, let's set a sane default duration if (baseDuration == 0) - baseDuration = 1000; + baseDuration = 1; // Scrolling ruleset hit objects typically have anchors+origins set to the hit object's start time, but if the hit object doesn't implement IHasEndTime and lies on the control point // then the baseDuration above will be 0. This will cause problems with masking when it is further set as the value for Size in Update(). We _want_ the timing section bounds to From 34ac932fe2aff3de756edfe8bda997e3b14f1687 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Mon, 19 Jun 2017 10:54:23 +0900 Subject: [PATCH 5/6] Reduce pollution in DrawableHitObject in favor of a loosely-coupled IScrollingHitObject. --- .../Tests/TestCaseScrollingHitObjects.cs | 4 +- .../Drawables/DrawableManiaHitObject.cs | 2 +- .../Objects/Drawables/DrawableHitObject.cs | 35 +------------ .../Drawables/DrawableScrollingHitObject.cs | 49 +++++++++++++++++++ .../Objects/Drawables/IScrollingHitObject.cs | 25 ++++++++++ .../Timing/SpeedAdjustmentCollection.cs | 17 ++++--- .../Timing/SpeedAdjustmentContainer.cs | 4 +- osu.Game/osu.Game.csproj | 2 + 8 files changed, 94 insertions(+), 44 deletions(-) create mode 100644 osu.Game/Rulesets/Objects/Drawables/DrawableScrollingHitObject.cs create mode 100644 osu.Game/Rulesets/Objects/Drawables/IScrollingHitObject.cs diff --git a/osu.Desktop.VisualTests/Tests/TestCaseScrollingHitObjects.cs b/osu.Desktop.VisualTests/Tests/TestCaseScrollingHitObjects.cs index 2d30d5603a..62e5cda052 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseScrollingHitObjects.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseScrollingHitObjects.cs @@ -152,11 +152,13 @@ namespace osu.Desktop.VisualTests.Tests } } - private class TestDrawableHitObject : DrawableHitObject + private class TestDrawableHitObject : DrawableHitObject, IScrollingHitObject { private readonly Box background; private const float height = 14; + public BindableDouble LifetimeOffset { get; } = new BindableDouble(); + public TestDrawableHitObject(HitObject hitObject) : base(hitObject) { diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs index 4e276fddb7..cb1352fc4a 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs @@ -10,7 +10,7 @@ using osu.Game.Rulesets.Objects.Drawables; namespace osu.Game.Rulesets.Mania.Objects.Drawables { - public abstract class DrawableManiaHitObject : DrawableHitObject + public abstract class DrawableManiaHitObject : DrawableScrollingHitObject where TObject : ManiaHitObject { /// diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index f23fab6d1b..79265d6266 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -18,22 +18,6 @@ namespace osu.Game.Rulesets.Objects.Drawables { public abstract class DrawableHitObject : Container { - private readonly BindableDouble lifetimeOffset = new BindableDouble(); - /// - /// Time offset before the hit object start time at which this becomes visible and the time offset - /// after the hit object's end time after which it expires. - /// - /// - /// This provides only a default life time range, however classes inheriting from should expire their state through - /// if more tight control over the life time is desired. - /// - /// - public BindableDouble LifetimeOffset - { - get { return lifetimeOffset; } - set { lifetimeOffset.BindTo(value); } - } - public readonly HitObject HitObject; /// @@ -45,22 +29,6 @@ namespace osu.Game.Rulesets.Objects.Drawables { HitObject = hitObject; } - - public override double LifetimeStart - { - get { return Math.Min(HitObject.StartTime - lifetimeOffset, base.LifetimeStart); } - set { base.LifetimeStart = value; } - } - - public override double LifetimeEnd - { - get - { - var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime; - return Math.Max(endTime + LifetimeOffset, base.LifetimeEnd); - } - set { base.LifetimeEnd = value; } - } } public abstract class DrawableHitObject : DrawableHitObject @@ -217,13 +185,12 @@ namespace osu.Game.Rulesets.Objects.Drawables private List> nestedHitObjects; protected IEnumerable> NestedHitObjects => nestedHitObjects; - protected void AddNested(DrawableHitObject h) + protected virtual void AddNested(DrawableHitObject h) { if (nestedHitObjects == null) nestedHitObjects = new List>(); h.OnJudgement += d => OnJudgement?.Invoke(d); - h.LifetimeOffset = LifetimeOffset; nestedHitObjects.Add(h); } diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableScrollingHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableScrollingHitObject.cs new file mode 100644 index 0000000000..ff6ee35745 --- /dev/null +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableScrollingHitObject.cs @@ -0,0 +1,49 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Configuration; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects.Types; + +namespace osu.Game.Rulesets.Objects.Drawables +{ + /// + /// A basic class that overrides and implements . + /// + public abstract class DrawableScrollingHitObject : DrawableHitObject, IScrollingHitObject + where TObject : HitObject + where TJudgement : Judgement + { + public BindableDouble LifetimeOffset { get; } = new BindableDouble(); + + public DrawableScrollingHitObject(TObject hitObject) + : base(hitObject) + { + } + + public override double LifetimeStart + { + get { return Math.Min(HitObject.StartTime - LifetimeOffset, base.LifetimeStart); } + set { base.LifetimeStart = value; } + } + + public override double LifetimeEnd + { + get + { + var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime; + return Math.Max(endTime + LifetimeOffset, base.LifetimeEnd); + } + set { base.LifetimeEnd = value; } + } + + protected override void AddNested(DrawableHitObject h) + { + var scrollingHitObject = h as IScrollingHitObject; + scrollingHitObject?.LifetimeOffset.BindTo(LifetimeOffset); + + base.AddNested(h); + } + } +} \ No newline at end of file diff --git a/osu.Game/Rulesets/Objects/Drawables/IScrollingHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/IScrollingHitObject.cs new file mode 100644 index 0000000000..560f15f133 --- /dev/null +++ b/osu.Game/Rulesets/Objects/Drawables/IScrollingHitObject.cs @@ -0,0 +1,25 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Configuration; +using osu.Framework.Graphics; + +namespace osu.Game.Rulesets.Objects.Drawables +{ + /// + /// An interface that exposes properties required for scrolling hit objects to be properly displayed. + /// + public interface IScrollingHitObject : IDrawable + { + /// + /// Time offset before the hit object start time at which this becomes visible and the time offset + /// after the hit object's end time after which it expires. + /// + /// + /// This provides only a default life time range, however classes inheriting from should override + /// their life times if more tight control is desired. + /// + /// + BindableDouble LifetimeOffset { get; } + } +} \ No newline at end of file diff --git a/osu.Game/Rulesets/Timing/SpeedAdjustmentCollection.cs b/osu.Game/Rulesets/Timing/SpeedAdjustmentCollection.cs index 53ad00c5e1..1323bb14a1 100644 --- a/osu.Game/Rulesets/Timing/SpeedAdjustmentCollection.cs +++ b/osu.Game/Rulesets/Timing/SpeedAdjustmentCollection.cs @@ -51,6 +51,13 @@ namespace osu.Game.Rulesets.Timing this.scrollingAxes = scrollingAxes; } + public override void Add(SpeedAdjustmentContainer speedAdjustment) + { + speedAdjustment.VisibleTimeRange.BindTo(VisibleTimeRange); + speedAdjustment.ScrollingAxes = scrollingAxes; + base.Add(speedAdjustment); + } + /// /// Adds a hit object to this . The hit objects will be kept in a queue /// and will be processed when new s are added to this . @@ -58,14 +65,10 @@ namespace osu.Game.Rulesets.Timing /// The hit object to add. public void Add(DrawableHitObject hitObject) { - queuedHitObjects.Enqueue(hitObject); - } + if (!(hitObject is IScrollingHitObject)) + throw new InvalidOperationException($"Hit objects added to a {nameof(SpeedAdjustmentCollection)} must implement {nameof(IScrollingHitObject)}."); - public override void Add(SpeedAdjustmentContainer speedAdjustment) - { - speedAdjustment.VisibleTimeRange.BindTo(VisibleTimeRange); - speedAdjustment.ScrollingAxes = scrollingAxes; - base.Add(speedAdjustment); + queuedHitObjects.Enqueue(hitObject); } protected override void Update() diff --git a/osu.Game/Rulesets/Timing/SpeedAdjustmentContainer.cs b/osu.Game/Rulesets/Timing/SpeedAdjustmentContainer.cs index 0024b3c9c8..f41ef21c1e 100644 --- a/osu.Game/Rulesets/Timing/SpeedAdjustmentContainer.cs +++ b/osu.Game/Rulesets/Timing/SpeedAdjustmentContainer.cs @@ -82,7 +82,9 @@ namespace osu.Game.Rulesets.Timing public override void Add(DrawableHitObject drawable) { - drawable.LifetimeOffset.BindTo(VisibleTimeRange); + var scrollingHitObject = drawable as IScrollingHitObject; + scrollingHitObject?.LifetimeOffset.BindTo(VisibleTimeRange); + base.Add(drawable); } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 36f358801e..21cf1f0687 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -185,6 +185,7 @@ + @@ -223,6 +224,7 @@ + From 3389c8a4dcbf66f3119a14f0bae47987469c21bb Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Wed, 21 Jun 2017 20:17:15 +0900 Subject: [PATCH 6/6] CI fixes. --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 1 - .../Rulesets/Objects/Drawables/DrawableScrollingHitObject.cs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 79265d6266..8cfedc9599 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -12,7 +12,6 @@ using osu.Game.Rulesets.Objects.Types; using OpenTK.Graphics; using osu.Game.Audio; using System.Linq; -using osu.Framework.Configuration; namespace osu.Game.Rulesets.Objects.Drawables { diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableScrollingHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableScrollingHitObject.cs index ff6ee35745..8dc17b9542 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableScrollingHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableScrollingHitObject.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Objects.Drawables { public BindableDouble LifetimeOffset { get; } = new BindableDouble(); - public DrawableScrollingHitObject(TObject hitObject) + protected DrawableScrollingHitObject(TObject hitObject) : base(hitObject) { }