mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 04:52:57 +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>.
|
||||
// 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.ControlPoints;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
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.Drawables;
|
||||
using osu.Game.Tests.Visual;
|
||||
@ -15,6 +18,16 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
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;
|
||||
|
||||
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.position = position;
|
||||
|
||||
slider.ControlPointsChanged += _ => UpdatePosition();
|
||||
}
|
||||
|
||||
protected override void UpdatePosition()
|
||||
|
@ -24,6 +24,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Masks.SliderMasks
|
||||
new SliderBodyPiece(sliderObject),
|
||||
headMask = new SliderCircleSelectionMask(slider.HeadCircle, sliderObject, SliderPosition.Start),
|
||||
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;
|
||||
Ball.Scale = new Vector2(HitObject.Scale);
|
||||
};
|
||||
|
||||
slider.ControlPointsChanged += _ => Body.Refresh();
|
||||
}
|
||||
|
||||
public override Color4 AccentColour
|
||||
|
@ -16,7 +16,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
this.slider = slider;
|
||||
|
||||
Position = HitObject.Position - slider.Position;
|
||||
h.PositionChanged += _ => updatePosition();
|
||||
slider.ControlPointsChanged += _ => updatePosition();
|
||||
|
||||
updatePosition();
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
@ -33,5 +36,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
public Action<double> OnShake;
|
||||
|
||||
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
|
||||
{
|
||||
private readonly Slider slider;
|
||||
|
||||
/// <summary>
|
||||
/// The judgement text is provided by the <see cref="DrawableSlider"/>.
|
||||
/// </summary>
|
||||
@ -18,6 +20,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
public DrawableSliderTail(Slider slider, SliderTailCircle hitCircle)
|
||||
: base(hitCircle)
|
||||
{
|
||||
this.slider = slider;
|
||||
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
@ -25,7 +29,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
AlwaysPresent = true;
|
||||
|
||||
Position = HitObject.Position - slider.Position;
|
||||
hitCircle.PositionChanged += _ => updatePosition();
|
||||
slider.ControlPointsChanged += _ => updatePosition();
|
||||
|
||||
updatePosition();
|
||||
}
|
||||
|
||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||
@ -33,5 +40,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
if (!userTriggered && timeOffset >= 0)
|
||||
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]
|
||||
private void load()
|
||||
{
|
||||
// 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]);
|
||||
Refresh();
|
||||
}
|
||||
|
||||
public void UpdateProgress(double completionProgress)
|
||||
@ -80,6 +72,27 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
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)
|
||||
{
|
||||
if (p0 > p1)
|
||||
|
@ -22,6 +22,8 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
/// </summary>
|
||||
private const float base_scoring_distance = 100;
|
||||
|
||||
public event Action<Vector2[]> ControlPointsChanged;
|
||||
|
||||
public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity;
|
||||
public double Duration => EndTime - StartTime;
|
||||
|
||||
@ -54,8 +56,18 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
|
||||
public Vector2[] ControlPoints
|
||||
{
|
||||
get { return Curve.ControlPoints; }
|
||||
set { Curve.ControlPoints = value; }
|
||||
get => Curve.ControlPoints;
|
||||
set
|
||||
{
|
||||
if (Curve.ControlPoints == value)
|
||||
return;
|
||||
Curve.ControlPoints = value;
|
||||
|
||||
ControlPointsChanged?.Invoke(value);
|
||||
|
||||
if (TailCircle != null)
|
||||
TailCircle.Position = EndPosition;
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
calculateCumulativeLengthAndTrimPath();
|
||||
|
||||
if (!updateDistance)
|
||||
calculateCumulativeLengthAndTrimPath();
|
||||
else
|
||||
calculateCumulativeLength();
|
||||
}
|
||||
|
||||
private int indexOfDistance(double d)
|
||||
|
Loading…
Reference in New Issue
Block a user