mirror of
https://github.com/ppy/osu.git
synced 2025-01-13 09:23:06 +08:00
Initial game-wide replacement of scrolling playfields
This commit is contained in:
parent
b11f4ab834
commit
f34131f8f4
@ -24,14 +24,13 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class DrawableCatchHitObject : DrawableScrollingHitObject<CatchHitObject>
|
||||
public abstract class DrawableCatchHitObject : DrawableHitObject<CatchHitObject>
|
||||
{
|
||||
protected DrawableCatchHitObject(CatchHitObject hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
RelativePositionAxes = Axes.Both;
|
||||
RelativePositionAxes = Axes.X;
|
||||
X = hitObject.X;
|
||||
Y = (float)HitObject.StartTime;
|
||||
}
|
||||
|
||||
public Func<CatchHitObject, bool> CheckPosition;
|
||||
|
@ -23,12 +23,10 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
private readonly CatcherArea catcherArea;
|
||||
|
||||
public CatchPlayfield(BeatmapDifficulty difficulty)
|
||||
: base(Axes.Y)
|
||||
: base(Direction.Vertical)
|
||||
{
|
||||
Container explodingFruitContainer;
|
||||
|
||||
Reversed.Value = true;
|
||||
|
||||
Anchor = Anchor.TopCentre;
|
||||
Origin = Anchor.TopCentre;
|
||||
|
||||
|
@ -95,7 +95,6 @@ namespace osu.Game.Rulesets.Mania
|
||||
new ModCinema(),
|
||||
},
|
||||
},
|
||||
new ManiaModGravity()
|
||||
};
|
||||
|
||||
default:
|
||||
|
@ -1,23 +0,0 @@
|
||||
// Copyright (c) 2007-2017 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.Mania.UI;
|
||||
using osu.Game.Rulesets.Timing;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
/// <summary>
|
||||
/// A type of mod which generates speed adjustments that scroll the hit objects and bar lines.
|
||||
/// </summary>
|
||||
internal interface IGenerateSpeedAdjustments
|
||||
{
|
||||
/// <summary>
|
||||
/// Applies this mod to a hit renderer.
|
||||
/// </summary>
|
||||
/// <param name="rulesetContainer">The hit renderer to apply to.</param>
|
||||
/// <param name="hitObjectTimingChanges">The per-column list of speed adjustments for hit objects.</param>
|
||||
/// <param name="barlineTimingChanges">The list of speed adjustments for bar lines.</param>
|
||||
void ApplyToRulesetContainer(ManiaRulesetContainer rulesetContainer, ref List<SpeedAdjustmentContainer>[] hitObjectTimingChanges, ref List<SpeedAdjustmentContainer> barlineTimingChanges);
|
||||
}
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
// Copyright (c) 2007-2017 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.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mania.Timing;
|
||||
using osu.Game.Rulesets.Timing;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public class ManiaModGravity : Mod, IGenerateSpeedAdjustments
|
||||
{
|
||||
public override string Name => "Gravity";
|
||||
public override string ShortenedName => "GR";
|
||||
|
||||
public override double ScoreMultiplier => 0;
|
||||
|
||||
public override FontAwesome Icon => FontAwesome.fa_sort_desc;
|
||||
|
||||
public void ApplyToRulesetContainer(ManiaRulesetContainer rulesetContainer, ref List<SpeedAdjustmentContainer>[] hitObjectTimingChanges, ref List<SpeedAdjustmentContainer> barlineTimingChanges)
|
||||
{
|
||||
// We have to generate one speed adjustment per hit object for gravity
|
||||
foreach (ManiaHitObject obj in rulesetContainer.Objects.OfType<ManiaHitObject>())
|
||||
{
|
||||
MultiplierControlPoint controlPoint = rulesetContainer.CreateControlPointAt(obj.StartTime);
|
||||
// Beat length has too large of an effect for gravity, so we'll force it to a constant value for now
|
||||
controlPoint.TimingPoint.BeatLength = 1000;
|
||||
|
||||
hitObjectTimingChanges[obj.Column].Add(new ManiaSpeedAdjustmentContainer(controlPoint, ScrollingAlgorithm.Gravity));
|
||||
}
|
||||
|
||||
// Like with hit objects, we need to generate one speed adjustment per bar line
|
||||
foreach (DrawableBarLine barLine in rulesetContainer.BarLines)
|
||||
{
|
||||
var controlPoint = rulesetContainer.CreateControlPointAt(barLine.HitObject.StartTime);
|
||||
// Beat length has too large of an effect for gravity, so we'll force it to a constant value for now
|
||||
controlPoint.TimingPoint.BeatLength = 1000;
|
||||
|
||||
barlineTimingChanges.Add(new ManiaSpeedAdjustmentContainer(controlPoint, ScrollingAlgorithm.Gravity));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,13 +1,12 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
{
|
||||
public abstract class DrawableManiaHitObject<TObject> : DrawableScrollingHitObject<ManiaHitObject>
|
||||
public abstract class DrawableManiaHitObject<TObject> : DrawableHitObject<ManiaHitObject>
|
||||
where TObject : ManiaHitObject
|
||||
{
|
||||
/// <summary>
|
||||
@ -20,7 +19,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
protected DrawableManiaHitObject(TObject hitObject, ManiaAction? action = null)
|
||||
: base(hitObject)
|
||||
{
|
||||
RelativePositionAxes = Axes.Y;
|
||||
HitObject = hitObject;
|
||||
|
||||
if (action != null)
|
||||
|
@ -5,16 +5,13 @@ using System;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Rulesets.Mania.Judgements;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Mania.Timing;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.Timing;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Tests
|
||||
@ -44,10 +41,10 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
AddStep("Right special style", () => createPlayfield(8, SpecialColumnPosition.Right));
|
||||
AddStep("Reversed", () => createPlayfield(4, SpecialColumnPosition.Normal, true));
|
||||
|
||||
AddStep("Notes with input", () => createPlayfieldWithNotes(false));
|
||||
AddStep("Notes with input (reversed)", () => createPlayfieldWithNotes(false, true));
|
||||
AddStep("Notes with gravity", () => createPlayfieldWithNotes(true));
|
||||
AddStep("Notes with gravity (reversed)", () => createPlayfieldWithNotes(true, true));
|
||||
AddStep("Notes with input", () => createPlayfieldWithNotes());
|
||||
AddStep("Notes with input (reversed)", () => createPlayfieldWithNotes(true));
|
||||
AddStep("Notes with gravity", () => createPlayfieldWithNotes());
|
||||
AddStep("Notes with gravity (reversed)", () => createPlayfieldWithNotes(true));
|
||||
|
||||
AddStep("Hit explosion", () =>
|
||||
{
|
||||
@ -70,11 +67,6 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
maniaRuleset = rulesets.GetRuleset(3);
|
||||
}
|
||||
|
||||
private SpeedAdjustmentContainer createTimingChange(double time, bool gravity) => new ManiaSpeedAdjustmentContainer(new MultiplierControlPoint(time)
|
||||
{
|
||||
TimingPoint = { BeatLength = 1000 }
|
||||
}, gravity ? ScrollingAlgorithm.Gravity : ScrollingAlgorithm.Basic);
|
||||
|
||||
private ManiaPlayfield createPlayfield(int cols, SpecialColumnPosition specialPos, bool inverted = false)
|
||||
{
|
||||
Clear();
|
||||
@ -95,7 +87,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
return playfield;
|
||||
}
|
||||
|
||||
private void createPlayfieldWithNotes(bool gravity, bool inverted = false)
|
||||
private void createPlayfieldWithNotes(bool inverted = false)
|
||||
{
|
||||
Clear();
|
||||
|
||||
@ -114,23 +106,14 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
|
||||
playfield.Inverted.Value = inverted;
|
||||
|
||||
if (!gravity)
|
||||
playfield.Columns.ForEach(c => c.Add(createTimingChange(0, false)));
|
||||
|
||||
for (double t = start_time; t <= start_time + duration; t += 100)
|
||||
{
|
||||
if (gravity)
|
||||
playfield.Columns.ElementAt(0).Add(createTimingChange(t, true));
|
||||
|
||||
playfield.Add(new DrawableNote(new Note
|
||||
{
|
||||
StartTime = t,
|
||||
Column = 0
|
||||
}, ManiaAction.Key1));
|
||||
|
||||
if (gravity)
|
||||
playfield.Columns.ElementAt(3).Add(createTimingChange(t, true));
|
||||
|
||||
playfield.Add(new DrawableNote(new Note
|
||||
{
|
||||
StartTime = t,
|
||||
@ -138,9 +121,6 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
}, ManiaAction.Key4));
|
||||
}
|
||||
|
||||
if (gravity)
|
||||
playfield.Columns.ElementAt(1).Add(createTimingChange(start_time, true));
|
||||
|
||||
playfield.Add(new DrawableHoldNote(new HoldNote
|
||||
{
|
||||
StartTime = start_time,
|
||||
@ -148,9 +128,6 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
Column = 1
|
||||
}, ManiaAction.Key2));
|
||||
|
||||
if (gravity)
|
||||
playfield.Columns.ElementAt(2).Add(createTimingChange(start_time, true));
|
||||
|
||||
playfield.Add(new DrawableHoldNote(new HoldNote
|
||||
{
|
||||
StartTime = start_time,
|
||||
|
@ -1,60 +0,0 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Timing;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Timing
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="ScrollingContainer"/> that emulates a form of gravity where hit objects speed up over time.
|
||||
/// </summary>
|
||||
internal class GravityScrollingContainer : ScrollingContainer
|
||||
{
|
||||
private readonly MultiplierControlPoint controlPoint;
|
||||
|
||||
public GravityScrollingContainer(MultiplierControlPoint controlPoint)
|
||||
{
|
||||
this.controlPoint = controlPoint;
|
||||
}
|
||||
|
||||
protected override void UpdateAfterChildren()
|
||||
{
|
||||
base.UpdateAfterChildren();
|
||||
|
||||
// The gravity-adjusted start position
|
||||
float startPos = (float)computeGravityTime(controlPoint.StartTime);
|
||||
// The gravity-adjusted end position
|
||||
float endPos = (float)computeGravityTime(controlPoint.StartTime + RelativeChildSize.Y);
|
||||
|
||||
Y = startPos;
|
||||
Height = endPos - startPos;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies gravity to a time value based on the current time.
|
||||
/// </summary>
|
||||
/// <param name="time">The time value gravity should be applied to.</param>
|
||||
/// <returns>The time after gravity is applied to <paramref name="time"/>.</returns>
|
||||
private double computeGravityTime(double time)
|
||||
{
|
||||
double relativeTime = relativeTimeAt(time);
|
||||
|
||||
// The sign of the relative time, this is used to apply backwards acceleration leading into startTime
|
||||
double sign = relativeTime < 0 ? -1 : 1;
|
||||
|
||||
return VisibleTimeRange - acceleration * relativeTime * relativeTime * sign;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The acceleration due to "gravity" of the content of this container.
|
||||
/// </summary>
|
||||
private double acceleration => 1 / VisibleTimeRange;
|
||||
|
||||
/// <summary>
|
||||
/// Computes the current time relative to <paramref name="time"/>, accounting for <see cref="ScrollingContainer.VisibleTimeRange"/>.
|
||||
/// </summary>
|
||||
/// <param name="time">The non-offset time.</param>
|
||||
/// <returns>The current time relative to <paramref name="time"/> - <see cref="ScrollingContainer.VisibleTimeRange"/>. </returns>
|
||||
private double relativeTimeAt(double time) => Time.Current - time + VisibleTimeRange;
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Timing;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Timing
|
||||
{
|
||||
public class ManiaSpeedAdjustmentContainer : SpeedAdjustmentContainer
|
||||
{
|
||||
private readonly ScrollingAlgorithm scrollingAlgorithm;
|
||||
|
||||
public ManiaSpeedAdjustmentContainer(MultiplierControlPoint timingSection, ScrollingAlgorithm scrollingAlgorithm)
|
||||
: base(timingSection)
|
||||
{
|
||||
this.scrollingAlgorithm = scrollingAlgorithm;
|
||||
}
|
||||
|
||||
protected override ScrollingContainer CreateScrollingContainer()
|
||||
{
|
||||
switch (scrollingAlgorithm)
|
||||
{
|
||||
default:
|
||||
return base.CreateScrollingContainer();
|
||||
case ScrollingAlgorithm.Gravity:
|
||||
return new GravityScrollingContainer(ControlPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Timing
|
||||
{
|
||||
public enum ScrollingAlgorithm
|
||||
{
|
||||
/// <summary>
|
||||
/// Basic scrolling algorithm based on the timing section time. This is the default algorithm.
|
||||
/// </summary>
|
||||
Basic,
|
||||
/// <summary>
|
||||
/// Emulating a form of gravity where hit objects speed up over time.
|
||||
/// </summary>
|
||||
Gravity
|
||||
}
|
||||
}
|
@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
private const float opacity_pressed = 0.25f;
|
||||
|
||||
public Column()
|
||||
: base(Axes.Y)
|
||||
: base(Direction.Vertical)
|
||||
{
|
||||
Width = column_width;
|
||||
|
||||
|
@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
private readonly int columnCount;
|
||||
|
||||
public ManiaPlayfield(int columnCount)
|
||||
: base(Axes.Y)
|
||||
: base(Direction.Vertical)
|
||||
{
|
||||
this.columnCount = columnCount;
|
||||
|
||||
|
@ -17,12 +17,10 @@ using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Mania.Replays;
|
||||
using osu.Game.Rulesets.Mania.Scoring;
|
||||
using osu.Game.Rulesets.Mania.Timing;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.Timing;
|
||||
using osu.Game.Rulesets.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.UI
|
||||
@ -121,8 +119,6 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
|
||||
protected override Vector2 GetPlayfieldAspectAdjust() => new Vector2(1, 0.8f);
|
||||
|
||||
protected override SpeedAdjustmentContainer CreateSpeedAdjustmentContainer(MultiplierControlPoint controlPoint) => new ManiaSpeedAdjustmentContainer(controlPoint, ScrollingAlgorithm.Basic);
|
||||
|
||||
protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay, this);
|
||||
}
|
||||
}
|
||||
|
@ -62,7 +62,6 @@
|
||||
<Compile Include="Judgements\HoldNoteTickJudgement.cs" />
|
||||
<Compile Include="Judgements\ManiaJudgement.cs" />
|
||||
<Compile Include="ManiaDifficultyCalculator.cs" />
|
||||
<Compile Include="Mods\IGenerateSpeedAdjustments.cs" />
|
||||
<Compile Include="Objects\Drawables\DrawableBarLine.cs" />
|
||||
<Compile Include="Objects\Drawables\DrawableHoldNote.cs" />
|
||||
<Compile Include="Objects\Drawables\DrawableHoldNoteTick.cs" />
|
||||
@ -88,8 +87,6 @@
|
||||
<Compile Include="Tests\TestCaseManiaHitObjects.cs" />
|
||||
<Compile Include="Tests\TestCaseManiaPlayfield.cs" />
|
||||
<Compile Include="Tests\TestCasePerformancePoints.cs" />
|
||||
<Compile Include="Timing\GravityScrollingContainer.cs" />
|
||||
<Compile Include="Timing\ScrollingAlgorithm.cs" />
|
||||
<Compile Include="UI\Column.cs" />
|
||||
<Compile Include="UI\DrawableManiaJudgement.cs" />
|
||||
<Compile Include="UI\HitExplosion.cs" />
|
||||
@ -97,9 +94,7 @@
|
||||
<Compile Include="UI\ManiaPlayfield.cs" />
|
||||
<Compile Include="ManiaRuleset.cs" />
|
||||
<Compile Include="Mods\ManiaMod.cs" />
|
||||
<Compile Include="Mods\ManiaModGravity.cs" />
|
||||
<Compile Include="UI\SpecialColumnPosition.cs" />
|
||||
<Compile Include="Timing\ManiaSpeedAdjustmentContainer.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
|
@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
/// <summary>
|
||||
/// A line that scrolls alongside hit objects in the playfield and visualises control points.
|
||||
/// </summary>
|
||||
public class DrawableBarLine : DrawableScrollingHitObject<TaikoHitObject>
|
||||
public class DrawableBarLine : DrawableHitObject<TaikoHitObject>
|
||||
{
|
||||
/// <summary>
|
||||
/// The width of the line tracker.
|
||||
|
@ -12,7 +12,7 @@ using System.Collections.Generic;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
{
|
||||
public abstract class DrawableTaikoHitObject<TaikoHitType> : DrawableScrollingHitObject<TaikoHitObject>, IKeyBindingHandler<TaikoAction>
|
||||
public abstract class DrawableTaikoHitObject<TaikoHitType> : DrawableHitObject<TaikoHitObject>, IKeyBindingHandler<TaikoAction>
|
||||
where TaikoHitType : TaikoHitObject
|
||||
{
|
||||
public override Vector2 OriginPosition => new Vector2(DrawHeight / 2);
|
||||
|
@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
private readonly Box background;
|
||||
|
||||
public TaikoPlayfield(ControlPointInfo controlPoints)
|
||||
: base(Axes.X)
|
||||
: base(Direction.Horizontal)
|
||||
{
|
||||
AddRangeInternal(new Drawable[]
|
||||
{
|
||||
|
@ -8,7 +8,6 @@ using OpenTK;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Lists;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Timing;
|
||||
@ -100,68 +99,7 @@ namespace osu.Game.Tests.Visual
|
||||
});
|
||||
}
|
||||
|
||||
private class ScrollingHitObjectContainer : Playfield.HitObjectContainer
|
||||
{
|
||||
public readonly BindableDouble TimeRange = new BindableDouble
|
||||
{
|
||||
MinValue = 0,
|
||||
MaxValue = double.MaxValue
|
||||
};
|
||||
|
||||
public readonly SortedList<MultiplierControlPoint> ControlPoints = new SortedList<MultiplierControlPoint>();
|
||||
|
||||
private readonly Direction scrollingDirection;
|
||||
|
||||
public ScrollingHitObjectContainer(Direction scrollingDirection)
|
||||
{
|
||||
this.scrollingDirection = scrollingDirection;
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
protected override void UpdateAfterChildren()
|
||||
{
|
||||
base.UpdateAfterChildren();
|
||||
|
||||
var currentMultiplier = controlPointAt(Time.Current);
|
||||
|
||||
foreach (var obj in AliveObjects)
|
||||
{
|
||||
var relativePosition = (Time.Current - obj.HitObject.StartTime) / (TimeRange / currentMultiplier.Multiplier);
|
||||
|
||||
// Todo: We may need to consider scale here
|
||||
var finalPosition = (float)relativePosition * DrawSize;
|
||||
|
||||
switch (scrollingDirection)
|
||||
{
|
||||
case Direction.Horizontal:
|
||||
obj.X = finalPosition.X;
|
||||
break;
|
||||
case Direction.Vertical:
|
||||
obj.Y = finalPosition.Y;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly MultiplierControlPoint searchingPoint = new MultiplierControlPoint();
|
||||
private MultiplierControlPoint controlPointAt(double time)
|
||||
{
|
||||
if (ControlPoints.Count == 0)
|
||||
return new MultiplierControlPoint(double.MinValue);
|
||||
|
||||
if (time < ControlPoints[0].StartTime)
|
||||
return ControlPoints[0];
|
||||
|
||||
searchingPoint.StartTime = time;
|
||||
|
||||
int index = ControlPoints.BinarySearch(searchingPoint);
|
||||
if (index < 0)
|
||||
index = ~index - 1;
|
||||
|
||||
return ControlPoints[index];
|
||||
}
|
||||
}
|
||||
|
||||
private class TestPlayfield : Playfield
|
||||
{
|
||||
@ -169,13 +107,13 @@ namespace osu.Game.Tests.Visual
|
||||
|
||||
public readonly Direction ScrollingDirection;
|
||||
|
||||
public new ScrollingHitObjectContainer HitObjects => (ScrollingHitObjectContainer)base.HitObjects;
|
||||
public new ScrollingPlayfield.ScrollingHitObjectContainer HitObjects => (ScrollingPlayfield.ScrollingHitObjectContainer)base.HitObjects;
|
||||
|
||||
public TestPlayfield(Direction scrollingDirection)
|
||||
{
|
||||
ScrollingDirection = scrollingDirection;
|
||||
|
||||
base.HitObjects = new ScrollingHitObjectContainer(scrollingDirection);
|
||||
base.HitObjects = new ScrollingPlayfield.ScrollingHitObjectContainer(scrollingDirection);
|
||||
HitObjects.TimeRange.BindTo(TimeRange);
|
||||
}
|
||||
}
|
||||
|
@ -1,219 +0,0 @@
|
||||
// Copyright (c) 2007-2017 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 NUnit.Framework;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.Timing;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
/// <summary>
|
||||
/// The most minimal implementation of a playfield with scrolling hit objects.
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public class TestCaseScrollingPlayfield : OsuTestCase
|
||||
{
|
||||
public TestCaseScrollingPlayfield()
|
||||
{
|
||||
Clock = new FramedClock();
|
||||
|
||||
var objects = new List<HitObject>();
|
||||
|
||||
int time = 1500;
|
||||
for (int i = 0; i < 50; i++)
|
||||
{
|
||||
objects.Add(new TestHitObject { StartTime = time });
|
||||
|
||||
time += 500;
|
||||
}
|
||||
|
||||
Beatmap b = new Beatmap
|
||||
{
|
||||
HitObjects = objects,
|
||||
BeatmapInfo = new BeatmapInfo
|
||||
{
|
||||
BaseDifficulty = new BeatmapDifficulty(),
|
||||
Metadata = new BeatmapMetadata()
|
||||
}
|
||||
};
|
||||
|
||||
WorkingBeatmap beatmap = new TestWorkingBeatmap(b);
|
||||
|
||||
TestRulesetContainer horizontalRulesetContainer;
|
||||
Add(horizontalRulesetContainer = new TestRulesetContainer(Axes.X, beatmap, true));
|
||||
|
||||
TestRulesetContainer verticalRulesetContainer;
|
||||
Add(verticalRulesetContainer = new TestRulesetContainer(Axes.Y, beatmap, true));
|
||||
|
||||
AddStep("Reverse direction", () =>
|
||||
{
|
||||
horizontalRulesetContainer.Playfield.Reverse();
|
||||
verticalRulesetContainer.Playfield.Reverse();
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSpeedAdjustmentOrdering()
|
||||
{
|
||||
var hitObjectContainer = new ScrollingPlayfield.ScrollingHitObjectContainer(Axes.X);
|
||||
|
||||
var speedAdjustments = new[]
|
||||
{
|
||||
new SpeedAdjustmentContainer(new MultiplierControlPoint()),
|
||||
new SpeedAdjustmentContainer(new MultiplierControlPoint(1000)
|
||||
{
|
||||
TimingPoint = new TimingControlPoint { BeatLength = 500 }
|
||||
}),
|
||||
new SpeedAdjustmentContainer(new MultiplierControlPoint(2000)
|
||||
{
|
||||
TimingPoint = new TimingControlPoint { BeatLength = 1000 },
|
||||
DifficultyPoint = new DifficultyControlPoint { SpeedMultiplier = 2}
|
||||
}),
|
||||
new SpeedAdjustmentContainer(new MultiplierControlPoint(3000)
|
||||
{
|
||||
TimingPoint = new TimingControlPoint { BeatLength = 1000 },
|
||||
DifficultyPoint = new DifficultyControlPoint { SpeedMultiplier = 1}
|
||||
}),
|
||||
};
|
||||
|
||||
var hitObjects = new[]
|
||||
{
|
||||
new DrawableTestHitObject(Axes.X, new TestHitObject { StartTime = -1000 }),
|
||||
new DrawableTestHitObject(Axes.X, new TestHitObject()),
|
||||
new DrawableTestHitObject(Axes.X, new TestHitObject { StartTime = 1000 }),
|
||||
new DrawableTestHitObject(Axes.X, new TestHitObject { StartTime = 2000 }),
|
||||
new DrawableTestHitObject(Axes.X, new TestHitObject { StartTime = 3000 }),
|
||||
new DrawableTestHitObject(Axes.X, new TestHitObject { StartTime = 4000 }),
|
||||
};
|
||||
|
||||
hitObjects.ForEach(h => hitObjectContainer.Add(h));
|
||||
speedAdjustments.ForEach(hitObjectContainer.AddSpeedAdjustment);
|
||||
|
||||
// The 0th index in hitObjectContainer.SpeedAdjustments is the "default" control point
|
||||
// Check multiplier of the default speed adjustment
|
||||
Assert.AreEqual(1, hitObjectContainer.SpeedAdjustments[0].ControlPoint.Multiplier);
|
||||
Assert.AreEqual(1, speedAdjustments[0].ControlPoint.Multiplier);
|
||||
Assert.AreEqual(2, speedAdjustments[1].ControlPoint.Multiplier);
|
||||
Assert.AreEqual(2, speedAdjustments[2].ControlPoint.Multiplier);
|
||||
Assert.AreEqual(1, speedAdjustments[3].ControlPoint.Multiplier);
|
||||
|
||||
// Check insertion of hit objects
|
||||
Assert.IsTrue(hitObjectContainer.SpeedAdjustments[4].Contains(hitObjects[0]));
|
||||
Assert.IsTrue(hitObjectContainer.SpeedAdjustments[3].Contains(hitObjects[1]));
|
||||
Assert.IsTrue(hitObjectContainer.SpeedAdjustments[2].Contains(hitObjects[2]));
|
||||
Assert.IsTrue(hitObjectContainer.SpeedAdjustments[1].Contains(hitObjects[3]));
|
||||
Assert.IsTrue(hitObjectContainer.SpeedAdjustments[0].Contains(hitObjects[4]));
|
||||
Assert.IsTrue(hitObjectContainer.SpeedAdjustments[0].Contains(hitObjects[5]));
|
||||
|
||||
hitObjectContainer.RemoveSpeedAdjustment(hitObjectContainer.SpeedAdjustments[3]);
|
||||
|
||||
// The hit object contained in this speed adjustment should be resorted into the one occuring before it
|
||||
|
||||
Assert.IsTrue(hitObjectContainer.SpeedAdjustments[3].Contains(hitObjects[1]));
|
||||
}
|
||||
|
||||
private class TestRulesetContainer : ScrollingRulesetContainer<TestPlayfield, TestHitObject>
|
||||
{
|
||||
private readonly Axes scrollingAxes;
|
||||
|
||||
public TestRulesetContainer(Axes scrollingAxes, WorkingBeatmap beatmap, bool isForCurrentRuleset)
|
||||
: base(null, beatmap, isForCurrentRuleset)
|
||||
{
|
||||
this.scrollingAxes = scrollingAxes;
|
||||
}
|
||||
|
||||
public new TestPlayfield Playfield => base.Playfield;
|
||||
|
||||
public override ScoreProcessor CreateScoreProcessor() => new TestScoreProcessor();
|
||||
|
||||
public override PassThroughInputManager CreateInputManager() => new PassThroughInputManager();
|
||||
|
||||
protected override BeatmapConverter<TestHitObject> CreateBeatmapConverter() => new TestBeatmapConverter();
|
||||
|
||||
protected override Playfield CreatePlayfield() => new TestPlayfield(scrollingAxes);
|
||||
|
||||
protected override DrawableHitObject<TestHitObject> GetVisualRepresentation(TestHitObject h) => new DrawableTestHitObject(scrollingAxes, h);
|
||||
}
|
||||
|
||||
private class TestScoreProcessor : ScoreProcessor<TestHitObject>
|
||||
{
|
||||
protected override void OnNewJudgement(Judgement judgement)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private class TestBeatmapConverter : BeatmapConverter<TestHitObject>
|
||||
{
|
||||
protected override IEnumerable<Type> ValidConversionTypes => new[] { typeof(HitObject) };
|
||||
|
||||
protected override IEnumerable<TestHitObject> ConvertHitObject(HitObject original, Beatmap beatmap)
|
||||
{
|
||||
yield return original as TestHitObject;
|
||||
}
|
||||
}
|
||||
|
||||
private class DrawableTestHitObject : DrawableScrollingHitObject<TestHitObject>
|
||||
{
|
||||
public DrawableTestHitObject(Axes scrollingAxes, TestHitObject hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
Anchor = scrollingAxes == Axes.Y ? Anchor.TopCentre : Anchor.CentreLeft;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
Add(new Circle
|
||||
{
|
||||
Size = new Vector2(50)
|
||||
});
|
||||
}
|
||||
|
||||
protected override void UpdateState(ArmedState state)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private class TestPlayfield : ScrollingPlayfield
|
||||
{
|
||||
protected override Container<Drawable> Content => content;
|
||||
private readonly Container<Drawable> content;
|
||||
|
||||
public TestPlayfield(Axes scrollingAxes)
|
||||
: base(scrollingAxes)
|
||||
{
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0.2f
|
||||
},
|
||||
content = new Container { RelativeSizeAxes = Axes.Both }
|
||||
};
|
||||
}
|
||||
|
||||
public void Reverse() => Reversed.Toggle();
|
||||
}
|
||||
|
||||
|
||||
private class TestHitObject : HitObject
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -142,7 +142,6 @@
|
||||
<Compile Include="Visual\TestCaseRoomInspector.cs" />
|
||||
<Compile Include="Visual\TestCaseScoreCounter.cs" />
|
||||
<Compile Include="Visual\TestCaseScrollingHitObjects.cs" />
|
||||
<Compile Include="Visual\TestCaseScrollingPlayfield.cs" />
|
||||
<Compile Include="Visual\TestCaseSettings.cs" />
|
||||
<Compile Include="Visual\TestCaseSkipButton.cs" />
|
||||
<Compile Include="Visual\TestCaseSocial.cs" />
|
||||
|
@ -1,67 +0,0 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
|
||||
namespace osu.Game.Rulesets.Objects.Drawables
|
||||
{
|
||||
/// <summary>
|
||||
/// A basic class that overrides <see cref="DrawableHitObject{TObject}"/> and implements <see cref="IScrollingHitObject"/>.
|
||||
/// This object does not need to have its <see cref="Drawable.RelativePositionAxes"/> set to be able to scroll, as this will
|
||||
/// will be set by the scrolling container that contains it.
|
||||
/// </summary>
|
||||
public abstract class DrawableScrollingHitObject<TObject> : DrawableHitObject<TObject>, IScrollingHitObject
|
||||
where TObject : HitObject
|
||||
{
|
||||
public BindableDouble LifetimeOffset { get; } = new BindableDouble();
|
||||
|
||||
Axes IScrollingHitObject.ScrollingAxes
|
||||
{
|
||||
set
|
||||
{
|
||||
RelativePositionAxes |= value;
|
||||
|
||||
if ((value & Axes.X) > 0)
|
||||
X = (float)HitObject.StartTime;
|
||||
if ((value & Axes.Y) > 0)
|
||||
Y = (float)HitObject.StartTime;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool RemoveWhenNotAlive => false;
|
||||
protected override bool RequiresChildrenUpdate => true;
|
||||
|
||||
protected DrawableScrollingHitObject(TObject hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
}
|
||||
|
||||
private double? lifetimeStart;
|
||||
public override double LifetimeStart
|
||||
{
|
||||
get { return lifetimeStart ?? HitObject.StartTime - LifetimeOffset; }
|
||||
set { lifetimeStart = value; }
|
||||
}
|
||||
|
||||
private double? lifetimeEnd;
|
||||
public override double LifetimeEnd
|
||||
{
|
||||
get
|
||||
{
|
||||
var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
|
||||
return lifetimeEnd ?? endTime + LifetimeOffset;
|
||||
}
|
||||
set { lifetimeEnd = value; }
|
||||
}
|
||||
|
||||
protected override void AddNested(DrawableHitObject<TObject> h)
|
||||
{
|
||||
var scrollingHitObject = h as IScrollingHitObject;
|
||||
scrollingHitObject?.LifetimeOffset.BindTo(LifetimeOffset);
|
||||
|
||||
base.AddNested(h);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Timing
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="ScrollingContainer"/> which scrolls linearly relative to the <see cref="MultiplierControlPoint"/> start time.
|
||||
/// </summary>
|
||||
public class LinearScrollingContainer : ScrollingContainer
|
||||
{
|
||||
private readonly MultiplierControlPoint controlPoint;
|
||||
|
||||
public LinearScrollingContainer(MultiplierControlPoint controlPoint)
|
||||
{
|
||||
this.controlPoint = controlPoint;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if ((ScrollingAxes & Axes.X) > 0) X = (float)(controlPoint.StartTime - Time.Current);
|
||||
if ((ScrollingAxes & Axes.Y) > 0) Y = (float)(controlPoint.StartTime - Time.Current);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,93 +0,0 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Caching;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using OpenTK;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
|
||||
namespace osu.Game.Rulesets.Timing
|
||||
{
|
||||
/// <summary>
|
||||
/// A container that scrolls relative to the current time. Will autosize to the total duration of all contained hit objects along the scrolling axes.
|
||||
/// </summary>
|
||||
public abstract class ScrollingContainer : Container<DrawableHitObject>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the range of time that is visible by the length of the scrolling axes.
|
||||
/// </summary>
|
||||
public readonly BindableDouble VisibleTimeRange = new BindableDouble { Default = 1000 };
|
||||
|
||||
/// <summary>
|
||||
/// The axes through which this <see cref="ScrollingContainer"/> scrolls. This is set by the <see cref="SpeedAdjustmentContainer"/>.
|
||||
/// </summary>
|
||||
internal Axes ScrollingAxes;
|
||||
|
||||
public override bool RemoveWhenNotAlive => false;
|
||||
protected override bool RequiresChildrenUpdate => true;
|
||||
|
||||
/// <summary>
|
||||
/// The control point that defines the speed adjustments for this container. This is set by the <see cref="SpeedAdjustmentContainer"/>.
|
||||
/// </summary>
|
||||
internal MultiplierControlPoint ControlPoint;
|
||||
|
||||
private Cached<double> durationBacking;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="ScrollingContainer"/>.
|
||||
/// </summary>
|
||||
protected ScrollingContainer()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
RelativePositionAxes = Axes.Both;
|
||||
}
|
||||
|
||||
protected override int Compare(Drawable x, Drawable y)
|
||||
{
|
||||
var hX = (DrawableHitObject)x;
|
||||
var hY = (DrawableHitObject)y;
|
||||
|
||||
int result = hY.HitObject.StartTime.CompareTo(hX.HitObject.StartTime);
|
||||
if (result != 0)
|
||||
return result;
|
||||
return base.Compare(y, x);
|
||||
}
|
||||
|
||||
public override void Add(DrawableHitObject drawable)
|
||||
{
|
||||
durationBacking.Invalidate();
|
||||
base.Add(drawable);
|
||||
}
|
||||
|
||||
public override bool Remove(DrawableHitObject drawable)
|
||||
{
|
||||
durationBacking.Invalidate();
|
||||
return base.Remove(drawable);
|
||||
}
|
||||
|
||||
// Todo: This may underestimate the size of the hit object in some cases, but won't be too much of a problem for now
|
||||
private double computeDuration() => Math.Max(0, Children.Select(c => (c.HitObject as IHasEndTime)?.EndTime ?? c.HitObject.StartTime).DefaultIfEmpty().Max() - ControlPoint.StartTime) + 1000;
|
||||
|
||||
/// <summary>
|
||||
/// An approximate total duration of this scrolling container.
|
||||
/// </summary>
|
||||
public double Duration => durationBacking.IsValid ? durationBacking : (durationBacking.Value = computeDuration());
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
RelativeChildOffset = new Vector2((ScrollingAxes & Axes.X) > 0 ? (float)ControlPoint.StartTime : 0, (ScrollingAxes & Axes.Y) > 0 ? (float)ControlPoint.StartTime : 0);
|
||||
|
||||
// We want our size and position-space along the scrolling axes to span our duration to completely enclose all the hit objects
|
||||
Size = new Vector2((ScrollingAxes & Axes.X) > 0 ? (float)Duration : Size.X, (ScrollingAxes & Axes.Y) > 0 ? (float)Duration : Size.Y);
|
||||
// And we need to make sure the hit object's position-space doesn't change due to our resizing
|
||||
RelativeChildSize = Size;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,114 +0,0 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Timing
|
||||
{
|
||||
/// <summary>
|
||||
/// A container that provides the speed adjustments defined by <see cref="MultiplierControlPoint"/>s to affect the scroll speed
|
||||
/// of container <see cref="DrawableHitObject"/>s.
|
||||
/// </summary>
|
||||
public class SpeedAdjustmentContainer : Container<DrawableHitObject>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the range of time that is visible by the length of the scrolling axes.
|
||||
/// </summary>
|
||||
public readonly Bindable<double> VisibleTimeRange = new Bindable<double> { Default = 1000 };
|
||||
|
||||
/// <summary>
|
||||
/// Whether to reverse the scrolling direction is reversed.
|
||||
/// </summary>
|
||||
public readonly BindableBool Reversed = new BindableBool();
|
||||
|
||||
protected override Container<DrawableHitObject> Content => content;
|
||||
private readonly Container<DrawableHitObject> content;
|
||||
|
||||
/// <summary>
|
||||
/// The axes which the content of this container will scroll through.
|
||||
/// </summary>
|
||||
public Axes ScrollingAxes
|
||||
{
|
||||
get { return scrollingContainer.ScrollingAxes; }
|
||||
set { scrollingContainer.ScrollingAxes = value; }
|
||||
}
|
||||
|
||||
public override bool RemoveWhenNotAlive => false;
|
||||
protected override bool RequiresChildrenUpdate => true;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="MultiplierControlPoint"/> that defines the speed adjustments.
|
||||
/// </summary>
|
||||
public readonly MultiplierControlPoint ControlPoint;
|
||||
|
||||
private readonly ScrollingContainer scrollingContainer;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="SpeedAdjustmentContainer"/>.
|
||||
/// </summary>
|
||||
/// <param name="controlPoint">The <see cref="MultiplierControlPoint"/> that defines the speed adjustments.</param>
|
||||
public SpeedAdjustmentContainer(MultiplierControlPoint controlPoint)
|
||||
{
|
||||
ControlPoint = controlPoint;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
scrollingContainer = CreateScrollingContainer();
|
||||
scrollingContainer.ControlPoint = ControlPoint;
|
||||
scrollingContainer.VisibleTimeRange.BindTo(VisibleTimeRange);
|
||||
|
||||
AddInternal(content = scrollingContainer);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
float multiplier = (float)ControlPoint.Multiplier;
|
||||
|
||||
// The speed adjustment happens by modifying our size by the multiplier while maintaining the visible time range as the relatve size for our children
|
||||
Size = new Vector2((ScrollingAxes & Axes.X) > 0 ? multiplier : 1, (ScrollingAxes & Axes.Y) > 0 ? multiplier : 1);
|
||||
|
||||
if (Reversed)
|
||||
{
|
||||
RelativeChildSize = new Vector2((ScrollingAxes & Axes.X) > 0 ? (float)-VisibleTimeRange : 1, (ScrollingAxes & Axes.Y) > 0 ? (float)-VisibleTimeRange : 1);
|
||||
RelativeChildOffset = new Vector2((ScrollingAxes & Axes.X) > 0 ? (float)VisibleTimeRange : 0, (ScrollingAxes & Axes.Y) > 0 ? (float)VisibleTimeRange : 0);
|
||||
Origin = Anchor = Anchor.BottomRight;
|
||||
}
|
||||
else
|
||||
{
|
||||
RelativeChildSize = new Vector2((ScrollingAxes & Axes.X) > 0 ? (float)VisibleTimeRange : 1, (ScrollingAxes & Axes.Y) > 0 ? (float)VisibleTimeRange : 1);
|
||||
RelativeChildOffset = Vector2.Zero;
|
||||
Origin = Anchor = Anchor.TopLeft;
|
||||
}
|
||||
}
|
||||
|
||||
public override double LifetimeStart => ControlPoint.StartTime - VisibleTimeRange;
|
||||
public override double LifetimeEnd => ControlPoint.StartTime + scrollingContainer.Duration + VisibleTimeRange;
|
||||
|
||||
public override void Add(DrawableHitObject drawable)
|
||||
{
|
||||
var scrollingHitObject = drawable as IScrollingHitObject;
|
||||
|
||||
if (scrollingHitObject != null)
|
||||
{
|
||||
scrollingHitObject.LifetimeOffset.BindTo(VisibleTimeRange);
|
||||
scrollingHitObject.ScrollingAxes = ScrollingAxes;
|
||||
}
|
||||
|
||||
base.Add(drawable);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether a point in time falls within this <see cref="SpeedAdjustmentContainer"/>s affecting timespan.
|
||||
/// </summary>
|
||||
public bool CanContain(double startTime) => ControlPoint.StartTime <= startTime;
|
||||
|
||||
/// <summary>
|
||||
/// Creates the <see cref="ScrollingContainer"/> which contains the scrolling <see cref="DrawableHitObject"/>s of this container.
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="ScrollingContainer"/>.</returns>
|
||||
protected virtual ScrollingContainer CreateScrollingContainer() => new LinearScrollingContainer(ControlPoint);
|
||||
}
|
||||
}
|
@ -1,15 +1,13 @@
|
||||
// Copyright (c) 2007-2017 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 System.Linq;
|
||||
using OpenTK.Input;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Transforms;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Lists;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Timing;
|
||||
@ -50,11 +48,6 @@ namespace osu.Game.Rulesets.UI
|
||||
MaxValue = time_span_max
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Whether to reverse the scrolling direction is reversed. Note that this does _not_ invert the hit objects.
|
||||
/// </summary>
|
||||
protected readonly BindableBool Reversed = new BindableBool();
|
||||
|
||||
/// <summary>
|
||||
/// The container that contains the <see cref="SpeedAdjustmentContainer"/>s and <see cref="DrawableHitObject"/>s.
|
||||
/// </summary>
|
||||
@ -65,12 +58,11 @@ namespace osu.Game.Rulesets.UI
|
||||
/// </summary>
|
||||
/// <param name="scrollingAxes">The axes on which <see cref="DrawableHitObject"/>s in this container should scroll.</param>
|
||||
/// <param name="customWidth">Whether we want our internal coordinate system to be scaled to a specified width</param>
|
||||
protected ScrollingPlayfield(Axes scrollingAxes, float? customWidth = null)
|
||||
protected ScrollingPlayfield(Direction scrollingDirection, float? customWidth = null)
|
||||
: base(customWidth)
|
||||
{
|
||||
base.HitObjects = HitObjects = new ScrollingHitObjectContainer(scrollingAxes) { RelativeSizeAxes = Axes.Both };
|
||||
HitObjects.VisibleTimeRange.BindTo(VisibleTimeRange);
|
||||
HitObjects.Reversed.BindTo(Reversed);
|
||||
base.HitObjects = HitObjects = new ScrollingHitObjectContainer(scrollingDirection) { RelativeSizeAxes = Axes.Both };
|
||||
HitObjects.TimeRange.BindTo(VisibleTimeRange);
|
||||
}
|
||||
|
||||
private List<ScrollingPlayfield> nestedPlayfields;
|
||||
@ -131,135 +123,66 @@ namespace osu.Game.Rulesets.UI
|
||||
protected override void ReadIntoStartValue(ScrollingPlayfield d) => StartValue = d.VisibleTimeRange.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A container that provides the foundation for sorting <see cref="DrawableHitObject"/>s into <see cref="SpeedAdjustmentContainer"/>s.
|
||||
/// </summary>
|
||||
public class ScrollingHitObjectContainer : HitObjectContainer
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the range 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 { Default = 1000 };
|
||||
|
||||
/// <summary>
|
||||
/// Whether to reverse the scrolling direction is reversed.
|
||||
/// </summary>
|
||||
public readonly BindableBool Reversed = new BindableBool();
|
||||
|
||||
private readonly SortedContainer speedAdjustments;
|
||||
public IReadOnlyList<SpeedAdjustmentContainer> SpeedAdjustments => speedAdjustments;
|
||||
|
||||
private readonly SpeedAdjustmentContainer defaultSpeedAdjustment;
|
||||
|
||||
private readonly Axes scrollingAxes;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="ScrollingHitObjectContainer"/>.
|
||||
/// </summary>
|
||||
/// <param name="scrollingAxes">The axes upon which hit objects should appear to scroll inside this container.</param>
|
||||
public ScrollingHitObjectContainer(Axes scrollingAxes)
|
||||
public readonly BindableDouble TimeRange = new BindableDouble
|
||||
{
|
||||
this.scrollingAxes = scrollingAxes;
|
||||
MinValue = 0,
|
||||
MaxValue = double.MaxValue
|
||||
};
|
||||
|
||||
AddInternal(speedAdjustments = new SortedContainer { RelativeSizeAxes = Axes.Both });
|
||||
public readonly SortedList<MultiplierControlPoint> ControlPoints = new SortedList<MultiplierControlPoint>();
|
||||
|
||||
// Default speed adjustment
|
||||
AddSpeedAdjustment(defaultSpeedAdjustment = new SpeedAdjustmentContainer(new MultiplierControlPoint(0)));
|
||||
private readonly Direction scrollingDirection;
|
||||
|
||||
public ScrollingHitObjectContainer(Direction scrollingDirection)
|
||||
{
|
||||
this.scrollingDirection = scrollingDirection;
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a <see cref="SpeedAdjustmentContainer"/> to this container, re-sorting all hit objects
|
||||
/// in the last <see cref="SpeedAdjustmentContainer"/> that occurred (time-wise) before it.
|
||||
/// </summary>
|
||||
/// <param name="speedAdjustment">The <see cref="SpeedAdjustmentContainer"/>.</param>
|
||||
public void AddSpeedAdjustment(SpeedAdjustmentContainer speedAdjustment)
|
||||
protected override void UpdateAfterChildren()
|
||||
{
|
||||
speedAdjustment.ScrollingAxes = scrollingAxes;
|
||||
speedAdjustment.VisibleTimeRange.BindTo(VisibleTimeRange);
|
||||
speedAdjustment.Reversed.BindTo(Reversed);
|
||||
base.UpdateAfterChildren();
|
||||
|
||||
if (speedAdjustments.Count > 0)
|
||||
var currentMultiplier = controlPointAt(Time.Current);
|
||||
|
||||
foreach (var obj in AliveObjects)
|
||||
{
|
||||
// We need to re-sort all hit objects in the speed adjustment container prior to figure out if they
|
||||
// should now lie within this one
|
||||
var existingAdjustment = adjustmentContainerAt(speedAdjustment.ControlPoint.StartTime);
|
||||
for (int i = 0; i < existingAdjustment.Count; i++)
|
||||
var relativePosition = (Time.Current - obj.HitObject.StartTime) / (TimeRange / currentMultiplier.Multiplier);
|
||||
|
||||
// Todo: We may need to consider scale here
|
||||
var finalPosition = (float)relativePosition * DrawSize;
|
||||
|
||||
switch (scrollingDirection)
|
||||
{
|
||||
DrawableHitObject hitObject = existingAdjustment[i];
|
||||
|
||||
if (!speedAdjustment.CanContain(hitObject.HitObject.StartTime))
|
||||
continue;
|
||||
|
||||
existingAdjustment.Remove(hitObject);
|
||||
speedAdjustment.Add(hitObject);
|
||||
|
||||
i--;
|
||||
case Direction.Horizontal:
|
||||
obj.X = finalPosition.X;
|
||||
break;
|
||||
case Direction.Vertical:
|
||||
obj.Y = finalPosition.Y;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
speedAdjustments.Add(speedAdjustment);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a <see cref="SpeedAdjustmentContainer"/> from this container, re-sorting all hit objects
|
||||
/// which it contained into new <see cref="SpeedAdjustmentContainer"/>s.
|
||||
/// </summary>
|
||||
/// <param name="speedAdjustment">The <see cref="SpeedAdjustmentContainer"/> to remove.</param>
|
||||
public void RemoveSpeedAdjustment(SpeedAdjustmentContainer speedAdjustment)
|
||||
private readonly MultiplierControlPoint searchingPoint = new MultiplierControlPoint();
|
||||
private MultiplierControlPoint controlPointAt(double time)
|
||||
{
|
||||
if (speedAdjustment == defaultSpeedAdjustment)
|
||||
throw new InvalidOperationException($"The default {nameof(SpeedAdjustmentContainer)} must not be removed.");
|
||||
if (ControlPoints.Count == 0)
|
||||
return new MultiplierControlPoint(double.MinValue);
|
||||
|
||||
if (!speedAdjustments.Remove(speedAdjustment))
|
||||
return;
|
||||
if (time < ControlPoints[0].StartTime)
|
||||
return ControlPoints[0];
|
||||
|
||||
while (speedAdjustment.Count > 0)
|
||||
{
|
||||
DrawableHitObject hitObject = speedAdjustment[0];
|
||||
searchingPoint.StartTime = time;
|
||||
|
||||
speedAdjustment.Remove(hitObject);
|
||||
Add(hitObject);
|
||||
}
|
||||
}
|
||||
int index = ControlPoints.BinarySearch(searchingPoint);
|
||||
if (index < 0)
|
||||
index = ~index - 1;
|
||||
|
||||
public override IEnumerable<DrawableHitObject> Objects => speedAdjustments.SelectMany(s => s.Children);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a hit object to this <see cref="ScrollingHitObjectContainer"/>. The hit objects will be queued to be processed
|
||||
/// new <see cref="SpeedAdjustmentContainer"/>s are added to this <see cref="ScrollingHitObjectContainer"/>.
|
||||
/// </summary>
|
||||
/// <param name="hitObject">The hit object to add.</param>
|
||||
public override void Add(DrawableHitObject hitObject)
|
||||
{
|
||||
if (!(hitObject is IScrollingHitObject))
|
||||
throw new InvalidOperationException($"Hit objects added to a {nameof(ScrollingHitObjectContainer)} must implement {nameof(IScrollingHitObject)}.");
|
||||
|
||||
adjustmentContainerAt(hitObject.HitObject.StartTime).Add(hitObject);
|
||||
}
|
||||
|
||||
public override bool Remove(DrawableHitObject hitObject) => speedAdjustments.Any(s => s.Remove(hitObject));
|
||||
|
||||
/// <summary>
|
||||
/// Finds the <see cref="SpeedAdjustmentContainer"/> which provides the speed adjustment active at a time.
|
||||
/// If there is no <see cref="SpeedAdjustmentContainer"/> active at the time, then the first (time-wise) speed adjustment is returned.
|
||||
/// </summary>
|
||||
/// <param name="time">The time to find the active <see cref="SpeedAdjustmentContainer"/> at.</param>
|
||||
/// <returns>The <see cref="SpeedAdjustmentContainer"/> active at <paramref name="time"/>. Null if there are no speed adjustments.</returns>
|
||||
private SpeedAdjustmentContainer adjustmentContainerAt(double time) => speedAdjustments.FirstOrDefault(c => c.CanContain(time)) ?? defaultSpeedAdjustment;
|
||||
|
||||
private class SortedContainer : Container<SpeedAdjustmentContainer>
|
||||
{
|
||||
protected override int Compare(Drawable x, Drawable y)
|
||||
{
|
||||
var sX = (SpeedAdjustmentContainer)x;
|
||||
var sY = (SpeedAdjustmentContainer)y;
|
||||
|
||||
int result = sY.ControlPoint.StartTime.CompareTo(sX.ControlPoint.StartTime);
|
||||
if (result != 0)
|
||||
return result;
|
||||
return base.Compare(y, x);
|
||||
}
|
||||
return ControlPoints[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
private void applySpeedAdjustment(MultiplierControlPoint controlPoint, ScrollingPlayfield playfield)
|
||||
{
|
||||
playfield.HitObjects.AddSpeedAdjustment(CreateSpeedAdjustmentContainer(controlPoint));
|
||||
playfield.HitObjects.ControlPoints.Add(controlPoint);
|
||||
playfield.NestedPlayfields.ForEach(p => applySpeedAdjustment(controlPoint, p));
|
||||
}
|
||||
|
||||
@ -108,12 +108,5 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
return new MultiplierControlPoint(time, DefaultControlPoints[index].DeepClone());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="SpeedAdjustmentContainer"/> that facilitates the movement of hit objects.
|
||||
/// </summary>
|
||||
/// <param name="controlPoint">The <see cref="MultiplierControlPoint"/> that provides the speed adjustments for the hitobjects.</param>
|
||||
/// <returns>The <see cref="SpeedAdjustmentContainer"/>.</returns>
|
||||
protected virtual SpeedAdjustmentContainer CreateSpeedAdjustmentContainer(MultiplierControlPoint controlPoint) => new SpeedAdjustmentContainer(controlPoint);
|
||||
}
|
||||
}
|
||||
|
@ -614,7 +614,6 @@
|
||||
<Compile Include="Rulesets\Objects\CircularArcApproximator.cs" />
|
||||
<Compile Include="Rulesets\Objects\Drawables\ArmedState.cs" />
|
||||
<Compile Include="Rulesets\Objects\Drawables\DrawableHitObject.cs" />
|
||||
<Compile Include="Rulesets\Objects\Drawables\DrawableScrollingHitObject.cs" />
|
||||
<Compile Include="Rulesets\Scoring\HitResult.cs" />
|
||||
<Compile Include="Rulesets\Objects\Drawables\IDrawableHitObjectWithProxiedApproach.cs" />
|
||||
<Compile Include="Rulesets\Objects\Drawables\IScrollingHitObject.cs" />
|
||||
@ -665,10 +664,7 @@
|
||||
<Compile Include="Rulesets\Scoring\ScoreProcessor.cs" />
|
||||
<Compile Include="Rulesets\Scoring\ScoreRank.cs" />
|
||||
<Compile Include="Rulesets\Scoring\ScoreStore.cs" />
|
||||
<Compile Include="Rulesets\Timing\LinearScrollingContainer.cs" />
|
||||
<Compile Include="Rulesets\Timing\MultiplierControlPoint.cs" />
|
||||
<Compile Include="Rulesets\Timing\ScrollingContainer.cs" />
|
||||
<Compile Include="Rulesets\Timing\SpeedAdjustmentContainer.cs" />
|
||||
<Compile Include="Rulesets\UI\ModIcon.cs" />
|
||||
<Compile Include="Rulesets\UI\Playfield.cs" />
|
||||
<Compile Include="Rulesets\UI\RulesetContainer.cs" />
|
||||
|
Loading…
Reference in New Issue
Block a user