1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-16 15:37:44 +08:00
osu-lazer/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs

149 lines
5.4 KiB
C#
Raw Normal View History

// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
2018-04-13 17:19:50 +08:00
2022-06-17 15:37:17 +08:00
#nullable disable
using System.Collections.Generic;
using osu.Framework.Allocation;
2020-11-20 13:10:28 +08:00
using osu.Framework.Bindables;
using osu.Framework.Extensions;
2018-04-13 17:19:50 +08:00
using osu.Framework.Graphics;
using osu.Framework.Graphics.Pooling;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Pooling;
2018-04-13 17:19:50 +08:00
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
{
2019-11-05 22:20:46 +08:00
/// <summary>
2019-11-06 15:33:42 +08:00
/// Visualises connections between <see cref="DrawableOsuHitObject"/>s.
2019-11-05 22:20:46 +08:00
/// </summary>
2022-11-24 13:32:20 +08:00
public partial class FollowPointRenderer : PooledDrawableWithLifetimeContainer<FollowPointLifetimeEntry, FollowPointConnection>
2018-04-13 17:19:50 +08:00
{
public new IReadOnlyList<FollowPointLifetimeEntry> Entries => lifetimeEntries;
2020-11-20 12:54:41 +08:00
private DrawablePool<FollowPointConnection> connectionPool;
private DrawablePool<FollowPoint> pointPool;
private readonly List<FollowPointLifetimeEntry> lifetimeEntries = new List<FollowPointLifetimeEntry>();
2020-11-20 13:10:28 +08:00
private readonly Dictionary<HitObject, IBindable> startTimeMap = new Dictionary<HitObject, IBindable>();
[BackgroundDependencyLoader]
private void load()
2018-04-13 17:19:50 +08:00
{
InternalChildren = new Drawable[]
{
connectionPool = new DrawablePool<FollowPointConnection>(10, 200),
pointPool = new DrawablePool<FollowPoint>(50, 1000)
};
}
2020-11-20 14:39:25 +08:00
public void AddFollowPoints(OsuHitObject hitObject)
2020-11-20 13:10:28 +08:00
{
addEntry(hitObject);
var startTimeBindable = hitObject.StartTimeBindable.GetBoundCopy();
startTimeBindable.ValueChanged += _ => onStartTimeChanged(hitObject);
startTimeMap[hitObject] = startTimeBindable;
}
2020-11-20 14:39:25 +08:00
public void RemoveFollowPoints(OsuHitObject hitObject)
2020-11-20 13:10:28 +08:00
{
removeEntry(hitObject);
startTimeMap[hitObject].UnbindAll();
startTimeMap.Remove(hitObject);
}
private void addEntry(OsuHitObject hitObject)
{
var newEntry = new FollowPointLifetimeEntry(hitObject);
int index = lifetimeEntries.AddInPlace(newEntry, Comparer<FollowPointLifetimeEntry>.Create((e1, e2) =>
{
int comp = e1.Start.StartTime.CompareTo(e2.Start.StartTime);
if (comp != 0)
return comp;
// we always want to insert the new item after equal ones.
// this is important for beatmaps with multiple hitobjects at the same point in time.
// if we use standard comparison insert order, there will be a churn of connections getting re-updated to
// the next object at the point-in-time, adding a construction/disposal overhead (see FollowPointConnection.End implementation's ClearInternal).
// this is easily visible on https://osu.ppy.sh/beatmapsets/150945#osu/372245
return -1;
}));
if (index < lifetimeEntries.Count - 1)
2018-04-13 17:19:50 +08:00
{
2019-11-06 15:33:42 +08:00
// Update the connection's end point to the next connection's start point
// h1 -> -> -> h2
2019-11-06 15:33:42 +08:00
// connection nextGroup
FollowPointLifetimeEntry nextEntry = lifetimeEntries[index + 1];
newEntry.End = nextEntry.Start;
2018-04-13 17:19:50 +08:00
}
else
2019-11-05 22:20:46 +08:00
{
// The end point may be non-null during re-ordering
newEntry.End = null;
2019-11-05 22:20:46 +08:00
}
if (index > 0)
2018-04-13 17:19:50 +08:00
{
2019-11-06 15:33:42 +08:00
// Update the previous connection's end point to the current connection's start point
// h1 -> -> -> h2
2019-11-06 15:33:42 +08:00
// prevGroup connection
FollowPointLifetimeEntry previousEntry = lifetimeEntries[index - 1];
previousEntry.End = newEntry.Start;
2018-04-13 17:19:50 +08:00
}
Add(newEntry);
2018-04-13 17:19:50 +08:00
}
2020-11-20 13:10:28 +08:00
private void removeEntry(OsuHitObject hitObject)
2018-04-13 17:19:50 +08:00
{
int index = lifetimeEntries.FindIndex(e => e.Start == hitObject);
var entry = lifetimeEntries[index];
entry.UnbindEvents();
2018-04-13 17:19:50 +08:00
lifetimeEntries.RemoveAt(index);
Remove(entry);
2019-04-01 11:16:05 +08:00
if (index > 0)
2018-04-13 17:19:50 +08:00
{
2019-11-06 15:33:42 +08:00
// Update the previous connection's end point to the next connection's start point
// h1 -> -> -> h2 -> -> -> h3
2019-11-06 15:33:42 +08:00
// prevGroup connection nextGroup
// The current connection's end point is used since there may not be a next connection
FollowPointLifetimeEntry previousEntry = lifetimeEntries[index - 1];
previousEntry.End = entry.End;
2018-04-13 17:19:50 +08:00
}
}
protected override FollowPointConnection GetDrawable(FollowPointLifetimeEntry entry)
{
var connection = connectionPool.Get();
connection.Pool = pointPool;
connection.Apply(entry);
return connection;
}
2020-11-20 13:10:28 +08:00
private void onStartTimeChanged(OsuHitObject hitObject)
{
removeEntry(hitObject);
addEntry(hitObject);
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
foreach (var entry in lifetimeEntries)
entry.UnbindEvents();
lifetimeEntries.Clear();
}
2018-04-13 17:19:50 +08:00
}
}