1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-26 19:32:55 +08:00

Merge pull request #13517 from ekrctb/initial-entry

Allow setting `Entry` of `PoolableDrawableWithLifetime` (including `DrawableHitObject`)
This commit is contained in:
Dean Herbert 2021-06-19 00:03:41 +09:00 committed by GitHub
commit 89c27dcb10
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 53 additions and 31 deletions

View File

@ -44,11 +44,9 @@ namespace osu.Game.Tests.Gameplay
{ {
TestDrawableHitObject dho = null; TestDrawableHitObject dho = null;
TestLifetimeEntry entry = null; TestLifetimeEntry entry = null;
AddStep("Create DHO", () => AddStep("Create DHO", () => Child = dho = new TestDrawableHitObject
{ {
dho = new TestDrawableHitObject(null); Entry = entry = new TestLifetimeEntry(new HitObject())
dho.Apply(entry = new TestLifetimeEntry(new HitObject()));
Child = dho;
}); });
AddStep("KeepAlive = true", () => AddStep("KeepAlive = true", () =>
@ -81,12 +79,10 @@ namespace osu.Game.Tests.Gameplay
AddAssert("Lifetime is updated", () => entry.LifetimeStart == -TestLifetimeEntry.INITIAL_LIFETIME_OFFSET); AddAssert("Lifetime is updated", () => entry.LifetimeStart == -TestLifetimeEntry.INITIAL_LIFETIME_OFFSET);
TestDrawableHitObject dho = null; TestDrawableHitObject dho = null;
AddStep("Create DHO", () => AddStep("Create DHO", () => Child = dho = new TestDrawableHitObject
{ {
dho = new TestDrawableHitObject(null); Entry = entry,
dho.Apply(entry); SetLifetimeStartOnApply = true
Child = dho;
dho.SetLifetimeStartOnApply = true;
}); });
AddStep("ApplyDefaults", () => entry.HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty())); AddStep("ApplyDefaults", () => entry.HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()));
AddAssert("Lifetime is correct", () => dho.LifetimeStart == TestDrawableHitObject.LIFETIME_ON_APPLY && entry.LifetimeStart == TestDrawableHitObject.LIFETIME_ON_APPLY); AddAssert("Lifetime is correct", () => dho.LifetimeStart == TestDrawableHitObject.LIFETIME_ON_APPLY && entry.LifetimeStart == TestDrawableHitObject.LIFETIME_ON_APPLY);
@ -97,11 +93,9 @@ namespace osu.Game.Tests.Gameplay
{ {
TestDrawableHitObject dho = null; TestDrawableHitObject dho = null;
TestLifetimeEntry entry = null; TestLifetimeEntry entry = null;
AddStep("Create DHO", () => AddStep("Create DHO", () => Child = dho = new TestDrawableHitObject
{ {
dho = new TestDrawableHitObject(null); Entry = entry = new TestLifetimeEntry(new HitObject())
dho.Apply(entry = new TestLifetimeEntry(new HitObject()));
Child = dho;
}); });
AddStep("Set entry lifetime", () => AddStep("Set entry lifetime", () =>
@ -135,7 +129,7 @@ namespace osu.Game.Tests.Gameplay
public bool SetLifetimeStartOnApply; public bool SetLifetimeStartOnApply;
public TestDrawableHitObject(HitObject hitObject) public TestDrawableHitObject(HitObject hitObject = null)
: base(hitObject) : base(hitObject)
{ {
} }

View File

@ -156,9 +156,10 @@ namespace osu.Game.Rulesets.Objects.Drawables
/// If <c>null</c>, a hitobject is expected to be later applied via <see cref="PoolableDrawableWithLifetime{TEntry}.Apply"/> (or automatically via pooling). /// If <c>null</c>, a hitobject is expected to be later applied via <see cref="PoolableDrawableWithLifetime{TEntry}.Apply"/> (or automatically via pooling).
/// </param> /// </param>
protected DrawableHitObject([CanBeNull] HitObject initialHitObject = null) protected DrawableHitObject([CanBeNull] HitObject initialHitObject = null)
: base(initialHitObject != null ? new SyntheticHitObjectEntry(initialHitObject) : null)
{ {
if (Entry != null) if (initialHitObject == null) return;
Entry = new SyntheticHitObjectEntry(initialHitObject);
ensureEntryHasResult(); ensureEntryHasResult();
} }

View File

@ -5,6 +5,7 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Performance; using osu.Framework.Graphics.Performance;
using osu.Framework.Graphics.Pooling; using osu.Framework.Graphics.Pooling;
@ -16,14 +17,32 @@ namespace osu.Game.Rulesets.Objects.Pooling
/// <typeparam name="TEntry">The <see cref="LifetimeEntry"/> type storing state and controlling this drawable.</typeparam> /// <typeparam name="TEntry">The <see cref="LifetimeEntry"/> type storing state and controlling this drawable.</typeparam>
public abstract class PoolableDrawableWithLifetime<TEntry> : PoolableDrawable where TEntry : LifetimeEntry public abstract class PoolableDrawableWithLifetime<TEntry> : PoolableDrawable where TEntry : LifetimeEntry
{ {
private TEntry? entry;
/// <summary> /// <summary>
/// The entry holding essential state of this <see cref="PoolableDrawableWithLifetime{TEntry}"/>. /// The entry holding essential state of this <see cref="PoolableDrawableWithLifetime{TEntry}"/>.
/// </summary> /// </summary>
public TEntry? Entry { get; private set; } /// <remarks>
/// If a non-null value is set before loading is started, the entry is applied when the loading is completed.
/// It is not valid to set an entry while this <see cref="PoolableDrawableWithLifetime{TEntry}"/> is loading.
/// </remarks>
public TEntry? Entry
{
get => entry;
set
{
if (LoadState == LoadState.NotLoaded)
entry = value;
else if (value != null)
Apply(value);
else if (HasEntryApplied)
free();
}
}
/// <summary> /// <summary>
/// Whether <see cref="Entry"/> is applied to this <see cref="PoolableDrawableWithLifetime{TEntry}"/>. /// Whether <see cref="Entry"/> is applied to this <see cref="PoolableDrawableWithLifetime{TEntry}"/>.
/// When an initial entry is specified in the constructor, <see cref="Entry"/> is set but not applied until loading is completed. /// When an <see cref="Entry"/> is set during initialization, it is not applied until loading is completed.
/// </summary> /// </summary>
protected bool HasEntryApplied { get; private set; } protected bool HasEntryApplied { get; private set; }
@ -65,9 +84,9 @@ namespace osu.Game.Rulesets.Objects.Pooling
{ {
base.LoadAsyncComplete(); base.LoadAsyncComplete();
// Apply the initial entry given in the constructor. // Apply the initial entry.
if (Entry != null && !HasEntryApplied) if (Entry != null && !HasEntryApplied)
Apply(Entry); apply(Entry);
} }
/// <summary> /// <summary>
@ -76,16 +95,10 @@ namespace osu.Game.Rulesets.Objects.Pooling
/// </summary> /// </summary>
public void Apply(TEntry entry) public void Apply(TEntry entry)
{ {
if (HasEntryApplied) if (LoadState == LoadState.Loading)
free(); throw new InvalidOperationException($"Cannot apply a new {nameof(TEntry)} while currently loading.");
Entry = entry; apply(entry);
entry.LifetimeChanged += setLifetimeFromEntry;
setLifetimeFromEntry(entry);
OnApply(entry);
HasEntryApplied = true;
} }
protected sealed override void FreeAfterUse() protected sealed override void FreeAfterUse()
@ -111,6 +124,20 @@ namespace osu.Game.Rulesets.Objects.Pooling
{ {
} }
private void apply(TEntry entry)
{
if (HasEntryApplied)
free();
this.entry = entry;
entry.LifetimeChanged += setLifetimeFromEntry;
setLifetimeFromEntry(entry);
OnApply(entry);
HasEntryApplied = true;
}
private void free() private void free()
{ {
Debug.Assert(Entry != null && HasEntryApplied); Debug.Assert(Entry != null && HasEntryApplied);
@ -118,7 +145,7 @@ namespace osu.Game.Rulesets.Objects.Pooling
OnFree(Entry); OnFree(Entry);
Entry.LifetimeChanged -= setLifetimeFromEntry; Entry.LifetimeChanged -= setLifetimeFromEntry;
Entry = null; entry = null;
base.LifetimeStart = double.MinValue; base.LifetimeStart = double.MinValue;
base.LifetimeEnd = double.MaxValue; base.LifetimeEnd = double.MaxValue;