Add top-level osu! hitobject pooling

This commit is contained in:
smoogipoo 2020-11-11 00:22:06 +09:00
parent 39d37c4779
commit bf72961959
6 changed files with 108 additions and 42 deletions

View File

@ -31,6 +31,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private Container scaleContainer; private Container scaleContainer;
private InputManager inputManager; private InputManager inputManager;
public DrawableHitCircle()
: this(null)
{
}
public DrawableHitCircle([CanBeNull] HitCircle h = null) public DrawableHitCircle([CanBeNull] HitCircle h = null)
: base(h) : base(h)
{ {

View File

@ -41,6 +41,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private Container<DrawableSliderTick> tickContainer; private Container<DrawableSliderTick> tickContainer;
private Container<DrawableSliderRepeat> repeatContainer; private Container<DrawableSliderRepeat> repeatContainer;
public DrawableSlider()
: this(null)
{
}
public DrawableSlider([CanBeNull] Slider s = null) public DrawableSlider([CanBeNull] Slider s = null)
: base(s) : base(s)
{ {

View File

@ -33,6 +33,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private Bindable<bool> isSpinning; private Bindable<bool> isSpinning;
private bool spinnerFrequencyModulate; private bool spinnerFrequencyModulate;
public DrawableSpinner()
: this(null)
{
}
public DrawableSpinner([CanBeNull] Spinner s = null) public DrawableSpinner([CanBeNull] Spinner s = null)
: base(s) : base(s)
{ {

View File

@ -4,12 +4,14 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Pooling;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Input.Handlers; using osu.Game.Input.Handlers;
using osu.Game.Replays; using osu.Game.Replays;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Configuration; using osu.Game.Rulesets.Osu.Configuration;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables;
@ -24,11 +26,28 @@ namespace osu.Game.Rulesets.Osu.UI
{ {
protected new OsuRulesetConfigManager Config => (OsuRulesetConfigManager)base.Config; protected new OsuRulesetConfigManager Config => (OsuRulesetConfigManager)base.Config;
public new OsuPlayfield Playfield => (OsuPlayfield)base.Playfield;
public DrawableOsuRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null) public DrawableOsuRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null)
: base(ruleset, beatmap, mods) : base(ruleset, beatmap, mods)
{ {
} }
protected override bool PoolHitObjects => true;
[BackgroundDependencyLoader]
private void load()
{
RegisterPool<HitCircle, DrawableHitCircle>(10, 100);
RegisterPool<Slider, DrawableSlider>(10, 100);
RegisterPool<Spinner, DrawableSpinner>(2, 20);
}
protected override DrawablePool<TDrawable> CreatePool<TDrawable>(int initialSize, int? maximumSize = null)
=> new OsuDrawablePool<TDrawable>(Playfield.CheckHittable, Playfield.OnHitObjectLoaded, initialSize, maximumSize);
protected override HitObjectLifetimeEntry CreateLifetimeEntry(OsuHitObject hitObject) => new OsuHitObjectLifetimeEntry(hitObject);
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; // always show the gameplay cursor public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; // always show the gameplay cursor
protected override Playfield CreatePlayfield() => new OsuPlayfield(); protected override Playfield CreatePlayfield() => new OsuPlayfield();
@ -39,23 +58,6 @@ namespace osu.Game.Rulesets.Osu.UI
protected override ResumeOverlay CreateResumeOverlay() => new OsuResumeOverlay(); protected override ResumeOverlay CreateResumeOverlay() => new OsuResumeOverlay();
public override DrawableHitObject<OsuHitObject> CreateDrawableRepresentation(OsuHitObject h)
{
switch (h)
{
case HitCircle circle:
return new DrawableHitCircle(circle);
case Slider slider:
return new DrawableSlider(slider);
case Spinner spinner:
return new DrawableSpinner(spinner);
}
return null;
}
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new OsuFramedReplayInputHandler(replay); protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new OsuFramedReplayInputHandler(replay);
protected override ReplayRecorder CreateReplayRecorder(Replay replay) => new OsuReplayRecorder(replay); protected override ReplayRecorder CreateReplayRecorder(Replay replay) => new OsuReplayRecorder(replay);
@ -70,5 +72,15 @@ namespace osu.Game.Rulesets.Osu.UI
return 0; return 0;
} }
} }
private class OsuHitObjectLifetimeEntry : HitObjectLifetimeEntry
{
public OsuHitObjectLifetimeEntry(HitObject hitObject)
: base(hitObject)
{
}
protected override double InitialLifetimeOffset => ((OsuHitObject)HitObject).TimePreempt;
}
} }
} }

View File

