mirror of
https://github.com/ppy/osu.git
synced 2025-02-20 06:12:55 +08:00
Add hitobject lifetime support
This commit is contained in:
parent
45e9f16f6b
commit
6f3f6dc28b
@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
Position = new Vector2(128, 128),
|
||||
ComboIndex = 1,
|
||||
})));
|
||||
}), null));
|
||||
}
|
||||
|
||||
private HitCircle prepareObject(HitCircle circle)
|
||||
|
@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
new Vector2(300, 0),
|
||||
}),
|
||||
RepeatCount = 1
|
||||
})));
|
||||
}), null));
|
||||
}
|
||||
|
||||
private Slider prepareObject(Slider slider)
|
||||
|
@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
Position = new Vector2(256, 192),
|
||||
ComboIndex = 1,
|
||||
Duration = 1000,
|
||||
})));
|
||||
}), null));
|
||||
}
|
||||
|
||||
private Spinner prepareObject(Spinner circle)
|
||||
|
@ -120,6 +120,12 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
/// </summary>
|
||||
private bool hasHitObjectApplied;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="HitObjectLifetimeEntry"/> controlling the lifetime of the currently-attached <see cref="HitObject"/>.
|
||||
/// </summary>
|
||||
[CanBeNull]
|
||||
private HitObjectLifetimeEntry lifetimeEntry;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="DrawableHitObject"/>.
|
||||
/// </summary>
|
||||
@ -143,7 +149,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
base.LoadAsyncComplete();
|
||||
|
||||
if (HitObject != null)
|
||||
Apply(HitObject);
|
||||
Apply(HitObject, lifetimeEntry);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
@ -160,16 +166,33 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
/// Applies a new <see cref="HitObject"/> to be represented by this <see cref="DrawableHitObject"/>.
|
||||
/// </summary>
|
||||
/// <param name="hitObject">The <see cref="HitObject"/> to apply.</param>
|
||||
public void Apply(HitObject hitObject)
|
||||
/// <param name="lifetimeEntry">The <see cref="HitObjectLifetimeEntry"/> controlling the lifetime of <paramref name="hitObject"/>.</param>
|
||||
public void Apply([NotNull] HitObject hitObject, [CanBeNull] HitObjectLifetimeEntry lifetimeEntry)
|
||||
{
|
||||
free();
|
||||
|
||||
HitObject = hitObject ?? throw new InvalidOperationException($"Cannot apply a null {nameof(HitObject)}.");
|
||||
|
||||
this.lifetimeEntry = lifetimeEntry;
|
||||
|
||||
if (lifetimeEntry != null)
|
||||
{
|
||||
// Transfer lifetime from the entry.
|
||||
LifetimeStart = lifetimeEntry.LifetimeStart;
|
||||
LifetimeEnd = lifetimeEntry.LifetimeEnd;
|
||||
|
||||
// Copy any existing result from the entry (required for rewind / judgement revert).
|
||||
Result = lifetimeEntry.Result;
|
||||
}
|
||||
|
||||
// Ensure this DHO has a result.
|
||||
Result ??= CreateResult(HitObject.CreateJudgement())
|
||||
?? throw new InvalidOperationException($"{GetType().ReadableName()} must provide a {nameof(JudgementResult)} through {nameof(CreateResult)}.");
|
||||
|
||||
// Copy back the result to the entry for potential future retrieval.
|
||||
if (lifetimeEntry != null)
|
||||
lifetimeEntry.Result = Result;
|
||||
|
||||
foreach (var h in HitObject.NestedHitObjects)
|
||||
{
|
||||
var drawableNested = CreateNestedHitObject(h) ?? throw new InvalidOperationException($"{nameof(CreateNestedHitObject)} returned null for {h.GetType().ReadableName()}.");
|
||||
@ -302,7 +325,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
|
||||
private void onDefaultsApplied(HitObject hitObject)
|
||||
{
|
||||
Apply(hitObject);
|
||||
Apply(hitObject, lifetimeEntry);
|
||||
DefaultsApplied?.Invoke(this);
|
||||
}
|
||||
|
||||
@ -549,15 +572,27 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
/// </remarks>
|
||||
protected internal new ScheduledDelegate Schedule(Action action) => base.Schedule(action);
|
||||
|
||||
private double? lifetimeStart;
|
||||
|
||||
public override double LifetimeStart
|
||||
{
|
||||
get => lifetimeStart ?? (HitObject.StartTime - InitialLifetimeOffset);
|
||||
set
|
||||
get => base.LifetimeStart;
|
||||
set => setLifetime(value, LifetimeEnd);
|
||||
}
|
||||
|
||||
public override double LifetimeEnd
|
||||
{
|
||||
get => base.LifetimeEnd;
|
||||
set => setLifetime(LifetimeStart, value);
|
||||
}
|
||||
|
||||
private void setLifetime(double lifetimeStart, double lifetimeEnd)
|
||||
{
|
||||
base.LifetimeStart = lifetimeStart;
|
||||
base.LifetimeEnd = lifetimeEnd;
|
||||
|
||||
if (lifetimeEntry != null)
|
||||
{
|
||||
lifetimeStart = value;
|
||||
base.LifetimeStart = value;
|
||||
lifetimeEntry.LifetimeStart = lifetimeStart;
|
||||
lifetimeEntry.LifetimeEnd = lifetimeEnd;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,9 @@
|
||||
// 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 osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Performance;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.Objects
|
||||
@ -16,6 +18,14 @@ namespace osu.Game.Rulesets.Objects
|
||||
/// </summary>
|
||||
public readonly HitObject HitObject;
|
||||
|
||||
/// <summary>
|
||||
/// The result that <see cref="HitObject"/> was judged with.
|
||||
/// This is set by the accompanying <see cref="DrawableHitObject"/>, and reused when required for rewinding.
|
||||
/// </summary>
|
||||
internal JudgementResult Result;
|
||||
|
||||
private readonly IBindable<double> startTimeBindable = new BindableDouble();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="HitObjectLifetimeEntry"/>.
|
||||
/// </summary>
|
||||
@ -23,7 +33,9 @@ namespace osu.Game.Rulesets.Objects
|
||||
public HitObjectLifetimeEntry(HitObject hitObject)
|
||||
{
|
||||
HitObject = hitObject;
|
||||
ResetLifetimeStart();
|
||||
|
||||
startTimeBindable.BindTo(HitObject.StartTimeBindable);
|
||||
startTimeBindable.BindValueChanged(onStartTimeChanged, true);
|
||||
}
|
||||
|
||||
// The lifetime start, as set by the hitobject.
|
||||
@ -91,8 +103,8 @@ namespace osu.Game.Rulesets.Objects
|
||||
protected virtual double InitialLifetimeOffset => 10000;
|
||||
|
||||
/// <summary>
|
||||
/// Resets <see cref="LifetimeStart"/> according to the start time of the <see cref="HitObject"/>.
|
||||
/// Resets <see cref="LifetimeStart"/> according to the change in start time of the <see cref="HitObject"/>.
|
||||
/// </summary>
|
||||
internal void ResetLifetimeStart() => LifetimeStart = HitObject.StartTime - InitialLifetimeOffset;
|
||||
private void onStartTimeChanged(ValueChangedEvent<double> startTime) => LifetimeStart = HitObject.StartTime - InitialLifetimeOffset;
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ using System.Threading;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Extensions.TypeExtensions;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.Pooling;
|
||||
using osu.Framework.Input;
|
||||
@ -246,6 +247,16 @@ namespace osu.Game.Rulesets.UI
|
||||
Playfield.Add(drawableObject);
|
||||
}
|
||||
|
||||
protected sealed override HitObjectLifetimeEntry CreateLifetimeEntry(HitObject hitObject)
|
||||
{
|
||||
if (!(hitObject is TObject tHitObject))
|
||||
throw new InvalidOperationException($"Unexpected hitobject type: {hitObject.GetType().ReadableName()}");
|
||||
|
||||
return CreateLifetimeEntry(tHitObject);
|
||||
}
|
||||
|
||||
protected virtual HitObjectLifetimeEntry CreateLifetimeEntry(TObject hitObject) => new HitObjectLifetimeEntry(hitObject);
|
||||
|
||||
public override void SetRecordTarget(Replay recordingReplay)
|
||||
{
|
||||
if (!(KeyBindingInputManager is IHasRecordingHandler recordingInputManager))
|
||||
@ -564,9 +575,21 @@ namespace osu.Game.Rulesets.UI
|
||||
m.ApplyToDrawableHitObjects(dho.Yield());
|
||||
}
|
||||
|
||||
dho.Apply(hitObject);
|
||||
dho.Apply(hitObject, GetLifetimeEntry(hitObject));
|
||||
});
|
||||
}
|
||||
|
||||
protected abstract HitObjectLifetimeEntry CreateLifetimeEntry(HitObject hitObject);
|
||||
|
||||
private readonly Dictionary<HitObject, HitObjectLifetimeEntry> lifetimeEntries = new Dictionary<HitObject, HitObjectLifetimeEntry>();
|
||||
|
||||
protected HitObjectLifetimeEntry GetLifetimeEntry(HitObject hitObject)
|
||||
{
|
||||
if (lifetimeEntries.TryGetValue(hitObject, out var entry))
|
||||
return entry;
|
||||
|
||||
return lifetimeEntries[hitObject] = CreateLifetimeEntry(hitObject);
|
||||
}
|
||||
}
|
||||
|
||||
public class BeatmapInvalidForRulesetException : ArgumentException
|
||||
|
Loading…
Reference in New Issue
Block a user