mirror of
https://github.com/osukey/osukey.git
synced 2025-08-03 22:56:36 +09:00
Normalize all the line endings
This commit is contained in:
@ -1,25 +1,25 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
namespace osu.Game.Rulesets.UI.Scrolling
|
||||
{
|
||||
public enum ScrollingDirection
|
||||
{
|
||||
/// <summary>
|
||||
/// Hitobjects will scroll vertically from the bottom of the hitobject container.
|
||||
/// </summary>
|
||||
Up,
|
||||
/// <summary>
|
||||
/// Hitobjects will scroll vertically from the top of the hitobject container.
|
||||
/// </summary>
|
||||
Down,
|
||||
/// <summary>
|
||||
/// Hitobjects will scroll horizontally from the right of the hitobject container.
|
||||
/// </summary>
|
||||
Left,
|
||||
/// <summary>
|
||||
/// Hitobjects will scroll horizontally from the left of the hitobject container.
|
||||
/// </summary>
|
||||
Right
|
||||
}
|
||||
}
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
namespace osu.Game.Rulesets.UI.Scrolling
|
||||
{
|
||||
public enum ScrollingDirection
|
||||
{
|
||||
/// <summary>
|
||||
/// Hitobjects will scroll vertically from the bottom of the hitobject container.
|
||||
/// </summary>
|
||||
Up,
|
||||
/// <summary>
|
||||
/// Hitobjects will scroll vertically from the top of the hitobject container.
|
||||
/// </summary>
|
||||
Down,
|
||||
/// <summary>
|
||||
/// Hitobjects will scroll horizontally from the right of the hitobject container.
|
||||
/// </summary>
|
||||
Left,
|
||||
/// <summary>
|
||||
/// Hitobjects will scroll horizontally from the left of the hitobject container.
|
||||
/// </summary>
|
||||
Right
|
||||
}
|
||||
}
|
||||
|
@ -1,116 +1,116 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Caching;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Lists;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Timing;
|
||||
using osu.Game.Rulesets.UI.Scrolling.Visualisers;
|
||||
|
||||
namespace osu.Game.Rulesets.UI.Scrolling
|
||||
{
|
||||
public class ScrollingHitObjectContainer : HitObjectContainer
|
||||
{
|
||||
/// <summary>
|
||||
/// The duration required to scroll through one length of the <see cref="ScrollingHitObjectContainer"/> before any control point adjustments.
|
||||
/// </summary>
|
||||
public readonly BindableDouble TimeRange = new BindableDouble
|
||||
{
|
||||
MinValue = 0,
|
||||
MaxValue = double.MaxValue
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The control points that adjust the scrolling speed.
|
||||
/// </summary>
|
||||
protected readonly SortedList<MultiplierControlPoint> ControlPoints = new SortedList<MultiplierControlPoint>();
|
||||
|
||||
private readonly ScrollingDirection direction;
|
||||
|
||||
private Cached initialStateCache = new Cached();
|
||||
|
||||
public ScrollingHitObjectContainer(ScrollingDirection direction)
|
||||
{
|
||||
this.direction = direction;
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
TimeRange.ValueChanged += v => initialStateCache.Invalidate();
|
||||
}
|
||||
|
||||
private ISpeedChangeVisualiser speedChangeVisualiser;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuConfigManager config)
|
||||
{
|
||||
switch (config.Get<SpeedChangeVisualisationMethod>(OsuSetting.SpeedChangeVisualisation))
|
||||
{
|
||||
case SpeedChangeVisualisationMethod.Sequential:
|
||||
speedChangeVisualiser = new SequentialSpeedChangeVisualiser(ControlPoints);
|
||||
break;
|
||||
case SpeedChangeVisualisationMethod.Overlapping:
|
||||
speedChangeVisualiser = new OverlappingSpeedChangeVisualiser(ControlPoints);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Add(DrawableHitObject hitObject)
|
||||
{
|
||||
initialStateCache.Invalidate();
|
||||
base.Add(hitObject);
|
||||
}
|
||||
|
||||
public override bool Remove(DrawableHitObject hitObject)
|
||||
{
|
||||
var result = base.Remove(hitObject);
|
||||
if (result)
|
||||
initialStateCache.Invalidate();
|
||||
return result;
|
||||
}
|
||||
|
||||
public void AddControlPoint(MultiplierControlPoint controlPoint)
|
||||
{
|
||||
ControlPoints.Add(controlPoint);
|
||||
initialStateCache.Invalidate();
|
||||
}
|
||||
|
||||
public bool RemoveControlPoint(MultiplierControlPoint controlPoint)
|
||||
{
|
||||
var result = ControlPoints.Remove(controlPoint);
|
||||
if (result)
|
||||
initialStateCache.Invalidate();
|
||||
return result;
|
||||
}
|
||||
|
||||
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
|
||||
{
|
||||
if ((invalidation & (Invalidation.RequiredParentSizeToFit | Invalidation.DrawInfo)) > 0)
|
||||
initialStateCache.Invalidate();
|
||||
|
||||
return base.Invalidate(invalidation, source, shallPropagate);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (!initialStateCache.IsValid)
|
||||
{
|
||||
speedChangeVisualiser.ComputeInitialStates(Objects, direction, TimeRange, DrawSize);
|
||||
initialStateCache.Validate();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void UpdateAfterChildrenLife()
|
||||
{
|
||||
base.UpdateAfterChildrenLife();
|
||||
|
||||
// We need to calculate this as soon as possible after lifetimes so that hitobjects get the final say in their positions
|
||||
speedChangeVisualiser.ComputePositions(AliveObjects, direction, Time.Current, TimeRange, DrawSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Caching;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Lists;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Timing;
|
||||
using osu.Game.Rulesets.UI.Scrolling.Visualisers;
|
||||
|
||||
namespace osu.Game.Rulesets.UI.Scrolling
|
||||
{
|
||||
public class ScrollingHitObjectContainer : HitObjectContainer
|
||||
{
|
||||
/// <summary>
|
||||
/// The duration required to scroll through one length of the <see cref="ScrollingHitObjectContainer"/> before any control point adjustments.
|
||||
/// </summary>
|
||||
public readonly BindableDouble TimeRange = new BindableDouble
|
||||
{
|
||||
MinValue = 0,
|
||||
MaxValue = double.MaxValue
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The control points that adjust the scrolling speed.
|
||||
/// </summary>
|
||||
protected readonly SortedList<MultiplierControlPoint> ControlPoints = new SortedList<MultiplierControlPoint>();
|
||||
|
||||
private readonly ScrollingDirection direction;
|
||||
|
||||
private Cached initialStateCache = new Cached();
|
||||
|
||||
public ScrollingHitObjectContainer(ScrollingDirection direction)
|
||||
{
|
||||
this.direction = direction;
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
TimeRange.ValueChanged += v => initialStateCache.Invalidate();
|
||||
}
|
||||
|
||||
private ISpeedChangeVisualiser speedChangeVisualiser;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuConfigManager config)
|
||||
{
|
||||
switch (config.Get<SpeedChangeVisualisationMethod>(OsuSetting.SpeedChangeVisualisation))
|
||||
{
|
||||
case SpeedChangeVisualisationMethod.Sequential:
|
||||
speedChangeVisualiser = new SequentialSpeedChangeVisualiser(ControlPoints);
|
||||
break;
|
||||
case SpeedChangeVisualisationMethod.Overlapping:
|
||||
speedChangeVisualiser = new OverlappingSpeedChangeVisualiser(ControlPoints);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Add(DrawableHitObject hitObject)
|
||||
{
|
||||
initialStateCache.Invalidate();
|
||||
base.Add(hitObject);
|
||||
}
|
||||
|
||||
public override bool Remove(DrawableHitObject hitObject)
|
||||
{
|
||||
var result = base.Remove(hitObject);
|
||||
if (result)
|
||||
initialStateCache.Invalidate();
|
||||
return result;
|
||||
}
|
||||
|
||||
public void AddControlPoint(MultiplierControlPoint controlPoint)
|
||||
{
|
||||
ControlPoints.Add(controlPoint);
|
||||
initialStateCache.Invalidate();
|
||||
}
|
||||
|
||||
public bool RemoveControlPoint(MultiplierControlPoint controlPoint)
|
||||
{
|
||||
var result = ControlPoints.Remove(controlPoint);
|
||||
if (result)
|
||||
initialStateCache.Invalidate();
|
||||
return result;
|
||||
}
|
||||
|
||||
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
|
||||
{
|
||||
if ((invalidation & (Invalidation.RequiredParentSizeToFit | Invalidation.DrawInfo)) > 0)
|
||||
initialStateCache.Invalidate();
|
||||
|
||||
return base.Invalidate(invalidation, source, shallPropagate);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (!initialStateCache.IsValid)
|
||||
{
|
||||
speedChangeVisualiser.ComputeInitialStates(Objects, direction, TimeRange, DrawSize);
|
||||
initialStateCache.Validate();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void UpdateAfterChildrenLife()
|
||||
{
|
||||
base.UpdateAfterChildrenLife();
|
||||
|
||||
// We need to calculate this as soon as possible after lifetimes so that hitobjects get the final say in their positions
|
||||
speedChangeVisualiser.ComputePositions(AliveObjects, direction, Time.Current, TimeRange, DrawSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,127 +1,127 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Transforms;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using OpenTK.Input;
|
||||
|
||||
namespace osu.Game.Rulesets.UI.Scrolling
|
||||
{
|
||||
/// <summary>
|
||||
/// A type of <see cref="Playfield"/> specialized towards scrolling <see cref="DrawableHitObject"/>s.
|
||||
/// </summary>
|
||||
public abstract class ScrollingPlayfield : Playfield
|
||||
{
|
||||
/// <summary>
|
||||
/// The default span of time visible by the length of the scrolling axes.
|
||||
/// This is clamped between <see cref="time_span_min"/> and <see cref="time_span_max"/>.
|
||||
/// </summary>
|
||||
private const double time_span_default = 1500;
|
||||
/// <summary>
|
||||
/// The minimum span of time that may be visible by the length of the scrolling axes.
|
||||
/// </summary>
|
||||
private const double time_span_min = 50;
|
||||
/// <summary>
|
||||
/// The maximum span of time that may be visible by the length of the scrolling axes.
|
||||
/// </summary>
|
||||
private const double time_span_max = 10000;
|
||||
/// <summary>
|
||||
/// The step increase/decrease of the span of time visible by the length of the scrolling axes.
|
||||
/// </summary>
|
||||
private const double time_span_step = 50;
|
||||
|
||||
/// <summary>
|
||||
/// The span of time that is visible by the length of the scrolling axes.
|
||||
/// For example, only hit objects with start time less than or equal to 1000 will be visible with <see cref="VisibleTimeRange"/> = 1000.
|
||||
/// </summary>
|
||||
public readonly BindableDouble VisibleTimeRange = new BindableDouble(time_span_default)
|
||||
{
|
||||
Default = time_span_default,
|
||||
MinValue = time_span_min,
|
||||
MaxValue = time_span_max
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Whether the player can change <see cref="VisibleTimeRange"/>.
|
||||
/// </summary>
|
||||
protected virtual bool UserScrollSpeedAdjustment => true;
|
||||
|
||||
/// <summary>
|
||||
/// The container that contains the <see cref="DrawableHitObject"/>s.
|
||||
/// </summary>
|
||||
public new ScrollingHitObjectContainer HitObjects => (ScrollingHitObjectContainer)base.HitObjects;
|
||||
|
||||
private readonly ScrollingDirection direction;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="ScrollingPlayfield"/>.
|
||||
/// </summary>
|
||||
/// <param name="direction">The direction in which <see cref="DrawableHitObject"/>s in this container should scroll.</param>
|
||||
/// <param name="customWidth">The width to scale the internal coordinate space to.
|
||||
/// May be null if scaling based on <paramref name="customHeight"/> is desired. If <paramref name="customHeight"/> is also null, no scaling will occur.
|
||||
/// </param>
|
||||
/// <param name="customHeight">The height to scale the internal coordinate space to.
|
||||
/// May be null if scaling based on <paramref name="customWidth"/> is desired. If <paramref name="customWidth"/> is also null, no scaling will occur.
|
||||
/// </param>
|
||||
protected ScrollingPlayfield(ScrollingDirection direction, float? customWidth = null, float? customHeight = null)
|
||||
: base(customWidth, customHeight)
|
||||
{
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
HitObjects.TimeRange.BindTo(VisibleTimeRange);
|
||||
}
|
||||
|
||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||
{
|
||||
if (!UserScrollSpeedAdjustment)
|
||||
return false;
|
||||
|
||||
if (state.Keyboard.ControlPressed)
|
||||
{
|
||||
switch (args.Key)
|
||||
{
|
||||
case Key.Minus:
|
||||
transformVisibleTimeRangeTo(VisibleTimeRange + time_span_step, 200, Easing.OutQuint);
|
||||
break;
|
||||
case Key.Plus:
|
||||
transformVisibleTimeRangeTo(VisibleTimeRange - time_span_step, 200, Easing.OutQuint);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void transformVisibleTimeRangeTo(double newTimeRange, double duration = 0, Easing easing = Easing.None)
|
||||
{
|
||||
this.TransformTo(this.PopulateTransform(new TransformVisibleTimeRange(), newTimeRange, duration, easing));
|
||||
}
|
||||
|
||||
protected sealed override HitObjectContainer CreateHitObjectContainer() => new ScrollingHitObjectContainer(direction);
|
||||
|
||||
private class TransformVisibleTimeRange : Transform<double, ScrollingPlayfield>
|
||||
{
|
||||
private double valueAt(double time)
|
||||
{
|
||||
if (time < StartTime) return StartValue;
|
||||
if (time >= EndTime) return EndValue;
|
||||
|
||||
return Interpolation.ValueAt(time, StartValue, EndValue, StartTime, EndTime, Easing);
|
||||
}
|
||||
|
||||
public override string TargetMember => "VisibleTimeRange.Value";
|
||||
|
||||
protected override void Apply(ScrollingPlayfield d, double time) => d.VisibleTimeRange.Value = valueAt(time);
|
||||
protected override void ReadIntoStartValue(ScrollingPlayfield d) => StartValue = d.VisibleTimeRange.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Transforms;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using OpenTK.Input;
|
||||
|
||||
namespace osu.Game.Rulesets.UI.Scrolling
|
||||
{
|
||||
/// <summary>
|
||||
/// A type of <see cref="Playfield"/> specialized towards scrolling <see cref="DrawableHitObject"/>s.
|
||||
/// </summary>
|
||||
public abstract class ScrollingPlayfield : Playfield
|
||||
{
|
||||
/// <summary>
|
||||
/// The default span of time visible by the length of the scrolling axes.
|
||||
/// This is clamped between <see cref="time_span_min"/> and <see cref="time_span_max"/>.
|
||||
/// </summary>
|
||||
private const double time_span_default = 1500;
|
||||
/// <summary>
|
||||
/// The minimum span of time that may be visible by the length of the scrolling axes.
|
||||
/// </summary>
|
||||
private const double time_span_min = 50;
|
||||
/// <summary>
|
||||
/// The maximum span of time that may be visible by the length of the scrolling axes.
|
||||
/// </summary>
|
||||
private const double time_span_max = 10000;
|
||||
/// <summary>
|
||||
/// The step increase/decrease of the span of time visible by the length of the scrolling axes.
|
||||
/// </summary>
|
||||
private const double time_span_step = 50;
|
||||
|
||||
/// <summary>
|
||||
/// The span of time that is visible by the length of the scrolling axes.
|
||||
/// For example, only hit objects with start time less than or equal to 1000 will be visible with <see cref="VisibleTimeRange"/> = 1000.
|
||||
/// </summary>
|
||||
public readonly BindableDouble VisibleTimeRange = new BindableDouble(time_span_default)
|
||||
{
|
||||
Default = time_span_default,
|
||||
MinValue = time_span_min,
|
||||
MaxValue = time_span_max
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Whether the player can change <see cref="VisibleTimeRange"/>.
|
||||
/// </summary>
|
||||
protected virtual bool UserScrollSpeedAdjustment => true;
|
||||
|
||||
/// <summary>
|
||||
/// The container that contains the <see cref="DrawableHitObject"/>s.
|
||||
/// </summary>
|
||||
public new ScrollingHitObjectContainer HitObjects => (ScrollingHitObjectContainer)base.HitObjects;
|
||||
|
||||
private readonly ScrollingDirection direction;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="ScrollingPlayfield"/>.
|
||||
/// </summary>
|
||||
/// <param name="direction">The direction in which <see cref="DrawableHitObject"/>s in this container should scroll.</param>
|
||||
/// <param name="customWidth">The width to scale the internal coordinate space to.
|
||||
/// May be null if scaling based on <paramref name="customHeight"/> is desired. If <paramref name="customHeight"/> is also null, no scaling will occur.
|
||||
/// </param>
|
||||
/// <param name="customHeight">The height to scale the internal coordinate space to.
|
||||
/// May be null if scaling based on <paramref name="customWidth"/> is desired. If <paramref name="customWidth"/> is also null, no scaling will occur.
|
||||
/// </param>
|
||||
protected ScrollingPlayfield(ScrollingDirection direction, float? customWidth = null, float? customHeight = null)
|
||||
: base(customWidth, customHeight)
|
||||
{
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
HitObjects.TimeRange.BindTo(VisibleTimeRange);
|
||||
}
|
||||
|
||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||
{
|
||||
if (!UserScrollSpeedAdjustment)
|
||||
return false;
|
||||
|
||||
if (state.Keyboard.ControlPressed)
|
||||
{
|
||||
switch (args.Key)
|
||||
{
|
||||
case Key.Minus:
|
||||
transformVisibleTimeRangeTo(VisibleTimeRange + time_span_step, 200, Easing.OutQuint);
|
||||
break;
|
||||
case Key.Plus:
|
||||
transformVisibleTimeRangeTo(VisibleTimeRange - time_span_step, 200, Easing.OutQuint);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void transformVisibleTimeRangeTo(double newTimeRange, double duration = 0, Easing easing = Easing.None)
|
||||
{
|
||||
this.TransformTo(this.PopulateTransform(new TransformVisibleTimeRange(), newTimeRange, duration, easing));
|
||||
}
|
||||
|
||||
protected sealed override HitObjectContainer CreateHitObjectContainer() => new ScrollingHitObjectContainer(direction);
|
||||
|
||||
private class TransformVisibleTimeRange : Transform<double, ScrollingPlayfield>
|
||||
{
|
||||
private double valueAt(double time)
|
||||
{
|
||||
if (time < StartTime) return StartValue;
|
||||
if (time >= EndTime) return EndValue;
|
||||
|
||||
return Interpolation.ValueAt(time, StartValue, EndValue, StartTime, EndTime, Easing);
|
||||
}
|
||||
|
||||
public override string TargetMember => "VisibleTimeRange.Value";
|
||||
|
||||
protected override void Apply(ScrollingPlayfield d, double time) => d.VisibleTimeRange.Value = valueAt(time);
|
||||
protected override void ReadIntoStartValue(ScrollingPlayfield d) => StartValue = d.VisibleTimeRange.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,110 +1,110 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Lists;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.IO.Serialization;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Timing;
|
||||
|
||||
namespace osu.Game.Rulesets.UI.Scrolling
|
||||
{
|
||||
/// <summary>
|
||||
/// A type of <see cref="RulesetContainer{TPlayfield,TObject}"/> that supports a <see cref="ScrollingPlayfield"/>.
|
||||
/// <see cref="HitObject"/>s inside this <see cref="RulesetContainer{TPlayfield,TObject}"/> will scroll within the playfield.
|
||||
/// </summary>
|
||||
public abstract class ScrollingRulesetContainer<TPlayfield, TObject> : RulesetContainer<TPlayfield, TObject>
|
||||
where TObject : HitObject
|
||||
where TPlayfield : ScrollingPlayfield
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides the default <see cref="MultiplierControlPoint"/>s that adjust the scrolling rate of <see cref="HitObject"/>s
|
||||
/// inside this <see cref="RulesetContainer{TPlayfield,TObject}"/>.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected readonly SortedList<MultiplierControlPoint> DefaultControlPoints = new SortedList<MultiplierControlPoint>(Comparer<MultiplierControlPoint>.Default);
|
||||
|
||||
protected ScrollingRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset)
|
||||
: base(ruleset, beatmap, isForCurrentRuleset)
|
||||
{
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
// Calculate default multiplier control points
|
||||
var lastTimingPoint = new TimingControlPoint();
|
||||
var lastDifficultyPoint = new DifficultyControlPoint();
|
||||
|
||||
// Merge timing + difficulty points
|
||||
var allPoints = new SortedList<ControlPoint>(Comparer<ControlPoint>.Default);
|
||||
allPoints.AddRange(Beatmap.ControlPointInfo.TimingPoints);
|
||||
allPoints.AddRange(Beatmap.ControlPointInfo.DifficultyPoints);
|
||||
|
||||
// Generate the timing points, making non-timing changes use the previous timing change
|
||||
var timingChanges = allPoints.Select(c =>
|
||||
{
|
||||
var timingPoint = c as TimingControlPoint;
|
||||
var difficultyPoint = c as DifficultyControlPoint;
|
||||
|
||||
if (timingPoint != null)
|
||||
lastTimingPoint = timingPoint;
|
||||
|
||||
if (difficultyPoint != null)
|
||||
lastDifficultyPoint = difficultyPoint;
|
||||
|
||||
return new MultiplierControlPoint(c.Time)
|
||||
{
|
||||
TimingPoint = lastTimingPoint,
|
||||
DifficultyPoint = lastDifficultyPoint
|
||||
};
|
||||
});
|
||||
|
||||
double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue;
|
||||
|
||||
// Perform some post processing of the timing changes
|
||||
timingChanges = timingChanges
|
||||
// Collapse sections after the last hit object
|
||||
.Where(s => s.StartTime <= lastObjectTime)
|
||||
// Collapse sections with the same start time
|
||||
.GroupBy(s => s.StartTime).Select(g => g.Last()).OrderBy(s => s.StartTime);
|
||||
|
||||
DefaultControlPoints.AddRange(timingChanges);
|
||||
|
||||
// If we have no control points, add a default one
|
||||
if (DefaultControlPoints.Count == 0)
|
||||
DefaultControlPoints.Add(new MultiplierControlPoint());
|
||||
|
||||
DefaultControlPoints.ForEach(c => applySpeedAdjustment(c, Playfield));
|
||||
}
|
||||
|
||||
private void applySpeedAdjustment(MultiplierControlPoint controlPoint, ScrollingPlayfield playfield)
|
||||
{
|
||||
playfield.HitObjects.AddControlPoint(controlPoint);
|
||||
playfield.NestedPlayfields?.OfType<ScrollingPlayfield>().ForEach(p => applySpeedAdjustment(controlPoint, p));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a <see cref="MultiplierControlPoint"/> with the default timing change/difficulty change from the beatmap at a time.
|
||||
/// </summary>
|
||||
/// <param name="time">The time to create the control point at.</param>
|
||||
/// <returns>The default <see cref="MultiplierControlPoint"/> at <paramref name="time"/>.</returns>
|
||||
public MultiplierControlPoint CreateControlPointAt(double time)
|
||||
{
|
||||
if (DefaultControlPoints.Count == 0)
|
||||
return new MultiplierControlPoint(time);
|
||||
|
||||
int index = DefaultControlPoints.BinarySearch(new MultiplierControlPoint(time));
|
||||
if (index < 0)
|
||||
return new MultiplierControlPoint(time);
|
||||
|
||||
return new MultiplierControlPoint(time, DefaultControlPoints[index].DeepClone());
|
||||
}
|
||||
}
|
||||
}
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Lists;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.IO.Serialization;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Timing;
|
||||
|
||||
namespace osu.Game.Rulesets.UI.Scrolling
|
||||
{
|
||||
/// <summary>
|
||||
/// A type of <see cref="RulesetContainer{TPlayfield,TObject}"/> that supports a <see cref="ScrollingPlayfield"/>.
|
||||
/// <see cref="HitObject"/>s inside this <see cref="RulesetContainer{TPlayfield,TObject}"/> will scroll within the playfield.
|
||||
/// </summary>
|
||||
public abstract class ScrollingRulesetContainer<TPlayfield, TObject> : RulesetContainer<TPlayfield, TObject>
|
||||
where TObject : HitObject
|
||||
where TPlayfield : ScrollingPlayfield
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides the default <see cref="MultiplierControlPoint"/>s that adjust the scrolling rate of <see cref="HitObject"/>s
|
||||
/// inside this <see cref="RulesetContainer{TPlayfield,TObject}"/>.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected readonly SortedList<MultiplierControlPoint> DefaultControlPoints = new SortedList<MultiplierControlPoint>(Comparer<MultiplierControlPoint>.Default);
|
||||
|
||||
protected ScrollingRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset)
|
||||
: base(ruleset, beatmap, isForCurrentRuleset)
|
||||
{
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
// Calculate default multiplier control points
|
||||
var lastTimingPoint = new TimingControlPoint();
|
||||
var lastDifficultyPoint = new DifficultyControlPoint();
|
||||
|
||||
// Merge timing + difficulty points
|
||||
var allPoints = new SortedList<ControlPoint>(Comparer<ControlPoint>.Default);
|
||||
allPoints.AddRange(Beatmap.ControlPointInfo.TimingPoints);
|
||||
allPoints.AddRange(Beatmap.ControlPointInfo.DifficultyPoints);
|
||||
|
||||
// Generate the timing points, making non-timing changes use the previous timing change
|
||||
var timingChanges = allPoints.Select(c =>
|
||||
{
|
||||
var timingPoint = c as TimingControlPoint;
|
||||
var difficultyPoint = c as DifficultyControlPoint;
|
||||
|
||||
if (timingPoint != null)
|
||||
lastTimingPoint = timingPoint;
|
||||
|
||||
if (difficultyPoint != null)
|
||||
lastDifficultyPoint = difficultyPoint;
|
||||
|
||||
return new MultiplierControlPoint(c.Time)
|
||||
{
|
||||
TimingPoint = lastTimingPoint,
|
||||
DifficultyPoint = lastDifficultyPoint
|
||||
};
|
||||
});
|
||||
|
||||
double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue;
|
||||
|
||||
// Perform some post processing of the timing changes
|
||||
timingChanges = timingChanges
|
||||
// Collapse sections after the last hit object
|
||||
.Where(s => s.StartTime <= lastObjectTime)
|
||||
// Collapse sections with the same start time
|
||||
.GroupBy(s => s.StartTime).Select(g => g.Last()).OrderBy(s => s.StartTime);
|
||||
|
||||
DefaultControlPoints.AddRange(timingChanges);
|
||||
|
||||
// If we have no control points, add a default one
|
||||
if (DefaultControlPoints.Count == 0)
|
||||
DefaultControlPoints.Add(new MultiplierControlPoint());
|
||||
|
||||
DefaultControlPoints.ForEach(c => applySpeedAdjustment(c, Playfield));
|
||||
}
|
||||
|
||||
private void applySpeedAdjustment(MultiplierControlPoint controlPoint, ScrollingPlayfield playfield)
|
||||
{
|
||||
playfield.HitObjects.AddControlPoint(controlPoint);
|
||||
playfield.NestedPlayfields?.OfType<ScrollingPlayfield>().ForEach(p => applySpeedAdjustment(controlPoint, p));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a <see cref="MultiplierControlPoint"/> with the default timing change/difficulty change from the beatmap at a time.
|
||||
/// </summary>
|
||||
/// <param name="time">The time to create the control point at.</param>
|
||||
/// <returns>The default <see cref="MultiplierControlPoint"/> at <paramref name="time"/>.</returns>
|
||||
public MultiplierControlPoint CreateControlPointAt(double time)
|
||||
{
|
||||
if (DefaultControlPoints.Count == 0)
|
||||
return new MultiplierControlPoint(time);
|
||||
|
||||
int index = DefaultControlPoints.BinarySearch(new MultiplierControlPoint(time));
|
||||
if (index < 0)
|
||||
return new MultiplierControlPoint(time);
|
||||
|
||||
return new MultiplierControlPoint(time, DefaultControlPoints[index].DeepClone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,33 +1,33 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.UI.Scrolling.Visualisers
|
||||
{
|
||||
public interface ISpeedChangeVisualiser
|
||||
{
|
||||
/// <summary>
|
||||
/// Computes the states of <see cref="DrawableHitObject"/>s that are constant, such as lifetime and spatial length.
|
||||
/// This is invoked once whenever <paramref name="timeRange"/> or <paramref name="length"/> changes.
|
||||
/// </summary>
|
||||
/// <param name="hitObjects">The <see cref="DrawableHitObject"/>s whose states should be computed.</param>
|
||||
/// <param name="direction">The scrolling direction.</param>
|
||||
/// <param name="timeRange">The duration required to scroll through one length of the screen before any control point adjustments.</param>
|
||||
/// <param name="length">The length of the screen that is scrolled through.</param>
|
||||
void ComputeInitialStates(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double timeRange, Vector2 length);
|
||||
|
||||
/// <summary>
|
||||
/// Computes the states of <see cref="DrawableHitObject"/>s that change depending on <paramref name="currentTime"/>, such as position.
|
||||
/// This is invoked once per frame.
|
||||
/// </summary>
|
||||
/// <param name="hitObjects">The <see cref="DrawableHitObject"/>s whose states should be computed.</param>
|
||||
/// <param name="direction">The scrolling direction.</param>
|
||||
/// <param name="currentTime">The current time.</param>
|
||||
/// <param name="timeRange">The duration required to scroll through one length of the screen before any control point adjustments.</param>
|
||||
/// <param name="length">The length of the screen that is scrolled through.</param>
|
||||
void ComputePositions(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length);
|
||||
}
|
||||
}
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.UI.Scrolling.Visualisers
|
||||
{
|
||||
public interface ISpeedChangeVisualiser
|
||||
{
|
||||
/// <summary>
|
||||
/// Computes the states of <see cref="DrawableHitObject"/>s that are constant, such as lifetime and spatial length.
|
||||
/// This is invoked once whenever <paramref name="timeRange"/> or <paramref name="length"/> changes.
|
||||
/// </summary>
|
||||
/// <param name="hitObjects">The <see cref="DrawableHitObject"/>s whose states should be computed.</param>
|
||||
/// <param name="direction">The scrolling direction.</param>
|
||||
/// <param name="timeRange">The duration required to scroll through one length of the screen before any control point adjustments.</param>
|
||||
/// <param name="length">The length of the screen that is scrolled through.</param>
|
||||
void ComputeInitialStates(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double timeRange, Vector2 length);
|
||||
|
||||
/// <summary>
|
||||
/// Computes the states of <see cref="DrawableHitObject"/>s that change depending on <paramref name="currentTime"/>, such as position.
|
||||
/// This is invoked once per frame.
|
||||
/// </summary>
|
||||
/// <param name="hitObjects">The <see cref="DrawableHitObject"/>s whose states should be computed.</param>
|
||||
/// <param name="direction">The scrolling direction.</param>
|
||||
/// <param name="currentTime">The current time.</param>
|
||||
/// <param name="timeRange">The duration required to scroll through one length of the screen before any control point adjustments.</param>
|
||||
/// <param name="length">The length of the screen that is scrolled through.</param>
|
||||
void ComputePositions(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length);
|
||||
}
|
||||
}
|
||||
|
@ -1,80 +1,80 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Lists;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Timing;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.UI.Scrolling.Visualisers
|
||||
{
|
||||
public class OverlappingSpeedChangeVisualiser : ISpeedChangeVisualiser
|
||||
{
|
||||
private readonly SortedList<MultiplierControlPoint> controlPoints;
|
||||
|
||||
public OverlappingSpeedChangeVisualiser(SortedList<MultiplierControlPoint> controlPoints)
|
||||
{
|
||||
this.controlPoints = controlPoints;
|
||||
}
|
||||
|
||||
public void ComputeInitialStates(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double timeRange, Vector2 length)
|
||||
{
|
||||
foreach (var obj in hitObjects)
|
||||
{
|
||||
var controlPoint = controlPointAt(obj.HitObject.StartTime);
|
||||
obj.LifetimeStart = obj.HitObject.StartTime - timeRange / controlPoint.Multiplier;
|
||||
|
||||
if (obj.HasNestedHitObjects)
|
||||
{
|
||||
ComputeInitialStates(obj.NestedHitObjects, direction, timeRange, length);
|
||||
ComputePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ComputePositions(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length)
|
||||
{
|
||||
foreach (var obj in hitObjects)
|
||||
{
|
||||
var controlPoint = controlPointAt(obj.HitObject.StartTime);
|
||||
|
||||
var position = (obj.HitObject.StartTime - currentTime) * controlPoint.Multiplier / timeRange;
|
||||
|
||||
switch (direction)
|
||||
{
|
||||
case ScrollingDirection.Up:
|
||||
obj.Y = (float)(position * length.Y);
|
||||
break;
|
||||
case ScrollingDirection.Down:
|
||||
obj.Y = (float)(-position * length.Y);
|
||||
break;
|
||||
case ScrollingDirection.Left:
|
||||
obj.X = (float)(position * length.X);
|
||||
break;
|
||||
case ScrollingDirection.Right:
|
||||
obj.X = (float)(-position * length.X);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly MultiplierControlPoint searchPoint = new MultiplierControlPoint();
|
||||
private MultiplierControlPoint controlPointAt(double time)
|
||||
{
|
||||
if (controlPoints.Count == 0)
|
||||
return new MultiplierControlPoint(double.NegativeInfinity);
|
||||
|
||||
if (time < controlPoints[0].StartTime)
|
||||
return controlPoints[0];
|
||||
|
||||
searchPoint.StartTime = time;
|
||||
int index = controlPoints.BinarySearch(searchPoint);
|
||||
|
||||
if (index < 0)
|
||||
index = ~index - 1;
|
||||
|
||||
return controlPoints[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Lists;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Timing;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.UI.Scrolling.Visualisers
|
||||
{
|
||||
public class OverlappingSpeedChangeVisualiser : ISpeedChangeVisualiser
|
||||
{
|
||||
private readonly SortedList<MultiplierControlPoint> controlPoints;
|
||||
|
||||
public OverlappingSpeedChangeVisualiser(SortedList<MultiplierControlPoint> controlPoints)
|
||||
{
|
||||
this.controlPoints = controlPoints;
|
||||
}
|
||||
|
||||
public void ComputeInitialStates(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double timeRange, Vector2 length)
|
||||
{
|
||||
foreach (var obj in hitObjects)
|
||||
{
|
||||
var controlPoint = controlPointAt(obj.HitObject.StartTime);
|
||||
obj.LifetimeStart = obj.HitObject.StartTime - timeRange / controlPoint.Multiplier;
|
||||
|
||||
if (obj.HasNestedHitObjects)
|
||||
{
|
||||
ComputeInitialStates(obj.NestedHitObjects, direction, timeRange, length);
|
||||
ComputePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ComputePositions(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length)
|
||||
{
|
||||
foreach (var obj in hitObjects)
|
||||
{
|
||||
var controlPoint = controlPointAt(obj.HitObject.StartTime);
|
||||
|
||||
var position = (obj.HitObject.StartTime - currentTime) * controlPoint.Multiplier / timeRange;
|
||||
|
||||
switch (direction)
|
||||
{
|
||||
case ScrollingDirection.Up:
|
||||
obj.Y = (float)(position * length.Y);
|
||||
break;
|
||||
case ScrollingDirection.Down:
|
||||
obj.Y = (float)(-position * length.Y);
|
||||
break;
|
||||
case ScrollingDirection.Left:
|
||||
obj.X = (float)(position * length.X);
|
||||
break;
|
||||
case ScrollingDirection.Right:
|
||||
obj.X = (float)(-position * length.X);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly MultiplierControlPoint searchPoint = new MultiplierControlPoint();
|
||||
private MultiplierControlPoint controlPointAt(double time)
|
||||
{
|
||||
if (controlPoints.Count == 0)
|
||||
return new MultiplierControlPoint(double.NegativeInfinity);
|
||||
|
||||
if (time < controlPoints[0].StartTime)
|
||||
return controlPoints[0];
|
||||
|
||||
searchPoint.StartTime = time;
|
||||
int index = controlPoints.BinarySearch(searchPoint);
|
||||
|
||||
if (index < 0)
|
||||
index = ~index - 1;
|
||||
|
||||
return controlPoints[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,103 +1,103 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Timing;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.UI.Scrolling.Visualisers
|
||||
{
|
||||
public class SequentialSpeedChangeVisualiser : ISpeedChangeVisualiser
|
||||
{
|
||||
private readonly Dictionary<DrawableHitObject, double> hitObjectPositions = new Dictionary<DrawableHitObject, double>();
|
||||
|
||||
private readonly IReadOnlyList<MultiplierControlPoint> controlPoints;
|
||||
|
||||
public SequentialSpeedChangeVisualiser(IReadOnlyList<MultiplierControlPoint> controlPoints)
|
||||
{
|
||||
this.controlPoints = controlPoints;
|
||||
}
|
||||
|
||||
public void ComputeInitialStates(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double timeRange, Vector2 length)
|
||||
{
|
||||
foreach (var obj in hitObjects)
|
||||
{
|
||||
var startPosition = hitObjectPositions[obj] = positionAt(obj.HitObject.StartTime, timeRange);
|
||||
|
||||
obj.LifetimeStart = obj.HitObject.StartTime - timeRange - 1000;
|
||||
|
||||
if (obj.HitObject is IHasEndTime endTime)
|
||||
{
|
||||
var diff = positionAt(endTime.EndTime, timeRange) - startPosition;
|
||||
|
||||
switch (direction)
|
||||
{
|
||||
case ScrollingDirection.Up:
|
||||
case ScrollingDirection.Down:
|
||||
obj.Height = (float)(diff * length.Y);
|
||||
break;
|
||||
case ScrollingDirection.Left:
|
||||
case ScrollingDirection.Right:
|
||||
obj.Width = (float)(diff * length.X);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (obj.HasNestedHitObjects)
|
||||
{
|
||||
ComputeInitialStates(obj.NestedHitObjects, direction, timeRange, length);
|
||||
ComputePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ComputePositions(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length)
|
||||
{
|
||||
var timelinePosition = positionAt(currentTime, timeRange);
|
||||
|
||||
foreach (var obj in hitObjects)
|
||||
{
|
||||
var finalPosition = hitObjectPositions[obj] - timelinePosition;
|
||||
|
||||
switch (direction)
|
||||
{
|
||||
case ScrollingDirection.Up:
|
||||
obj.Y = (float)(finalPosition * length.Y);
|
||||
break;
|
||||
case ScrollingDirection.Down:
|
||||
obj.Y = (float)(-finalPosition * length.Y);
|
||||
break;
|
||||
case ScrollingDirection.Left:
|
||||
obj.X = (float)(finalPosition * length.X);
|
||||
break;
|
||||
case ScrollingDirection.Right:
|
||||
obj.X = (float)(-finalPosition * length.X);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private double positionAt(double time, double timeRange)
|
||||
{
|
||||
double length = 0;
|
||||
for (int i = 0; i < controlPoints.Count; i++)
|
||||
{
|
||||
var current = controlPoints[i];
|
||||
var next = i < controlPoints.Count - 1 ? controlPoints[i + 1] : null;
|
||||
|
||||
if (i > 0 && current.StartTime > time)
|
||||
continue;
|
||||
|
||||
// Duration of the current control point
|
||||
var currentDuration = (next?.StartTime ?? double.PositiveInfinity) - current.StartTime;
|
||||
|
||||
length += Math.Min(currentDuration, time - current.StartTime) * current.Multiplier / timeRange;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Timing;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.UI.Scrolling.Visualisers
|
||||
{
|
||||
public class SequentialSpeedChangeVisualiser : ISpeedChangeVisualiser
|
||||
{
|
||||
private readonly Dictionary<DrawableHitObject, double> hitObjectPositions = new Dictionary<DrawableHitObject, double>();
|
||||
|
||||
private readonly IReadOnlyList<MultiplierControlPoint> controlPoints;
|
||||
|
||||
public SequentialSpeedChangeVisualiser(IReadOnlyList<MultiplierControlPoint> controlPoints)
|
||||
{
|
||||
this.controlPoints = controlPoints;
|
||||
}
|
||||
|
||||
public void ComputeInitialStates(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double timeRange, Vector2 length)
|
||||
{
|
||||
foreach (var obj in hitObjects)
|
||||
{
|
||||
var startPosition = hitObjectPositions[obj] = positionAt(obj.HitObject.StartTime, timeRange);
|
||||
|
||||
obj.LifetimeStart = obj.HitObject.StartTime - timeRange - 1000;
|
||||
|
||||
if (obj.HitObject is IHasEndTime endTime)
|
||||
{
|
||||
var diff = positionAt(endTime.EndTime, timeRange) - startPosition;
|
||||
|
||||
switch (direction)
|
||||
{
|
||||
case ScrollingDirection.Up:
|
||||
case ScrollingDirection.Down:
|
||||
obj.Height = (float)(diff * length.Y);
|
||||
break;
|
||||
case ScrollingDirection.Left:
|
||||
case ScrollingDirection.Right:
|
||||
obj.Width = (float)(diff * length.X);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (obj.HasNestedHitObjects)
|
||||
{
|
||||
ComputeInitialStates(obj.NestedHitObjects, direction, timeRange, length);
|
||||
ComputePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ComputePositions(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length)
|
||||
{
|
||||
var timelinePosition = positionAt(currentTime, timeRange);
|
||||
|
||||
foreach (var obj in hitObjects)
|
||||
{
|
||||
var finalPosition = hitObjectPositions[obj] - timelinePosition;
|
||||
|
||||
switch (direction)
|
||||
{
|
||||
case ScrollingDirection.Up:
|
||||
obj.Y = (float)(finalPosition * length.Y);
|
||||
break;
|
||||
case ScrollingDirection.Down:
|
||||
obj.Y = (float)(-finalPosition * length.Y);
|
||||
break;
|
||||
case ScrollingDirection.Left:
|
||||
obj.X = (float)(finalPosition * length.X);
|
||||
break;
|
||||
case ScrollingDirection.Right:
|
||||
obj.X = (float)(-finalPosition * length.X);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private double positionAt(double time, double timeRange)
|
||||
{
|
||||
double length = 0;
|
||||
for (int i = 0; i < controlPoints.Count; i++)
|
||||
{
|
||||
var current = controlPoints[i];
|
||||
var next = i < controlPoints.Count - 1 ? controlPoints[i + 1] : null;
|
||||
|
||||
if (i > 0 && current.StartTime > time)
|
||||
continue;
|
||||
|
||||
// Duration of the current control point
|
||||
var currentDuration = (next?.StartTime ?? double.PositiveInfinity) - current.StartTime;
|
||||
|
||||
length += Math.Min(currentDuration, time - current.StartTime) * current.Multiplier / timeRange;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user