mirror of
https://github.com/ppy/osu.git
synced 2025-02-08 07:02:54 +08:00
Merge pull request #31743 from bdach/fix-limit-distance-snap-to-current
Avoid moving already placed objects temporally when "limit distance snap to current time" is active
This commit is contained in:
commit
56000ddb37
@ -2,10 +2,13 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Screens.Edit;
|
||||
using osuTK;
|
||||
using osuTK.Input;
|
||||
|
||||
@ -20,12 +23,23 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles
|
||||
[Resolved]
|
||||
private OsuHitObjectComposer? composer { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private EditorClock? editorClock { get; set; }
|
||||
|
||||
private Bindable<bool> limitedDistanceSnap { get; set; } = null!;
|
||||
|
||||
public HitCirclePlacementBlueprint()
|
||||
: base(new HitCircle())
|
||||
{
|
||||
InternalChild = circlePiece = new HitCirclePiece();
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuConfigManager config)
|
||||
{
|
||||
limitedDistanceSnap = config.GetBindable<bool>(OsuSetting.EditorLimitedDistanceSnap);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
@ -53,7 +67,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles
|
||||
public override SnapResult UpdateTimeAndPosition(Vector2 screenSpacePosition, double fallbackTime)
|
||||
{
|
||||
var result = composer?.TrySnapToNearbyObjects(screenSpacePosition, fallbackTime);
|
||||
result ??= composer?.TrySnapToDistanceGrid(screenSpacePosition);
|
||||
result ??= composer?.TrySnapToDistanceGrid(screenSpacePosition, limitedDistanceSnap.Value && editorClock != null ? editorClock.CurrentTime : null);
|
||||
if (composer?.TrySnapToPositionGrid(result?.ScreenSpacePosition ?? screenSpacePosition, result?.Time ?? fallbackTime) is SnapResult gridSnapResult)
|
||||
result = gridSnapResult;
|
||||
result ??= new SnapResult(screenSpacePosition, fallbackTime);
|
||||
|
@ -21,6 +21,7 @@ using osu.Framework.Input;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
@ -55,6 +56,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
[Resolved(CanBeNull = true)]
|
||||
private IDistanceSnapProvider distanceSnapProvider { get; set; }
|
||||
|
||||
private Bindable<bool> limitedDistanceSnap { get; set; } = null!;
|
||||
|
||||
public PathControlPointVisualiser(T hitObject, bool allowSelection)
|
||||
{
|
||||
this.hitObject = hitObject;
|
||||
@ -69,6 +72,12 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuConfigManager config)
|
||||
{
|
||||
limitedDistanceSnap = config.GetBindable<bool>(OsuSetting.EditorLimitedDistanceSnap);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
@ -437,7 +446,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
Vector2 newHeadPosition = Parent!.ToScreenSpace(e.MousePosition + (dragStartPositions[0] - dragStartPositions[draggedControlPointIndex]));
|
||||
|
||||
var result = positionSnapProvider?.TrySnapToNearbyObjects(newHeadPosition, oldStartTime);
|
||||
result ??= positionSnapProvider?.TrySnapToDistanceGrid(newHeadPosition);
|
||||
result ??= positionSnapProvider?.TrySnapToDistanceGrid(newHeadPosition, limitedDistanceSnap.Value ? oldStartTime : null);
|
||||
if (positionSnapProvider?.TrySnapToPositionGrid(result?.ScreenSpacePosition ?? newHeadPosition, result?.Time ?? oldStartTime) is SnapResult gridSnapResult)
|
||||
result = gridSnapResult;
|
||||
result ??= new SnapResult(newHeadPosition, oldStartTime);
|
||||
|
@ -5,10 +5,12 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
@ -49,6 +51,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
[Resolved]
|
||||
private FreehandSliderToolboxGroup? freehandToolboxGroup { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private EditorClock? editorClock { get; set; }
|
||||
|
||||
private Bindable<bool> limitedDistanceSnap { get; set; } = null!;
|
||||
|
||||
private readonly IncrementalBSplineBuilder bSplineBuilder = new IncrementalBSplineBuilder { Degree = 4 };
|
||||
|
||||
protected override bool IsValidForPlacement => HitObject.Path.HasValidLength;
|
||||
@ -63,7 +70,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
private void load(OsuConfigManager config)
|
||||
{
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
@ -74,6 +81,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
};
|
||||
|
||||
state = SliderPlacementState.Initial;
|
||||
limitedDistanceSnap = config.GetBindable<bool>(OsuSetting.EditorLimitedDistanceSnap);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
@ -109,7 +117,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
public override SnapResult UpdateTimeAndPosition(Vector2 screenSpacePosition, double fallbackTime)
|
||||
{
|
||||
var result = composer?.TrySnapToNearbyObjects(screenSpacePosition, fallbackTime);
|
||||
result ??= composer?.TrySnapToDistanceGrid(screenSpacePosition);
|
||||
result ??= composer?.TrySnapToDistanceGrid(screenSpacePosition, limitedDistanceSnap.Value && editorClock != null ? editorClock.CurrentTime : null);
|
||||
if (composer?.TrySnapToPositionGrid(result?.ScreenSpacePosition ?? screenSpacePosition, result?.Time ?? fallbackTime) is SnapResult gridSnapResult)
|
||||
result = gridSnapResult;
|
||||
result ??= new SnapResult(screenSpacePosition, fallbackTime);
|
||||
|
@ -3,7 +3,10 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles;
|
||||
@ -17,6 +20,8 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
{
|
||||
public partial class OsuBlueprintContainer : ComposeBlueprintContainer
|
||||
{
|
||||
private Bindable<bool> limitedDistanceSnap { get; set; } = null!;
|
||||
|
||||
public new OsuHitObjectComposer Composer => (OsuHitObjectComposer)base.Composer;
|
||||
|
||||
public OsuBlueprintContainer(OsuHitObjectComposer composer)
|
||||
@ -24,6 +29,12 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
{
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuConfigManager config)
|
||||
{
|
||||
limitedDistanceSnap = config.GetBindable<bool>(OsuSetting.EditorLimitedDistanceSnap);
|
||||
}
|
||||
|
||||
protected override SelectionHandler<HitObject> CreateSelectionHandler() => new OsuSelectionHandler();
|
||||
|
||||
public override HitObjectSelectionBlueprint? CreateHitObjectBlueprintFor(HitObject hitObject)
|
||||
@ -58,15 +69,15 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
|
||||
// The final movement position, relative to movementBlueprintOriginalPosition.
|
||||
Vector2 movePosition = blueprints.First().originalSnapPositions.First() + distanceTravelled;
|
||||
var referenceBlueprint = blueprints.First().blueprint;
|
||||
|
||||
// Retrieve a snapped position.
|
||||
var result = Composer.TrySnapToNearbyObjects(movePosition);
|
||||
result ??= Composer.TrySnapToDistanceGrid(movePosition);
|
||||
result ??= Composer.TrySnapToDistanceGrid(movePosition, limitedDistanceSnap.Value ? referenceBlueprint.Item.StartTime : null);
|
||||
if (Composer.TrySnapToPositionGrid(result?.ScreenSpacePosition ?? movePosition, result?.Time) is SnapResult gridSnapResult)
|
||||
result = gridSnapResult;
|
||||
result ??= new SnapResult(movePosition, null);
|
||||
|
||||
var referenceBlueprint = blueprints.First().blueprint;
|
||||
bool moved = SelectionHandler.HandleMovement(new MoveSelectionEvent<HitObject>(referenceBlueprint, result.ScreenSpacePosition - referenceBlueprint.ScreenSpaceSelectionPoint));
|
||||
if (moved)
|
||||
ApplySnapResultTime(result, referenceBlueprint.Item.StartTime);
|
||||
|
@ -250,13 +250,13 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
}
|
||||
|
||||
[CanBeNull]
|
||||
public SnapResult TrySnapToDistanceGrid(Vector2 screenSpacePosition)
|
||||
public SnapResult TrySnapToDistanceGrid(Vector2 screenSpacePosition, double? fixedTime = null)
|
||||
{
|
||||
if (DistanceSnapProvider.DistanceSnapToggle.Value != TernaryState.True || distanceSnapGrid == null)
|
||||
return null;
|
||||
|
||||
var playfield = PlayfieldAtScreenSpacePosition(screenSpacePosition);
|
||||
(Vector2 pos, double time) = distanceSnapGrid.GetSnappedPosition(distanceSnapGrid.ToLocalSpace(screenSpacePosition));
|
||||
(Vector2 pos, double time) = distanceSnapGrid.GetSnappedPosition(distanceSnapGrid.ToLocalSpace(screenSpacePosition), fixedTime);
|
||||
return new SnapResult(distanceSnapGrid.ToScreenSpace(pos), time, playfield);
|
||||
}
|
||||
|
||||
|
@ -181,7 +181,7 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
}
|
||||
}
|
||||
|
||||
public override (Vector2 position, double time) GetSnappedPosition(Vector2 screenSpacePosition)
|
||||
public override (Vector2 position, double time) GetSnappedPosition(Vector2 screenSpacePosition, double? fixedTime = null)
|
||||
=> (Vector2.Zero, 0);
|
||||
}
|
||||
|
||||
|
@ -16,9 +16,6 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
{
|
||||
public abstract partial class CircularDistanceSnapGrid : DistanceSnapGrid
|
||||
{
|
||||
[Resolved]
|
||||
private EditorClock editorClock { get; set; } = null!;
|
||||
|
||||
protected CircularDistanceSnapGrid(HitObject referenceObject, Vector2 startPosition, double startTime, double? endTime = null)
|
||||
: base(referenceObject, startPosition, startTime, endTime)
|
||||
{
|
||||
@ -76,7 +73,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
}
|
||||
}
|
||||
|
||||
public override (Vector2 position, double time) GetSnappedPosition(Vector2 position)
|
||||
public override (Vector2 position, double time) GetSnappedPosition(Vector2 position, double? fixedTime = null)
|
||||
{
|
||||
if (MaxIntervals == 0)
|
||||
return (StartPosition, StartTime);
|
||||
@ -100,8 +97,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
if (travelLength < DistanceBetweenTicks)
|
||||
travelLength = DistanceBetweenTicks;
|
||||
|
||||
float snappedDistance = LimitedDistanceSnap.Value
|
||||
? SnapProvider.DurationToDistance(ReferenceObject, editorClock.CurrentTime - ReferenceObject.GetEndTime())
|
||||
float snappedDistance = fixedTime != null
|
||||
? SnapProvider.DurationToDistance(ReferenceObject, fixedTime.Value - ReferenceObject.GetEndTime())
|
||||
// When interacting with the resolved snap provider, the distance spacing multiplier should first be removed
|
||||
// to allow for snapping at a non-multiplied ratio.
|
||||
: SnapProvider.FindSnappedDistance(ReferenceObject, travelLength / distanceSpacingMultiplier, DistanceSnapTarget.End);
|
||||
|
@ -10,7 +10,6 @@ using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Layout;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
@ -61,18 +60,6 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
[Resolved]
|
||||
private BindableBeatDivisor beatDivisor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// When enabled, distance snap should only snap to the current time (as per the editor clock).
|
||||
/// This is to emulate stable behaviour.
|
||||
/// </summary>
|
||||
protected Bindable<bool> LimitedDistanceSnap { get; private set; }
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuConfigManager config)
|
||||
{
|
||||
LimitedDistanceSnap = config.GetBindable<bool>(OsuSetting.EditorLimitedDistanceSnap);
|
||||
}
|
||||
|
||||
private readonly LayoutValue gridCache = new LayoutValue(Invalidation.RequiredParentSizeToFit);
|
||||
|
||||
protected readonly HitObject ReferenceObject;
|
||||
@ -143,8 +130,12 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
/// Snaps a position to this grid.
|
||||
/// </summary>
|
||||
/// <param name="position">The original position in coordinate space local to this <see cref="DistanceSnapGrid"/>.</param>
|
||||
/// <param name="fixedTime">
|
||||
/// Whether the snap operation should be temporally constrained to a particular time instant,
|
||||
/// thus fixing the possible positions to a set distance from the <see cref="ReferenceObject"/>.
|
||||
/// </param>
|
||||
/// <returns>A tuple containing the snapped position in coordinate space local to this <see cref="DistanceSnapGrid"/> and the respective time value.</returns>
|
||||
public abstract (Vector2 position, double time) GetSnappedPosition(Vector2 position);
|
||||
public abstract (Vector2 position, double time) GetSnappedPosition(Vector2 position, double? fixedTime = null);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the applicable colour for a beat index.
|
||||
|
Loading…
Reference in New Issue
Block a user