mirror of
https://github.com/ppy/osu.git
synced 2025-01-06 08:22:56 +08:00
Merge pull request #13795 from ekrctb/juice-stream-selection-1
Add path visualization to juice stream selection blueprint
This commit is contained in:
commit
fd46d30f29
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
@ -18,7 +19,6 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components
|
|||||||
{
|
{
|
||||||
Anchor = Anchor.BottomLeft;
|
Anchor = Anchor.BottomLeft;
|
||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
Size = new Vector2(2 * CatchHitObject.OBJECT_RADIUS);
|
|
||||||
InternalChild = new BorderPiece();
|
InternalChild = new BorderPiece();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,10 +28,10 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components
|
|||||||
Colour = osuColour.Yellow;
|
Colour = osuColour.Yellow;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateFrom(ScrollingHitObjectContainer hitObjectContainer, CatchHitObject hitObject)
|
public void UpdateFrom(ScrollingHitObjectContainer hitObjectContainer, CatchHitObject hitObject, [CanBeNull] CatchHitObject parent = null)
|
||||||
{
|
{
|
||||||
X = hitObject.EffectiveX;
|
X = hitObject.EffectiveX - (parent?.OriginalX ?? 0);
|
||||||
Y = hitObjectContainer.PositionAtTime(hitObject.StartTime);
|
Y = hitObjectContainer.PositionAtTime(hitObject.StartTime, parent?.StartTime ?? hitObjectContainer.Time.Current);
|
||||||
Scale = new Vector2(hitObject.Scale);
|
Scale = new Vector2(hitObject.Scale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,53 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Primitives;
|
||||||
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components
|
||||||
|
{
|
||||||
|
public class NestedOutlineContainer : CompositeDrawable
|
||||||
|
{
|
||||||
|
private readonly List<CatchHitObject> nestedHitObjects = new List<CatchHitObject>();
|
||||||
|
|
||||||
|
public NestedOutlineContainer()
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdatePositionFrom(ScrollingHitObjectContainer hitObjectContainer, CatchHitObject parentHitObject)
|
||||||
|
{
|
||||||
|
X = parentHitObject.OriginalX;
|
||||||
|
Y = hitObjectContainer.PositionAtTime(parentHitObject.StartTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateNestedObjectsFrom(ScrollingHitObjectContainer hitObjectContainer, CatchHitObject parentHitObject)
|
||||||
|
{
|
||||||
|
nestedHitObjects.Clear();
|
||||||
|
nestedHitObjects.AddRange(parentHitObject.NestedHitObjects
|
||||||
|
.OfType<CatchHitObject>()
|
||||||
|
.Where(h => !(h is TinyDroplet)));
|
||||||
|
|
||||||
|
while (nestedHitObjects.Count < InternalChildren.Count)
|
||||||
|
RemoveInternal(InternalChildren[^1]);
|
||||||
|
|
||||||
|
while (InternalChildren.Count < nestedHitObjects.Count)
|
||||||
|
AddInternal(new FruitOutline());
|
||||||
|
|
||||||
|
for (int i = 0; i < nestedHitObjects.Count; i++)
|
||||||
|
{
|
||||||
|
var hitObject = nestedHitObjects[i];
|
||||||
|
var outline = (FruitOutline)InternalChildren[i];
|
||||||
|
outline.UpdateFrom(hitObjectContainer, hitObject, parentHitObject);
|
||||||
|
outline.Scale *= hitObject is Droplet ? 0.5f : 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool ComputeIsMaskedAway(RectangleF maskingBounds) => false;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,83 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Lines;
|
||||||
|
using osu.Framework.Graphics.Primitives;
|
||||||
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components
|
||||||
|
{
|
||||||
|
public class ScrollingPath : CompositeDrawable
|
||||||
|
{
|
||||||
|
private readonly Path drawablePath;
|
||||||
|
|
||||||
|
private readonly List<(double Distance, float X)> vertices = new List<(double, float)>();
|
||||||
|
|
||||||
|
public ScrollingPath()
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomLeft;
|
||||||
|
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
drawablePath = new SmoothPath
|
||||||
|
{
|
||||||
|
PathRadius = 2,
|
||||||
|
Alpha = 0.5f
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdatePositionFrom(ScrollingHitObjectContainer hitObjectContainer, CatchHitObject hitObject)
|
||||||
|
{
|
||||||
|
X = hitObject.OriginalX;
|
||||||
|
Y = hitObjectContainer.PositionAtTime(hitObject.StartTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdatePathFrom(ScrollingHitObjectContainer hitObjectContainer, JuiceStream hitObject)
|
||||||
|
{
|
||||||
|
double distanceToYFactor = -hitObjectContainer.LengthAtTime(hitObject.StartTime, hitObject.StartTime + 1 / hitObject.Velocity);
|
||||||
|
|
||||||
|
computeDistanceXs(hitObject);
|
||||||
|
drawablePath.Vertices = vertices
|
||||||
|
.Select(v => new Vector2(v.X, (float)(v.Distance * distanceToYFactor)))
|
||||||
|
.ToArray();
|
||||||
|
drawablePath.OriginPosition = drawablePath.PositionInBoundingBox(Vector2.Zero);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void computeDistanceXs(JuiceStream hitObject)
|
||||||
|
{
|
||||||
|
vertices.Clear();
|
||||||
|
|
||||||
|
var sliderVertices = new List<Vector2>();
|
||||||
|
hitObject.Path.GetPathToProgress(sliderVertices, 0, 1);
|
||||||
|
|
||||||
|
if (sliderVertices.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
double distance = 0;
|
||||||
|
Vector2 lastPosition = Vector2.Zero;
|
||||||
|
|
||||||
|
for (int repeat = 0; repeat < hitObject.RepeatCount + 1; repeat++)
|
||||||
|
{
|
||||||
|
foreach (var position in sliderVertices)
|
||||||
|
{
|
||||||
|
distance += Vector2.Distance(lastPosition, position);
|
||||||
|
lastPosition = position;
|
||||||
|
|
||||||
|
vertices.Add((distance, position.X));
|
||||||
|
}
|
||||||
|
|
||||||
|
sliderVertices.Reverse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Because this has 0x0 size, the contents are otherwise masked away if the start position is outside the screen.
|
||||||
|
protected override bool ComputeIsMaskedAway(RectangleF maskingBounds) => false;
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +3,10 @@
|
|||||||
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Caching;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Primitives;
|
using osu.Framework.Graphics.Primitives;
|
||||||
|
using osu.Game.Rulesets.Catch.Edit.Blueprints.Components;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -17,9 +20,20 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
|||||||
private float minNestedX;
|
private float minNestedX;
|
||||||
private float maxNestedX;
|
private float maxNestedX;
|
||||||
|
|
||||||
|
private readonly ScrollingPath scrollingPath;
|
||||||
|
|
||||||
|
private readonly NestedOutlineContainer nestedOutlineContainer;
|
||||||
|
|
||||||
|
private readonly Cached pathCache = new Cached();
|
||||||
|
|
||||||
public JuiceStreamSelectionBlueprint(JuiceStream hitObject)
|
public JuiceStreamSelectionBlueprint(JuiceStream hitObject)
|
||||||
: base(hitObject)
|
: base(hitObject)
|
||||||
{
|
{
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
scrollingPath = new ScrollingPath(),
|
||||||
|
nestedOutlineContainer = new NestedOutlineContainer()
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -29,7 +43,28 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
|||||||
computeObjectBounds();
|
computeObjectBounds();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onDefaultsApplied(HitObject _) => computeObjectBounds();
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
if (!IsSelected) return;
|
||||||
|
|
||||||
|
scrollingPath.UpdatePositionFrom(HitObjectContainer, HitObject);
|
||||||
|
nestedOutlineContainer.UpdatePositionFrom(HitObjectContainer, HitObject);
|
||||||
|
|
||||||
|
if (pathCache.IsValid) return;
|
||||||
|
|
||||||
|
scrollingPath.UpdatePathFrom(HitObjectContainer, HitObject);
|
||||||
|
nestedOutlineContainer.UpdateNestedObjectsFrom(HitObjectContainer, HitObject);
|
||||||
|
|
||||||
|
pathCache.Validate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onDefaultsApplied(HitObject _)
|
||||||
|
{
|
||||||
|
computeObjectBounds();
|
||||||
|
pathCache.Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
private void computeObjectBounds()
|
private void computeObjectBounds()
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user