@ -0,0 +1,33 @@
// 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.
using System;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Pooling;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables;
namespace osu.Game.Rulesets.Osu.UI
{
public class OsuDrawablePool<T> : DrawablePool<T>
where T : DrawableHitObject, new()
{
private readonly Func<DrawableHitObject, double, bool> checkHittable;
private readonly Action<Drawable> onLoaded;
public OsuDrawablePool(Func<DrawableHitObject, double, bool> checkHittable, Action<Drawable> onLoaded, int initialSize, int? maximumSize = null)
: base(initialSize, maximumSize)
{
this.checkHittable = checkHittable;
this.onLoaded = onLoaded;
}
protected override T CreateNewDrawable() => base.CreateNewDrawable().With(o =>
{
var osuObject = (DrawableOsuHitObject)(object)o;
osuObject.CheckHittable = checkHittable;
osuObject.OnLoadComplete += onLoaded;
});
}
}

View File

@ -13,6 +13,7 @@ using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Configuration; using osu.Game.Rulesets.Osu.Configuration;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Connections; using osu.Game.Rulesets.Osu.Objects.Drawables.Connections;
using osu.Game.Rulesets.Osu.Scoring; using osu.Game.Rulesets.Osu.Scoring;
@ -26,6 +27,8 @@ namespace osu.Game.Rulesets.Osu.UI
{ {
public class OsuPlayfield : Playfield public class OsuPlayfield : Playfield
{ {
public readonly Func<DrawableHitObject, double, bool> CheckHittable;
private readonly PlayfieldBorder playfieldBorder; private readonly PlayfieldBorder playfieldBorder;
private readonly ProxyContainer approachCircles; private readonly ProxyContainer approachCircles;
private readonly ProxyContainer spinnerProxies; private readonly ProxyContainer spinnerProxies;
@ -78,6 +81,7 @@ namespace osu.Game.Rulesets.Osu.UI
}; };
hitPolicy = new OrderedHitPolicy(HitObjectContainer); hitPolicy = new OrderedHitPolicy(HitObjectContainer);
CheckHittable = hitPolicy.IsHittable;
var hitWindows = new OsuHitWindows(); var hitWindows = new OsuHitWindows();
@ -85,6 +89,8 @@ namespace osu.Game.Rulesets.Osu.UI
poolDictionary.Add(result, new DrawableJudgementPool(result)); poolDictionary.Add(result, new DrawableJudgementPool(result));
AddRangeInternal(poolDictionary.Values); AddRangeInternal(poolDictionary.Values);
NewResult += onNewResult;
} }
[BackgroundDependencyLoader(true)] [BackgroundDependencyLoader(true)]
@ -93,37 +99,37 @@ namespace osu.Game.Rulesets.Osu.UI
config?.BindWith(OsuRulesetSetting.PlayfieldBorderStyle, playfieldBorder.PlayfieldBorderStyle); config?.BindWith(OsuRulesetSetting.PlayfieldBorderStyle, playfieldBorder.PlayfieldBorderStyle);
} }
public override void Add(DrawableHitObject h) protected override void OnHitObjectAdded(HitObject hitObject)
{ {
DrawableOsuHitObject osuHitObject = (DrawableOsuHitObject)h; base.OnHitObjectAdded(hitObject);
followPoints.AddFollowPoints((OsuHitObject)hitObject);
h.OnNewResult += onNewResult;
h.OnLoadComplete += d =>
{
if (d is DrawableSpinner)
spinnerProxies.Add(d.CreateProxy());
if (d is IDrawableHitObjectWithProxiedApproach c)
approachCircles.Add(c.ProxiedLayer.CreateProxy());
};
base.Add(h);
osuHitObject.CheckHittable = hitPolicy.IsHittable;
followPoints.AddFollowPoints(osuHitObject.HitObject);
} }
public override bool Remove(DrawableHitObject h) protected override void OnHitObjectRemoved(HitObject hitObject)
{ {
DrawableOsuHitObject osuHitObject = (DrawableOsuHitObject)h; base.OnHitObjectRemoved(hitObject);
followPoints.RemoveFollowPoints((OsuHitObject)hitObject);
}
bool result = base.Remove(h); public void OnHitObjectLoaded(Drawable drawable)
{
switch (drawable)
{
case DrawableSliderHead _:
case DrawableSliderTail _:
case DrawableSliderTick _:
case DrawableSliderRepeat _:
case DrawableSpinnerTick _:
break;
if (result) case DrawableSpinner _:
followPoints.RemoveFollowPoints(osuHitObject.HitObject); spinnerProxies.Add(drawable.CreateProxy());
break;
return result; case IDrawableHitObjectWithProxiedApproach approach:
approachCircles.Add(approach.ProxiedLayer.CreateProxy());
break;
}
} }
private void onNewResult(DrawableHitObject judgedObject, JudgementResult result) private void onNewResult(DrawableHitObject judgedObject, JudgementResult result)