diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneFollowPoints.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneFollowPoints.cs
index 9587c721c6..3c447e5009 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneFollowPoints.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneFollowPoints.cs
@@ -4,13 +4,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using JetBrains.Annotations;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
-using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Connections;
@@ -184,209 +182,5 @@ namespace osu.Game.Rulesets.Osu.Tests
followPointRenderer.RemoveFollowPoints(drawableObject);
});
}
-
- private class FollowPointRenderer : CompositeDrawable
- {
- ///
- /// Adds the s around a .
- /// This includes s leading into , and s exiting .
- ///
- /// The to add s for.
- public void AddFollowPoints(DrawableOsuHitObject hitObject)
- {
- var startGroup = new FollowPointGroup(hitObject);
- AddInternal(startGroup);
-
- // Groups are sorted by their start time when added, so the index can be used to post-process other surrounding groups
- int startIndex = IndexOfInternal(startGroup);
-
- if (startIndex < InternalChildren.Count - 1)
- {
- // h1 -> -> -> h2
- // hitObject nextGroup
-
- var nextGroup = (FollowPointGroup)InternalChildren[startIndex + 1];
- startGroup.End = nextGroup.Start;
- }
-
- if (startIndex > 0)
- {
- // h1 -> -> -> h2
- // prevGroup hitObject
-
- var previousGroup = (FollowPointGroup)InternalChildren[startIndex - 1];
- previousGroup.End = startGroup.Start;
- }
- }
-
- ///
- /// Removes the s around a .
- /// This includes s leading into , and s exiting .
- ///
- /// The to remove s for.
- public void RemoveFollowPoints(DrawableOsuHitObject hitObject)
- {
- var groups = findGroups(hitObject);
-
- // Regardless of the position of the hitobject in the beatmap, there will always be a group leading from the hitobject
- RemoveInternal(groups.start);
-
- if (groups.end != null)
- {
- // When there were two groups referencing the same hitobject, merge them by updating the end group to point to the new end (the start group was already removed)
- groups.end.End = groups.start.End;
- }
- }
-
- ///
- /// Finds the s with as the start and end s.
- ///
- /// The to find the relevant of.
- /// A tuple containing the end group (the where is the end of),
- /// and the start group (the where is the start of).
- private (FollowPointGroup start, FollowPointGroup end) findGroups(DrawableOsuHitObject hitObject)
- {
- // endGroup startGroup
- // h1 -> -> -> -> -> h2 -> -> -> -> -> h3
- // hitObject
-
- FollowPointGroup startGroup = null; // The group which the hitobject is the start in
- FollowPointGroup endGroup = null; // The group which the hitobject is the end in
-
- int startIndex = 0;
-
- for (; startIndex < InternalChildren.Count; startIndex++)
- {
- var group = (FollowPointGroup)InternalChildren[startIndex];
-
- if (group.Start == hitObject)
- {
- startGroup = group;
- break;
- }
- }
-
- if (startIndex > 0)
- endGroup = (FollowPointGroup)InternalChildren[startIndex - 1];
-
- return (startGroup, endGroup);
- }
-
- protected override int Compare(Drawable x, Drawable y)
- {
- var groupX = (FollowPointGroup)x;
- var groupY = (FollowPointGroup)y;
-
- return groupX.Start.HitObject.StartTime.CompareTo(groupY.Start.HitObject.StartTime);
- }
- }
-
- private class FollowPointGroup : CompositeDrawable
- {
- // Todo: These shouldn't be constants
- private const int spacing = 32;
- private const double preempt = 800;
-
- ///
- /// The which s will exit from.
- ///
- [NotNull]
- public readonly DrawableOsuHitObject Start;
-
- public FollowPointGroup(DrawableOsuHitObject start)
- {
- Start = start;
- RelativeSizeAxes = Axes.Both;
- }
-
- protected override void LoadComplete()
- {
- base.LoadComplete();
- bindHitObject(Start);
- }
-
- private DrawableOsuHitObject end;
-
- ///
- /// The which s will enter.
- ///
- [CanBeNull]
- public DrawableOsuHitObject End
- {
- get => end;
- set
- {
- end = value;
-
- if (end != null)
- bindHitObject(end);
-
- refreshFollowPoints();
- }
- }
-
- private void bindHitObject(DrawableOsuHitObject drawableObject)
- {
- drawableObject.HitObject.StartTimeBindable.BindValueChanged(_ => refreshFollowPoints());
- drawableObject.HitObject.PositionBindable.BindValueChanged(_ => refreshFollowPoints());
- drawableObject.HitObject.DefaultsApplied += refreshFollowPoints;
- }
-
- private void refreshFollowPoints()
- {
- ClearInternal();
-
- if (End == null)
- return;
-
- OsuHitObject osuStart = Start.HitObject;
- OsuHitObject osuEnd = End.HitObject;
-
- if (osuEnd.NewCombo)
- return;
-
- if (osuStart is Spinner || osuEnd is Spinner)
- return;
-
- Vector2 startPosition = osuStart.EndPosition;
- Vector2 endPosition = osuEnd.Position;
- double startTime = (osuStart as IHasEndTime)?.EndTime ?? osuStart.StartTime;
- double endTime = osuEnd.StartTime;
-
- Vector2 distanceVector = endPosition - startPosition;
- int distance = (int)distanceVector.Length;
- float rotation = (float)(Math.Atan2(distanceVector.Y, distanceVector.X) * (180 / Math.PI));
- double duration = endTime - startTime;
-
- for (int d = (int)(spacing * 1.5); d < distance - spacing; d += spacing)
- {
- float fraction = (float)d / distance;
- Vector2 pointStartPosition = startPosition + (fraction - 0.1f) * distanceVector;
- Vector2 pointEndPosition = startPosition + fraction * distanceVector;
- double fadeOutTime = startTime + fraction * duration;
- double fadeInTime = fadeOutTime - preempt;
-
- FollowPoint fp;
-
- AddInternal(fp = new FollowPoint
- {
- Position = pointStartPosition,
- Rotation = rotation,
- Alpha = 0,
- Scale = new Vector2(1.5f * osuEnd.Scale),
- });
-
- using (fp.BeginAbsoluteSequence(fadeInTime))
- {
- fp.FadeIn(osuEnd.TimeFadeIn);
- fp.ScaleTo(osuEnd.Scale, osuEnd.TimeFadeIn, Easing.Out);
- fp.MoveTo(pointEndPosition, osuEnd.TimeFadeIn, Easing.Out);
- fp.Delay(fadeOutTime - fadeInTime).FadeOut(osuEnd.TimeFadeIn);
- }
-
- fp.Expire(true);
- }
- }
- }
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointGroup.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointGroup.cs
new file mode 100644
index 0000000000..06aadcc342
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointGroup.cs
@@ -0,0 +1,120 @@
+// 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 JetBrains.Annotations;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Rulesets.Objects.Types;
+using osuTK;
+
+namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
+{
+ public class FollowPointGroup : CompositeDrawable
+ {
+ // Todo: These shouldn't be constants
+ private const int spacing = 32;
+ private const double preempt = 800;
+
+ ///
+ /// The which s will exit from.
+ ///
+ [NotNull]
+ public readonly DrawableOsuHitObject Start;
+
+ public FollowPointGroup(DrawableOsuHitObject start)
+ {
+ Start = start;
+ RelativeSizeAxes = Axes.Both;
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+ bindHitObject(Start);
+ }
+
+ private DrawableOsuHitObject end;
+
+ ///
+ /// The which s will enter.
+ ///
+ [CanBeNull]
+ public DrawableOsuHitObject End
+ {
+ get => end;
+ set
+ {
+ end = value;
+
+ if (end != null)
+ bindHitObject(end);
+
+ refreshFollowPoints();
+ }
+ }
+
+ private void bindHitObject(DrawableOsuHitObject drawableObject)
+ {
+ drawableObject.HitObject.StartTimeBindable.BindValueChanged(_ => refreshFollowPoints());
+ drawableObject.HitObject.PositionBindable.BindValueChanged(_ => refreshFollowPoints());
+ drawableObject.HitObject.DefaultsApplied += refreshFollowPoints;
+ }
+
+ private void refreshFollowPoints()
+ {
+ ClearInternal();
+
+ if (End == null)
+ return;
+
+ OsuHitObject osuStart = Start.HitObject;
+ OsuHitObject osuEnd = End.HitObject;
+
+ if (osuEnd.NewCombo)
+ return;
+
+ if (osuStart is Spinner || osuEnd is Spinner)
+ return;
+
+ Vector2 startPosition = osuStart.EndPosition;
+ Vector2 endPosition = osuEnd.Position;
+ double startTime = (osuStart as IHasEndTime)?.EndTime ?? osuStart.StartTime;
+ double endTime = osuEnd.StartTime;
+
+ Vector2 distanceVector = endPosition - startPosition;
+ int distance = (int)distanceVector.Length;
+ float rotation = (float)(Math.Atan2(distanceVector.Y, distanceVector.X) * (180 / Math.PI));
+ double duration = endTime - startTime;
+
+ for (int d = (int)(spacing * 1.5); d < distance - spacing; d += spacing)
+ {
+ float fraction = (float)d / distance;
+ Vector2 pointStartPosition = startPosition + (fraction - 0.1f) * distanceVector;
+ Vector2 pointEndPosition = startPosition + fraction * distanceVector;
+ double fadeOutTime = startTime + fraction * duration;
+ double fadeInTime = fadeOutTime - preempt;
+
+ FollowPoint fp;
+
+ AddInternal(fp = new FollowPoint
+ {
+ Position = pointStartPosition,
+ Rotation = rotation,
+ Alpha = 0,
+ Scale = new Vector2(1.5f * osuEnd.Scale),
+ });
+
+ using (fp.BeginAbsoluteSequence(fadeInTime))
+ {
+ fp.FadeIn(osuEnd.TimeFadeIn);
+ fp.ScaleTo(osuEnd.Scale, osuEnd.TimeFadeIn, Easing.Out);
+ fp.MoveTo(pointEndPosition, osuEnd.TimeFadeIn, Easing.Out);
+ fp.Delay(fadeOutTime - fadeInTime).FadeOut(osuEnd.TimeFadeIn);
+ }
+
+ fp.Expire(true);
+ }
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs
index 863ce869aa..47c35746f0 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs
@@ -1,122 +1,104 @@
// 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 System.Collections.Generic;
-using osuTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
{
public class FollowPointRenderer : CompositeDrawable
{
- private int pointDistance = 32;
+ ///
+ /// Adds the s around a .
+ /// This includes s leading into , and s exiting .
+ ///
+ /// The to add s for.
+ public void AddFollowPoints(DrawableOsuHitObject hitObject)
+ {
+ var startGroup = new FollowPointGroup(hitObject);
+ AddInternal(startGroup);
+
+ // Groups are sorted by their start time when added, so the index can be used to post-process other surrounding groups
+ int startIndex = IndexOfInternal(startGroup);
+
+ if (startIndex < InternalChildren.Count - 1)
+ {
+ // h1 -> -> -> h2
+ // hitObject nextGroup
+
+ var nextGroup = (FollowPointGroup)InternalChildren[startIndex + 1];
+ startGroup.End = nextGroup.Start;
+ }
+
+ if (startIndex > 0)
+ {
+ // h1 -> -> -> h2
+ // prevGroup hitObject
+
+ var previousGroup = (FollowPointGroup)InternalChildren[startIndex - 1];
+ previousGroup.End = startGroup.Start;
+ }
+ }
///
- /// Determines how much space there is between points.
+ /// Removes the s around a .
+ /// This includes s leading into , and s exiting .
///
- public int PointDistance
+ /// The to remove s for.
+ public void RemoveFollowPoints(DrawableOsuHitObject hitObject)
{
- get => pointDistance;
- set
- {
- if (pointDistance == value) return;
+ var groups = findGroups(hitObject);
- pointDistance = value;
- update();
+ // Regardless of the position of the hitobject in the beatmap, there will always be a group leading from the hitobject
+ RemoveInternal(groups.start);
+
+ if (groups.end != null)
+ {
+ // When there were two groups referencing the same hitobject, merge them by updating the end group to point to the new end (the start group was already removed)
+ groups.end.End = groups.start.End;
}
}
- private int preEmpt = 800;
-
///
- /// Follow points to the next hitobject start appearing for this many milliseconds before an hitobject's end time.
+ /// Finds the s with as the start and end s.
///
- public int PreEmpt
+ /// The to find the relevant of.
+ /// A tuple containing the end group (the where is the end of),
+ /// and the start group (the where is the start of).
+ private (FollowPointGroup start, FollowPointGroup end) findGroups(DrawableOsuHitObject hitObject)
{
- get => preEmpt;
- set
+ // endGroup startGroup
+ // h1 -> -> -> -> -> h2 -> -> -> -> -> h3
+ // hitObject
+
+ FollowPointGroup startGroup = null; // The group which the hitobject is the start in
+ FollowPointGroup endGroup = null; // The group which the hitobject is the end in
+
+ int startIndex = 0;
+
+ for (; startIndex < InternalChildren.Count; startIndex++)
{
- if (preEmpt == value) return;
+ var group = (FollowPointGroup)InternalChildren[startIndex];
- preEmpt = value;
- update();
- }
- }
-
- private IEnumerable hitObjects;
-
- public IEnumerable HitObjects
- {
- get => hitObjects;
- set
- {
- hitObjects = value;
- update();
- }
- }
-
- public override bool RemoveCompletedTransforms => false;
-
- private void update()
- {
- ClearInternal();
-
- if (hitObjects == null)
- return;
-
- OsuHitObject prevHitObject = null;
-
- foreach (var currHitObject in hitObjects)
- {
- if (prevHitObject != null && !currHitObject.NewCombo && !(prevHitObject is Spinner) && !(currHitObject is Spinner))
+ if (group.Start == hitObject)
{
- Vector2 startPosition = prevHitObject.EndPosition;
- Vector2 endPosition = currHitObject.Position;
- double startTime = (prevHitObject as IHasEndTime)?.EndTime ?? prevHitObject.StartTime;
- double endTime = currHitObject.StartTime;
-
- Vector2 distanceVector = endPosition - startPosition;
- int distance = (int)distanceVector.Length;
- float rotation = (float)(Math.Atan2(distanceVector.Y, distanceVector.X) * (180 / Math.PI));
- double duration = endTime - startTime;
-
- for (int d = (int)(PointDistance * 1.5); d < distance - PointDistance; d += PointDistance)
- {
- float fraction = (float)d / distance;
- Vector2 pointStartPosition = startPosition + (fraction - 0.1f) * distanceVector;
- Vector2 pointEndPosition = startPosition + fraction * distanceVector;
- double fadeOutTime = startTime + fraction * duration;
- double fadeInTime = fadeOutTime - PreEmpt;
-
- FollowPoint fp;
-
- AddInternal(fp = new FollowPoint
- {
- Position = pointStartPosition,
- Rotation = rotation,
- Alpha = 0,
- Scale = new Vector2(1.5f * currHitObject.Scale),
- });
-
- using (fp.BeginAbsoluteSequence(fadeInTime))
- {
- fp.FadeIn(currHitObject.TimeFadeIn);
- fp.ScaleTo(currHitObject.Scale, currHitObject.TimeFadeIn, Easing.Out);
-
- fp.MoveTo(pointEndPosition, currHitObject.TimeFadeIn, Easing.Out);
-
- fp.Delay(fadeOutTime - fadeInTime).FadeOut(currHitObject.TimeFadeIn);
- }
-
- fp.Expire(true);
- }
+ startGroup = group;
+ break;
}
-
- prevHitObject = currHitObject;
}
+
+ if (startIndex > 0)
+ endGroup = (FollowPointGroup)InternalChildren[startIndex - 1];
+
+ return (startGroup, endGroup);
+ }
+
+ protected override int Compare(Drawable x, Drawable y)
+ {
+ var groupX = (FollowPointGroup)x;
+ var groupY = (FollowPointGroup)y;
+
+ return groupX.Start.HitObject.StartTime.CompareTo(groupY.Start.HitObject.StartTime);
}
}
}
diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs
index cbb29ce387..6d1ea4bbfc 100644
--- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs
+++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs
@@ -9,7 +9,6 @@ using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Connections;
using osu.Game.Rulesets.UI;
-using System.Linq;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu.UI.Cursor;
using osu.Game.Skinning;
@@ -20,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.UI
{
private readonly ApproachCircleProxyContainer approachCircles;
private readonly JudgementContainer judgementLayer;
- private readonly FollowPointRenderer connectionLayer;
+ private readonly FollowPointRenderer followPoints;
public static readonly Vector2 BASE_SIZE = new Vector2(512, 384);
@@ -30,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.UI
{
InternalChildren = new Drawable[]
{
- connectionLayer = new FollowPointRenderer
+ followPoints = new FollowPointRenderer
{
RelativeSizeAxes = Axes.Both,
Depth = 2,
@@ -64,11 +63,18 @@ namespace osu.Game.Rulesets.Osu.UI
};
base.Add(h);
+
+ followPoints.AddFollowPoints((DrawableOsuHitObject)h);
}
- public override void PostProcess()
+ public override bool Remove(DrawableHitObject h)
{
- connectionLayer.HitObjects = HitObjectContainer.Objects.Select(d => d.HitObject).OfType();
+ bool result = base.Remove(h);
+
+ if (result)
+ followPoints.RemoveFollowPoints((DrawableOsuHitObject)h);
+
+ return result;
}
private void onNewResult(DrawableHitObject judgedObject, JudgementResult result)