mirror of
https://github.com/osukey/osukey.git
synced 2025-08-05 15:44:04 +09:00
Merge branch 'master' into autoplay-pause-support
This commit is contained in:
@ -11,7 +11,6 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.TypeExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Performance;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Audio;
|
||||
@ -311,6 +310,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
|
||||
/// <summary>
|
||||
/// Invoked for this <see cref="DrawableHitObject"/> to take on any values from a newly-applied <see cref="HitObject"/>.
|
||||
/// This is also fired after any changes which occurred via an <see cref="osu.Game.Rulesets.Objects.HitObject.ApplyDefaults"/> call.
|
||||
/// </summary>
|
||||
protected virtual void OnApply()
|
||||
{
|
||||
@ -318,6 +318,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
|
||||
/// <summary>
|
||||
/// Invoked for this <see cref="DrawableHitObject"/> to revert any values previously taken on from the currently-applied <see cref="HitObject"/>.
|
||||
/// This is also fired after any changes which occurred via an <see cref="osu.Game.Rulesets.Objects.HitObject.ApplyDefaults"/> call.
|
||||
/// </summary>
|
||||
protected virtual void OnFree()
|
||||
{
|
||||
@ -443,9 +444,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
|
||||
/// <summary>
|
||||
/// Apply (generally fade-in) transforms leading into the <see cref="HitObject"/> start time.
|
||||
/// The local drawable hierarchy is recursively delayed to <see cref="LifetimeEntry.LifetimeStart"/> for convenience.
|
||||
///
|
||||
/// By default this will fade in the object from zero with no duration.
|
||||
/// By default, this will fade in the object from zero with no duration.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is called once before every <see cref="UpdateStateTransforms"/>. This is to ensure a good state in the case
|
||||
@ -621,17 +620,11 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
protected internal new ScheduledDelegate Schedule(Action action) => base.Schedule(action);
|
||||
|
||||
/// <summary>
|
||||
/// A safe offset prior to the start time of <see cref="HitObject"/> at which this <see cref="DrawableHitObject"/> may begin displaying contents.
|
||||
/// An offset prior to the start time of <see cref="HitObject"/> at which this <see cref="DrawableHitObject"/> may begin displaying contents.
|
||||
/// By default, <see cref="DrawableHitObject"/>s are assumed to display their contents within 10 seconds prior to the start time of <see cref="HitObject"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is only used as an optimisation to delay the initial update of this <see cref="DrawableHitObject"/> and may be tuned more aggressively if required.
|
||||
/// It is indirectly used to decide the automatic transform offset provided to <see cref="UpdateInitialTransforms"/>.
|
||||
/// A more accurate <see cref="LifetimeEntry.LifetimeStart"/> should be set for further optimisation (in <see cref="LoadComplete"/>, for example).
|
||||
/// <para>
|
||||
/// Only has an effect if this <see cref="DrawableHitObject"/> is not being pooled.
|
||||
/// For pooled <see cref="DrawableHitObject"/>s, use <see cref="HitObjectLifetimeEntry.InitialLifetimeOffset"/> instead.
|
||||
/// </para>
|
||||
/// The initial transformation (<see cref="UpdateInitialTransforms"/>) starts at this offset before the start time of <see cref="HitObject"/>.
|
||||
/// </remarks>
|
||||
protected virtual double InitialLifetimeOffset => 10000;
|
||||
|
||||
|
@ -1,31 +0,0 @@
|
||||
// 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;
|
||||
|
||||
namespace osu.Game.Rulesets.Objects.Drawables
|
||||
{
|
||||
/// <summary>
|
||||
/// An interface that exposes properties required for scrolling hit objects to be properly displayed.
|
||||
/// </summary>
|
||||
internal interface IScrollingHitObject : IDrawable
|
||||
{
|
||||
/// <summary>
|
||||
/// Time offset before the hit object start time at which this <see cref="IScrollingHitObject"/> becomes visible and the time offset
|
||||
/// after the hit object's end time after which it expires.
|
||||
///
|
||||
/// <para>
|
||||
/// This provides only a default life time range, however classes inheriting from <see cref="IScrollingHitObject"/> should override
|
||||
/// their life times if more tight control is desired.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
BindableDouble LifetimeOffset { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Axes which this <see cref="IScrollingHitObject"/> will scroll through.
|
||||
/// This is set by the container which this scrolls through.
|
||||
/// </summary>
|
||||
Axes ScrollingAxes { set; }
|
||||
}
|
||||
}
|
@ -35,7 +35,11 @@ namespace osu.Game.Rulesets.Objects
|
||||
HitObject = hitObject;
|
||||
|
||||
startTimeBindable.BindTo(HitObject.StartTimeBindable);
|
||||
startTimeBindable.BindValueChanged(onStartTimeChanged, true);
|
||||
startTimeBindable.BindValueChanged(_ => setInitialLifetime(), true);
|
||||
|
||||
// Subscribe to this event before the DrawableHitObject so that the local callback is invoked before the entry is re-applied as a result of DefaultsApplied.
|
||||
// This way, the DrawableHitObject can use OnApply() to overwrite the LifetimeStart that was set inside setInitialLifetime().
|
||||
HitObject.DefaultsApplied += _ => setInitialLifetime();
|
||||
}
|
||||
|
||||
// The lifetime, as set by the hitobject.
|
||||
@ -82,15 +86,14 @@ namespace osu.Game.Rulesets.Objects
|
||||
/// By default, <see cref="HitObject"/>s are assumed to display their contents within 10 seconds prior to their start time.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is only used as an optimisation to delay the initial update of the <see cref="HitObject"/> and may be tuned more aggressively if required.
|
||||
/// It is indirectly used to decide the automatic transform offset provided to <see cref="DrawableHitObject.UpdateInitialTransforms"/>.
|
||||
/// A more accurate <see cref="LifetimeEntry.LifetimeStart"/> should be set for further optimisation (in <see cref="DrawableHitObject.LoadComplete"/>, for example).
|
||||
/// This is only used as an optimisation to delay the initial application of the <see cref="HitObject"/> to a <see cref="DrawableHitObject"/>.
|
||||
/// A more accurate <see cref="LifetimeEntry.LifetimeStart"/> should be set on the hit object application, for further optimisation.
|
||||
/// </remarks>
|
||||
protected virtual double InitialLifetimeOffset => 10000;
|
||||
|
||||
/// <summary>
|
||||
/// Resets <see cref="LifetimeEntry.LifetimeStart"/> according to the change in start time of the <see cref="HitObject"/>.
|
||||
/// Set <see cref="LifetimeEntry.LifetimeStart"/> using <see cref="InitialLifetimeOffset"/>.
|
||||
/// </summary>
|
||||
private void onStartTimeChanged(ValueChangedEvent<double> startTime) => LifetimeStart = HitObject.StartTime - InitialLifetimeOffset;
|
||||
private void setInitialLifetime() => LifetimeStart = HitObject.StartTime - InitialLifetimeOffset;
|
||||
}
|
||||
}
|
||||
|
@ -122,18 +122,20 @@ namespace osu.Game.Rulesets.UI
|
||||
var entry = (HitObjectLifetimeEntry)lifetimeEntry;
|
||||
Debug.Assert(!aliveDrawableMap.ContainsKey(entry));
|
||||
|
||||
bool isNonPooled = nonPooledDrawableMap.TryGetValue(entry, out var drawable);
|
||||
bool isPooled = !nonPooledDrawableMap.TryGetValue(entry, out var drawable);
|
||||
drawable ??= pooledObjectProvider?.GetPooledDrawableRepresentation(entry.HitObject, null);
|
||||
if (drawable == null)
|
||||
throw new InvalidOperationException($"A drawable representation could not be retrieved for hitobject type: {entry.HitObject.GetType().ReadableName()}.");
|
||||
|
||||
aliveDrawableMap[entry] = drawable;
|
||||
|
||||
if (isPooled)
|
||||
{
|
||||
addDrawable(drawable);
|
||||
HitObjectUsageBegan?.Invoke(entry.HitObject);
|
||||
}
|
||||
|
||||
OnAdd(drawable);
|
||||
|
||||
if (isNonPooled) return;
|
||||
|
||||
addDrawable(drawable);
|
||||
HitObjectUsageBegan?.Invoke(entry.HitObject);
|
||||
}
|
||||
|
||||
private void entryBecameDead(LifetimeEntry lifetimeEntry)
|
||||
@ -142,17 +144,18 @@ namespace osu.Game.Rulesets.UI
|
||||
Debug.Assert(aliveDrawableMap.ContainsKey(entry));
|
||||
|
||||
var drawable = aliveDrawableMap[entry];
|
||||
bool isNonPooled = nonPooledDrawableMap.ContainsKey(entry);
|
||||
bool isPooled = !nonPooledDrawableMap.ContainsKey(entry);
|
||||
|
||||
drawable.OnKilled();
|
||||
aliveDrawableMap.Remove(entry);
|
||||
|
||||
if (isPooled)
|
||||
{
|
||||
removeDrawable(drawable);
|
||||
HitObjectUsageFinished?.Invoke(entry.HitObject);
|
||||
}
|
||||
|
||||
OnRemove(drawable);
|
||||
|
||||
if (isNonPooled) return;
|
||||
|
||||
removeDrawable(drawable);
|
||||
// The hit object is not freed when the DHO was not pooled.
|
||||
HitObjectUsageFinished?.Invoke(entry.HitObject);
|
||||
}
|
||||
|
||||
private void addDrawable(DrawableHitObject drawable)
|
||||
@ -211,21 +214,16 @@ namespace osu.Game.Rulesets.UI
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when a <see cref="DrawableHitObject"/> is added to this container.
|
||||
/// Invoked after a <see cref="DrawableHitObject"/> is added to this container.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method is not invoked for nested <see cref="DrawableHitObject"/>s.
|
||||
/// </remarks>
|
||||
protected virtual void OnAdd(DrawableHitObject drawableHitObject)
|
||||
{
|
||||
Debug.Assert(drawableHitObject.LoadState >= LoadState.Ready);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when a <see cref="DrawableHitObject"/> is removed from this container.
|
||||
/// Invoked after a <see cref="DrawableHitObject"/> is removed from this container.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method is not invoked for nested <see cref="DrawableHitObject"/>s.
|
||||
/// </remarks>
|
||||
protected virtual void OnRemove(DrawableHitObject drawableHitObject)
|
||||
{
|
||||
}
|
||||
|
@ -18,12 +18,7 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
||||
|
||||
/// <summary>
|
||||
/// Hit objects which require lifetime computation in the next update call.
|
||||
/// </summary>
|
||||
private readonly HashSet<DrawableHitObject> toComputeLifetime = new HashSet<DrawableHitObject>();
|
||||
|
||||
/// <summary>
|
||||
/// A set containing all <see cref="HitObjectContainer.AliveObjects"/> which have an up-to-date layout.
|
||||
/// A set of top-level <see cref="DrawableHitObject"/>s which have an up-to-date layout.
|
||||
/// </summary>
|
||||
private readonly HashSet<DrawableHitObject> layoutComputed = new HashSet<DrawableHitObject>();
|
||||
|
||||
@ -54,7 +49,6 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
{
|
||||
base.Clear();
|
||||
|
||||
toComputeLifetime.Clear();
|
||||
layoutComputed.Clear();
|
||||
}
|
||||
|
||||
@ -83,7 +77,7 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
|
||||
flipPositionIfRequired(ref position);
|
||||
|
||||
return scrollingInfo.Algorithm.TimeAt(position, Time.Current, scrollingInfo.TimeRange.Value, getLength());
|
||||
return scrollingInfo.Algorithm.TimeAt(position, Time.Current, scrollingInfo.TimeRange.Value, scrollLength);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -91,7 +85,7 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
/// </summary>
|
||||
public Vector2 ScreenSpacePositionAtTime(double time)
|
||||
{
|
||||
var pos = scrollingInfo.Algorithm.PositionAt(time, Time.Current, scrollingInfo.TimeRange.Value, getLength());
|
||||
var pos = scrollingInfo.Algorithm.PositionAt(time, Time.Current, scrollingInfo.TimeRange.Value, scrollLength);
|
||||
|
||||
flipPositionIfRequired(ref pos);
|
||||
|
||||
@ -106,16 +100,19 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
}
|
||||
}
|
||||
|
||||
private float getLength()
|
||||
private float scrollLength
|
||||
{
|
||||
switch (scrollingInfo.Direction.Value)
|
||||
get
|
||||
{
|
||||
case ScrollingDirection.Left:
|
||||
case ScrollingDirection.Right:
|
||||
return DrawWidth;
|
||||
switch (scrollingInfo.Direction.Value)
|
||||
{
|
||||
case ScrollingDirection.Left:
|
||||
case ScrollingDirection.Right:
|
||||
return DrawWidth;
|
||||
|
||||
default:
|
||||
return DrawHeight;
|
||||
default:
|
||||
return DrawHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -150,81 +147,40 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnAdd(DrawableHitObject drawableHitObject) => onAddRecursive(drawableHitObject);
|
||||
|
||||
protected override void OnRemove(DrawableHitObject drawableHitObject) => onRemoveRecursive(drawableHitObject);
|
||||
|
||||
private void onAddRecursive(DrawableHitObject hitObject)
|
||||
protected override void OnAdd(DrawableHitObject drawableHitObject)
|
||||
{
|
||||
invalidateHitObject(hitObject);
|
||||
|
||||
hitObject.DefaultsApplied += invalidateHitObject;
|
||||
|
||||
foreach (var nested in hitObject.NestedHitObjects)
|
||||
onAddRecursive(nested);
|
||||
invalidateHitObject(drawableHitObject);
|
||||
drawableHitObject.DefaultsApplied += invalidateHitObject;
|
||||
}
|
||||
|
||||
private void onRemoveRecursive(DrawableHitObject hitObject)
|
||||
protected override void OnRemove(DrawableHitObject drawableHitObject)
|
||||
{
|
||||
toComputeLifetime.Remove(hitObject);
|
||||
layoutComputed.Remove(hitObject);
|
||||
layoutComputed.Remove(drawableHitObject);
|
||||
|
||||
hitObject.DefaultsApplied -= invalidateHitObject;
|
||||
|
||||
foreach (var nested in hitObject.NestedHitObjects)
|
||||
onRemoveRecursive(nested);
|
||||
drawableHitObject.DefaultsApplied -= invalidateHitObject;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make this <see cref="DrawableHitObject"/> lifetime and layout computed in next update.
|
||||
/// </summary>
|
||||
private void invalidateHitObject(DrawableHitObject hitObject)
|
||||
{
|
||||
// Lifetime computation is delayed until next update because
|
||||
// when the hit object is not pooled this container is not loaded here and `scrollLength` cannot be computed.
|
||||
toComputeLifetime.Add(hitObject);
|
||||
hitObject.LifetimeStart = computeOriginAdjustedLifetimeStart(hitObject);
|
||||
layoutComputed.Remove(hitObject);
|
||||
}
|
||||
|
||||
private float scrollLength;
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (!layoutCache.IsValid)
|
||||
if (layoutCache.IsValid) return;
|
||||
|
||||
foreach (var hitObject in Objects)
|
||||
{
|
||||
toComputeLifetime.Clear();
|
||||
|
||||
foreach (var hitObject in Objects)
|
||||
{
|
||||
if (hitObject.HitObject != null)
|
||||
toComputeLifetime.Add(hitObject);
|
||||
}
|
||||
|
||||
layoutComputed.Clear();
|
||||
|
||||
scrollingInfo.Algorithm.Reset();
|
||||
|
||||
switch (direction.Value)
|
||||
{
|
||||
case ScrollingDirection.Up:
|
||||
case ScrollingDirection.Down:
|
||||
scrollLength = DrawSize.Y;
|
||||
break;
|
||||
|
||||
default:
|
||||
scrollLength = DrawSize.X;
|
||||
break;
|
||||
}
|
||||
|
||||
layoutCache.Validate();
|
||||
if (hitObject.HitObject != null)
|
||||
invalidateHitObject(hitObject);
|
||||
}
|
||||
|
||||
foreach (var hitObject in toComputeLifetime)
|
||||
hitObject.LifetimeStart = computeOriginAdjustedLifetimeStart(hitObject);
|
||||
scrollingInfo.Algorithm.Reset();
|
||||
|
||||
toComputeLifetime.Clear();
|
||||
layoutCache.Validate();
|
||||
}
|
||||
|
||||
protected override void UpdateAfterChildrenLife()
|
||||
|
Reference in New Issue
Block a user