From 7526225282f8aa912e51bb7b1a5fa23af8f07260 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 5 Jan 2018 20:56:21 +0900 Subject: [PATCH] Use DP for most of the code to avoid unnecessary computations --- .../Visual/TestCaseScrollingHitObjects.cs | 11 +- .../Scrolling/ScrollingHitObjectContainer.cs | 105 ++++++++++++------ .../UI/Scrolling/ScrollingRulesetContainer.cs | 2 +- 3 files changed, 79 insertions(+), 39 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs b/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs index 7e332c7310..591bf4fadd 100644 --- a/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs +++ b/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs @@ -28,7 +28,7 @@ namespace osu.Game.Tests.Visual playfields.Add(new TestPlayfield(ScrollingDirection.Down)); playfields.Add(new TestPlayfield(ScrollingDirection.Right)); - playfields.ForEach(p => p.HitObjects.ControlPoints.Add(new MultiplierControlPoint(double.MinValue))); + playfields.ForEach(p => p.HitObjects.AddControlPoint(new MultiplierControlPoint(double.MinValue))); Add(new Container { @@ -82,12 +82,9 @@ namespace osu.Game.Tests.Visual { playfields.ForEach(p => { - p.HitObjects.ControlPoints.AddRange(new[] - { - new MultiplierControlPoint(time) { DifficultyPoint = { SpeedMultiplier = 3 } }, - new MultiplierControlPoint(time + 2000) { DifficultyPoint = { SpeedMultiplier = 2 } }, - new MultiplierControlPoint(time + 3000) { DifficultyPoint = { SpeedMultiplier = 1 } }, - }); + p.HitObjects.AddControlPoint(new MultiplierControlPoint(time) { DifficultyPoint = { SpeedMultiplier = 3 } }); + p.HitObjects.AddControlPoint(new MultiplierControlPoint(time + 2000) { DifficultyPoint = { SpeedMultiplier = 2 } }); + p.HitObjects.AddControlPoint(new MultiplierControlPoint(time + 3000) { DifficultyPoint = { SpeedMultiplier = 1 } }); TestDrawableControlPoint createDrawablePoint(double t) => new TestDrawableControlPoint(t) { diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index 8c3e1162e3..7bd5ab8f10 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -2,9 +2,12 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using System.Collections.Generic; +using osu.Framework.Caching; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Lists; +using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Timing; using OpenTK; @@ -19,26 +22,86 @@ namespace osu.Game.Rulesets.UI.Scrolling MaxValue = double.MaxValue }; - public readonly SortedList ControlPoints = new SortedList(); - private readonly ScrollingDirection direction; + private Cached positionCache = new Cached(); + public ScrollingHitObjectContainer(ScrollingDirection direction) { this.direction = direction; RelativeSizeAxes = Axes.Both; + + TimeRange.ValueChanged += v => positionCache.Invalidate(); } - protected override bool UpdateChildrenLife() + public override void Add(DrawableHitObject hitObject) { + positionCache.Invalidate(); + base.Add(hitObject); + } + + public override bool Remove(DrawableHitObject hitObject) + { + var result = base.Remove(hitObject); + if (result) + positionCache.Invalidate(); + return result; + } + + private readonly SortedList controlPoints = new SortedList(); + + public void AddControlPoint(MultiplierControlPoint controlPoint) + { + controlPoints.Add(controlPoint); + positionCache.Invalidate(); + } + + public bool RemoveControlPoint(MultiplierControlPoint controlPoint) + { + var result = controlPoints.Remove(controlPoint); + if (result) + positionCache.Invalidate(); + return result; + } + + private readonly Dictionary hitObjectPositions = new Dictionary(); + + protected override void Update() + { + base.Update(); + + if (positionCache.IsValid) + return; + foreach (var obj in Objects) { - obj.LifetimeStart = obj.HitObject.StartTime - TimeRange * 2; - obj.LifetimeEnd = ((obj.HitObject as IHasEndTime)?.EndTime ?? obj.HitObject.StartTime) + TimeRange * 2; + var startPosition = hitObjectPositions[obj] = positionAt(obj.HitObject.StartTime); + + obj.LifetimeStart = obj.HitObject.StartTime - TimeRange - 1000; + obj.LifetimeEnd = ((obj.HitObject as IHasEndTime)?.EndTime ?? obj.HitObject.StartTime) + TimeRange + 1000; + + if (!(obj.HitObject is IHasEndTime endTime)) + continue; + + var endPosition = positionAt(endTime.EndTime); + + float length = Vector2.Distance(startPosition, endPosition); + + switch (direction) + { + case ScrollingDirection.Up: + case ScrollingDirection.Down: + obj.Height = length; + break; + case ScrollingDirection.Left: + case ScrollingDirection.Right: + obj.Width = length; + break; + } } - return base.UpdateChildrenLife(); + positionCache.Validate(); } protected override void UpdateAfterChildrenLife() @@ -52,7 +115,7 @@ namespace osu.Game.Rulesets.UI.Scrolling foreach (var obj in AliveObjects) { - var finalPosition = positionAt(obj.HitObject.StartTime); + var finalPosition = hitObjectPositions[obj]; switch (direction) { @@ -69,36 +132,16 @@ namespace osu.Game.Rulesets.UI.Scrolling obj.X = -finalPosition.X + timelinePosition.X; break; } - - if (!(obj.HitObject is IHasEndTime endTime)) - continue; - - // Todo: We may need to consider scale here - var finalEndPosition = positionAt(endTime.EndTime); - - float length = Vector2.Distance(finalPosition, finalEndPosition); - - switch (direction) - { - case ScrollingDirection.Up: - case ScrollingDirection.Down: - obj.Height = length; - break; - case ScrollingDirection.Left: - case ScrollingDirection.Right: - obj.Width = length; - break; - } } } private Vector2 positionAt(double time) { - float length = 0; - for (int i = 0; i < ControlPoints.Count; i++) + double length = 0; + for (int i = 0; i < controlPoints.Count; i++) { - var current = ControlPoints[i]; - var next = i < ControlPoints.Count - 1 ? ControlPoints[i + 1] : null; + var current = controlPoints[i]; + var next = i < controlPoints.Count - 1 ? controlPoints[i + 1] : null; if (i > 0 && current.StartTime > time) continue; diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingRulesetContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingRulesetContainer.cs index 418013bcc1..8990710a9d 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingRulesetContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingRulesetContainer.cs @@ -86,7 +86,7 @@ namespace osu.Game.Rulesets.UI.Scrolling private void applySpeedAdjustment(MultiplierControlPoint controlPoint, ScrollingPlayfield playfield) { - playfield.HitObjects.ControlPoints.Add(controlPoint); + playfield.HitObjects.AddControlPoint(controlPoint); playfield.NestedPlayfields.ForEach(p => applySpeedAdjustment(controlPoint, p)); }