mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 10:12:54 +08:00
Merge branch 'slider-controlpoint-masks' into slider-placement
# Conflicts: # osu.Game.Rulesets.Osu/Edit/Masks/SliderMasks/Components/SliderBodyPiece.cs # osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
This commit is contained in:
commit
08b16be3b8
@ -1,11 +1,14 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Osu.Edit.Masks.SliderMasks;
|
using osu.Game.Rulesets.Osu.Edit.Masks.SliderMasks;
|
||||||
|
using osu.Game.Rulesets.Osu.Edit.Masks.SliderMasks.Components;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
@ -15,6 +18,16 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
public class TestCaseSliderSelectionMask : HitObjectSelectionMaskTestCase
|
public class TestCaseSliderSelectionMask : HitObjectSelectionMaskTestCase
|
||||||
{
|
{
|
||||||
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
|
{
|
||||||
|
typeof(SliderSelectionMask),
|
||||||
|
typeof(SliderCircleSelectionMask),
|
||||||
|
typeof(SliderBodyPiece),
|
||||||
|
typeof(SliderCircle),
|
||||||
|
typeof(ControlPointVisualiser),
|
||||||
|
typeof(ControlPointPiece)
|
||||||
|
};
|
||||||
|
|
||||||
private readonly DrawableSlider drawableObject;
|
private readonly DrawableSlider drawableObject;
|
||||||
|
|
||||||
public TestCaseSliderSelectionMask()
|
public TestCaseSliderSelectionMask()
|
||||||
|
@ -0,0 +1,118 @@
|
|||||||
|
// 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.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Lines;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using OpenTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Edit.Masks.SliderMasks.Components
|
||||||
|
{
|
||||||
|
public class ControlPointPiece : CompositeDrawable
|
||||||
|
{
|
||||||
|
private readonly Slider slider;
|
||||||
|
private readonly int index;
|
||||||
|
|
||||||
|
private readonly Path path;
|
||||||
|
private readonly CircularContainer marker;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OsuColour colours { get; set; }
|
||||||
|
|
||||||
|
public ControlPointPiece(Slider slider, int index)
|
||||||
|
{
|
||||||
|
this.slider = slider;
|
||||||
|
this.index = index;
|
||||||
|
|
||||||
|
Origin = Anchor.Centre;
|
||||||
|
Size = new Vector2(10);
|
||||||
|
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
path = new SmoothPath
|
||||||
|
{
|
||||||
|
BypassAutoSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
PathWidth = 1
|
||||||
|
},
|
||||||
|
marker = new CircularContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Masking = true,
|
||||||
|
Child = new Box { RelativeSizeAxes = Axes.Both }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
Position = slider.StackedPosition + slider.ControlPoints[index];
|
||||||
|
|
||||||
|
marker.Colour = isSegmentSeparator ? colours.Red : colours.Yellow;
|
||||||
|
|
||||||
|
path.ClearVertices();
|
||||||
|
|
||||||
|
if (index != slider.ControlPoints.Length - 1)
|
||||||
|
{
|
||||||
|
path.AddVertex(Vector2.Zero);
|
||||||
|
path.AddVertex(slider.ControlPoints[index + 1] - slider.ControlPoints[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
path.OriginPosition = path.PositionInBoundingBox(Vector2.Zero);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnDragStart(DragStartEvent e) => true;
|
||||||
|
|
||||||
|
protected override bool OnDrag(DragEvent e)
|
||||||
|
{
|
||||||
|
if (index == 0)
|
||||||
|
{
|
||||||
|
// Special handling for the head - only the position of the slider changes
|
||||||
|
slider.Position += e.Delta;
|
||||||
|
|
||||||
|
// Since control points are relative to the position of the slider, they all need to be offset backwards by the delta
|
||||||
|
var newControlPoints = slider.ControlPoints.ToArray();
|
||||||
|
for (int i = 1; i < newControlPoints.Length; i++)
|
||||||
|
newControlPoints[i] -= e.Delta;
|
||||||
|
|
||||||
|
slider.ControlPoints = newControlPoints;
|
||||||
|
slider.Curve.Calculate(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var newControlPoints = slider.ControlPoints.ToArray();
|
||||||
|
newControlPoints[index] += e.Delta;
|
||||||
|
|
||||||
|
slider.ControlPoints = newControlPoints;
|
||||||
|
slider.Curve.Calculate(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnDragEnd(DragEndEvent e) => true;
|
||||||
|
|
||||||
|
private bool isSegmentSeparator
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
bool separator = false;
|
||||||
|
|
||||||
|
if (index < slider.ControlPoints.Length - 1)
|
||||||
|
separator |= slider.ControlPoints[index + 1] == slider.ControlPoints[index];
|
||||||
|
if (index > 0)
|
||||||
|
separator |= slider.ControlPoints[index - 1] == slider.ControlPoints[index];
|
||||||
|
|
||||||
|
return separator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
// 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.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Edit.Masks.SliderMasks.Components
|
||||||
|
{
|
||||||
|
public class ControlPointVisualiser : CompositeDrawable
|
||||||
|
{
|
||||||
|
private readonly Slider slider;
|
||||||
|
|
||||||
|
private readonly Container<ControlPointPiece> pieces;
|
||||||
|
|
||||||
|
public ControlPointVisualiser(Slider slider)
|
||||||
|
{
|
||||||
|
this.slider = slider;
|
||||||
|
|
||||||
|
InternalChild = pieces = new Container<ControlPointPiece> { RelativeSizeAxes = Axes.Both };
|
||||||
|
|
||||||
|
slider.ControlPointsChanged += _ => updateControlPoints();
|
||||||
|
updateControlPoints();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateControlPoints()
|
||||||
|
{
|
||||||
|
while (slider.ControlPoints.Length > pieces.Count)
|
||||||
|
pieces.Add(new ControlPointPiece(slider, pieces.Count));
|
||||||
|
while (slider.ControlPoints.Length < pieces.Count)
|
||||||
|
pieces.Remove(pieces[pieces.Count - 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -16,6 +16,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Masks.SliderMasks.Components
|
|||||||
{
|
{
|
||||||
this.slider = slider;
|
this.slider = slider;
|
||||||
this.position = position;
|
this.position = position;
|
||||||
|
|
||||||
|
slider.ControlPointsChanged += _ => UpdatePosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UpdatePosition()
|
protected override void UpdatePosition()
|
||||||
|
@ -24,6 +24,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Masks.SliderMasks
|
|||||||
new SliderBodyPiece(sliderObject),
|
new SliderBodyPiece(sliderObject),
|
||||||
headMask = new SliderCircleSelectionMask(slider.HeadCircle, sliderObject, SliderPosition.Start),
|
headMask = new SliderCircleSelectionMask(slider.HeadCircle, sliderObject, SliderPosition.Start),
|
||||||
new SliderCircleSelectionMask(slider.TailCircle, sliderObject, SliderPosition.End),
|
new SliderCircleSelectionMask(slider.TailCircle, sliderObject, SliderPosition.End),
|
||||||
|
new ControlPointVisualiser(sliderObject),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,6 +90,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
Body.PathWidth = HitObject.Scale * 64;
|
Body.PathWidth = HitObject.Scale * 64;
|
||||||
Ball.Scale = new Vector2(HitObject.Scale);
|
Ball.Scale = new Vector2(HitObject.Scale);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
slider.ControlPointsChanged += _ => Body.Refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Color4 AccentColour
|
public override Color4 AccentColour
|
||||||
|
@ -16,7 +16,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
{
|
{
|
||||||
this.slider = slider;
|
this.slider = slider;
|
||||||
|
|
||||||
Position = HitObject.Position - slider.Position;
|
h.PositionChanged += _ => updatePosition();
|
||||||
|
slider.ControlPointsChanged += _ => updatePosition();
|
||||||
|
|
||||||
|
updatePosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
@ -33,5 +36,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
public Action<double> OnShake;
|
public Action<double> OnShake;
|
||||||
|
|
||||||
protected override void Shake(double maximumLength) => OnShake?.Invoke(maximumLength);
|
protected override void Shake(double maximumLength) => OnShake?.Invoke(maximumLength);
|
||||||
|
|
||||||
|
private void updatePosition() => Position = HitObject.Position - slider.Position;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
{
|
{
|
||||||
public class DrawableSliderTail : DrawableOsuHitObject, IRequireTracking
|
public class DrawableSliderTail : DrawableOsuHitObject, IRequireTracking
|
||||||
{
|
{
|
||||||
|
private readonly Slider slider;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The judgement text is provided by the <see cref="DrawableSlider"/>.
|
/// The judgement text is provided by the <see cref="DrawableSlider"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -18,6 +20,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
public DrawableSliderTail(Slider slider, SliderTailCircle hitCircle)
|
public DrawableSliderTail(Slider slider, SliderTailCircle hitCircle)
|
||||||
: base(hitCircle)
|
: base(hitCircle)
|
||||||
{
|
{
|
||||||
|
this.slider = slider;
|
||||||
|
|
||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
@ -25,7 +29,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
|
|
||||||
AlwaysPresent = true;
|
AlwaysPresent = true;
|
||||||
|
|
||||||
Position = HitObject.Position - slider.Position;
|
hitCircle.PositionChanged += _ => updatePosition();
|
||||||
|
slider.ControlPointsChanged += _ => updatePosition();
|
||||||
|
|
||||||
|
updatePosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||||
@ -33,5 +40,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
if (!userTriggered && timeOffset >= 0)
|
if (!userTriggered && timeOffset >= 0)
|
||||||
ApplyResult(r => r.Type = Tracking ? HitResult.Great : HitResult.Miss);
|
ApplyResult(r => r.Type = Tracking ? HitResult.Great : HitResult.Miss);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updatePosition() => Position = HitObject.Position - slider.Position;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,15 +45,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
// Generate the entire curve
|
Refresh();
|
||||||
slider.Curve.GetPathToProgress(CurrentCurve, 0, 1);
|
|
||||||
SetVertices(CurrentCurve);
|
|
||||||
|
|
||||||
// The body is sized to the full path size to avoid excessive autosize computations
|
|
||||||
Size = Path.Size;
|
|
||||||
|
|
||||||
snakedPosition = Path.PositionInBoundingBox(Vector2.Zero);
|
|
||||||
snakedPathOffset = Path.PositionInBoundingBox(Path.Vertices[0]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateProgress(double completionProgress)
|
public void UpdateProgress(double completionProgress)
|
||||||
@ -80,6 +72,27 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
setRange(start, end);
|
setRange(start, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Refresh()
|
||||||
|
{
|
||||||
|
// Generate the entire curve
|
||||||
|
slider.Curve.GetPathToProgress(CurrentCurve, 0, 1);
|
||||||
|
SetVertices(CurrentCurve);
|
||||||
|
|
||||||
|
// The body is sized to the full path size to avoid excessive autosize computations
|
||||||
|
Size = Path.Size;
|
||||||
|
|
||||||
|
snakedPosition = Path.PositionInBoundingBox(Vector2.Zero);
|
||||||
|
snakedPathOffset = Path.PositionInBoundingBox(Path.Vertices[0]);
|
||||||
|
|
||||||
|
var lastSnakedStart = SnakedStart ?? 0;
|
||||||
|
var lastSnakedEnd = SnakedEnd ?? 0;
|
||||||
|
|
||||||
|
SnakedStart = null;
|
||||||
|
SnakedEnd = null;
|
||||||
|
|
||||||
|
setRange(lastSnakedStart, lastSnakedEnd);
|
||||||
|
}
|
||||||
|
|
||||||
private void setRange(double p0, double p1)
|
private void setRange(double p0, double p1)
|
||||||
{
|
{
|
||||||
if (p0 > p1)
|
if (p0 > p1)
|
||||||
|
@ -22,6 +22,8 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private const float base_scoring_distance = 100;
|
private const float base_scoring_distance = 100;
|
||||||
|
|
||||||
|
public event Action<Vector2[]> ControlPointsChanged;
|
||||||
|
|
||||||
public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity;
|
public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity;
|
||||||
public double Duration => EndTime - StartTime;
|
public double Duration => EndTime - StartTime;
|
||||||
|
|
||||||
@ -54,8 +56,18 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
|
|
||||||
public Vector2[] ControlPoints
|
public Vector2[] ControlPoints
|
||||||
{
|
{
|
||||||
get { return Curve.ControlPoints; }
|
get => Curve.ControlPoints;
|
||||||
set { Curve.ControlPoints = value; }
|
set
|
||||||
|
{
|
||||||
|
if (Curve.ControlPoints == value)
|
||||||
|
return;
|
||||||
|
Curve.ControlPoints = value;
|
||||||
|
|
||||||
|
ControlPointsChanged?.Invoke(value);
|
||||||
|
|
||||||
|
if (TailCircle != null)
|
||||||
|
TailCircle.Position = EndPosition;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public CurveType CurveType
|
public CurveType CurveType
|
||||||
|
@ -120,10 +120,33 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Calculate()
|
private void calculateCumulativeLength()
|
||||||
|
{
|
||||||
|
double l = 0;
|
||||||
|
|
||||||
|
cumulativeLength.Clear();
|
||||||
|
cumulativeLength.Add(l);
|
||||||
|
|
||||||
|
for (int i = 0; i < calculatedPath.Count - 1; ++i)
|
||||||
|
{
|
||||||
|
Vector2 diff = calculatedPath[i + 1] - calculatedPath[i];
|
||||||
|
double d = diff.Length;
|
||||||
|
|
||||||
|
l += d;
|
||||||
|
cumulativeLength.Add(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
Distance = l;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Calculate(bool updateDistance = false)
|
||||||
{
|
{
|
||||||
calculatePath();
|
calculatePath();
|
||||||
|
|
||||||
|
if (!updateDistance)
|
||||||
calculateCumulativeLengthAndTrimPath();
|
calculateCumulativeLengthAndTrimPath();
|
||||||
|
else
|
||||||
|
calculateCumulativeLength();
|
||||||
}
|
}
|
||||||
|
|
||||||
private int indexOfDistance(double d)
|
private int indexOfDistance(double d)
|
||||||
|
Loading…
Reference in New Issue
Block a user