mirror of
https://github.com/ppy/osu.git
synced 2025-01-13 03:33:22 +08:00
Merge branch 'editor-position-snap' into mania-distance-snap-grid
This commit is contained in:
commit
6de2597958
@ -7,6 +7,7 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Timing;
|
using osu.Framework.Timing;
|
||||||
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Mania.Edit;
|
using osu.Game.Rulesets.Mania.Edit;
|
||||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
@ -43,12 +44,18 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override SnapResult SnapForBlueprint(PlacementBlueprint blueprint)
|
||||||
|
{
|
||||||
|
var time = column.TimeAtScreenSpacePosition(InputManager.CurrentState.Mouse.Position);
|
||||||
|
var pos = column.ScreenSpacePositionAtTime(time);
|
||||||
|
|
||||||
|
return new ManiaSnapResult(pos, time, column);
|
||||||
|
}
|
||||||
|
|
||||||
protected override Container CreateHitObjectContainer() => new ScrollingTestContainer(ScrollingDirection.Down) { RelativeSizeAxes = Axes.Both };
|
protected override Container CreateHitObjectContainer() => new ScrollingTestContainer(ScrollingDirection.Down) { RelativeSizeAxes = Axes.Both };
|
||||||
|
|
||||||
protected override void AddHitObject(DrawableHitObject hitObject) => column.Add((DrawableManiaHitObject)hitObject);
|
protected override void AddHitObject(DrawableHitObject hitObject) => column.Add((DrawableManiaHitObject)hitObject);
|
||||||
|
|
||||||
public Column ColumnAt(Vector2 screenSpacePosition) => column;
|
|
||||||
|
|
||||||
public ManiaPlayfield Playfield => null;
|
public ManiaPlayfield Playfield => null;
|
||||||
|
|
||||||
public Vector2 ScreenSpacePositionAtTime(double time, Column column = null) => Vector2.Zero;
|
public Vector2 ScreenSpacePositionAtTime(double time, Column column = null) => Vector2.Zero;
|
||||||
|
@ -66,7 +66,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
|
|
||||||
AddStep("move mouse downwards", () =>
|
AddStep("move mouse downwards", () =>
|
||||||
{
|
{
|
||||||
InputManager.MoveMouseTo(lastObject, new Vector2(0, lastObject.ScreenSpaceDrawQuad.Height * 2));
|
InputManager.MoveMouseTo(lastObject, new Vector2(0, lastObject.ScreenSpaceDrawQuad.Height * 4));
|
||||||
InputManager.ReleaseButton(MouseButton.Left);
|
InputManager.ReleaseButton(MouseButton.Left);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
|
|
||||||
AddStep("move mouse upwards", () =>
|
AddStep("move mouse upwards", () =>
|
||||||
{
|
{
|
||||||
InputManager.MoveMouseTo(lastObject, new Vector2(0, -lastObject.ScreenSpaceDrawQuad.Height * 2));
|
InputManager.MoveMouseTo(lastObject, new Vector2(0, -lastObject.ScreenSpaceDrawQuad.Height * 4));
|
||||||
InputManager.ReleaseButton(MouseButton.Left);
|
InputManager.ReleaseButton(MouseButton.Left);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -41,8 +41,8 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
|||||||
|
|
||||||
if (Column != null)
|
if (Column != null)
|
||||||
{
|
{
|
||||||
headPiece.Y = Parent.ToLocalSpace(composer.ScreenSpacePositionAtTime(HitObject.StartTime, Column)).Y;
|
headPiece.Y = Parent.ToLocalSpace(Column.ScreenSpacePositionAtTime(HitObject.StartTime)).Y;
|
||||||
tailPiece.Y = Parent.ToLocalSpace(composer.ScreenSpacePositionAtTime(HitObject.EndTime, Column)).Y;
|
tailPiece.Y = Parent.ToLocalSpace(Column.ScreenSpacePositionAtTime(HitObject.EndTime)).Y;
|
||||||
}
|
}
|
||||||
|
|
||||||
var topPosition = new Vector2(headPiece.DrawPosition.X, Math.Min(headPiece.DrawPosition.Y, tailPiece.DrawPosition.Y));
|
var topPosition = new Vector2(headPiece.DrawPosition.X, Math.Min(headPiece.DrawPosition.Y, tailPiece.DrawPosition.Y));
|
||||||
|
@ -50,6 +50,8 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
|||||||
|
|
||||||
public override void UpdatePosition(SnapResult result)
|
public override void UpdatePosition(SnapResult result)
|
||||||
{
|
{
|
||||||
|
base.UpdatePosition(result);
|
||||||
|
|
||||||
if (!PlacementActive)
|
if (!PlacementActive)
|
||||||
Column = (result as ManiaSnapResult)?.Column;
|
Column = (result as ManiaSnapResult)?.Column;
|
||||||
}
|
}
|
||||||
|
@ -2,14 +2,11 @@
|
|||||||
// 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 osu.Game.Rulesets.Mania.UI;
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
using osuTK;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Edit
|
namespace osu.Game.Rulesets.Mania.Edit
|
||||||
{
|
{
|
||||||
public interface IManiaHitObjectComposer
|
public interface IManiaHitObjectComposer
|
||||||
{
|
{
|
||||||
ManiaPlayfield Playfield { get; }
|
ManiaPlayfield Playfield { get; }
|
||||||
|
|
||||||
Vector2 ScreenSpacePositionAtTime(double time, Column column = null);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,48 +83,17 @@ namespace osu.Game.Rulesets.Mania.Edit
|
|||||||
if (column == null)
|
if (column == null)
|
||||||
return new SnapResult(screenSpacePosition, null);
|
return new SnapResult(screenSpacePosition, null);
|
||||||
|
|
||||||
var hoc = column.HitObjectContainer;
|
double targetTime = column.TimeAtScreenSpacePosition(screenSpacePosition);
|
||||||
|
|
||||||
// convert to local space of column so we can snap and fetch correct location.
|
|
||||||
Vector2 localPosition = hoc.ToLocalSpace(screenSpacePosition);
|
|
||||||
|
|
||||||
var scrollInfo = drawableRuleset.ScrollingInfo;
|
|
||||||
|
|
||||||
if (scrollInfo.Direction.Value == ScrollingDirection.Down)
|
|
||||||
{
|
|
||||||
// We're dealing with screen coordinates in which the position decreases towards the centre of the screen resulting in an increase in start time.
|
|
||||||
// The scrolling algorithm instead assumes a top anchor meaning an increase in time corresponds to an increase in position,
|
|
||||||
// so when scrolling downwards the coordinates need to be flipped.
|
|
||||||
localPosition.Y = hoc.DrawHeight - localPosition.Y;
|
|
||||||
}
|
|
||||||
|
|
||||||
double targetTime = scrollInfo.Algorithm.TimeAt(localPosition.Y, EditorClock.CurrentTime, scrollInfo.TimeRange.Value, hoc.DrawHeight);
|
|
||||||
|
|
||||||
// apply beat snapping
|
// apply beat snapping
|
||||||
targetTime = BeatSnapProvider.SnapTime(targetTime);
|
targetTime = BeatSnapProvider.SnapTime(targetTime);
|
||||||
|
|
||||||
// convert back to screen space
|
// convert back to screen space
|
||||||
screenSpacePosition = ScreenSpacePositionAtTime(targetTime, column);
|
screenSpacePosition = column.ScreenSpacePositionAtTime(targetTime);
|
||||||
|
|
||||||
return new ManiaSnapResult(screenSpacePosition, targetTime, column);
|
return new ManiaSnapResult(screenSpacePosition, targetTime, column);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vector2 ScreenSpacePositionAtTime(double time, Column column = null)
|
|
||||||
{
|
|
||||||
var hoc = (column ?? Playfield.GetColumn(0)).HitObjectContainer;
|
|
||||||
var scrollInfo = drawableRuleset.ScrollingInfo;
|
|
||||||
|
|
||||||
var pos = scrollInfo.Algorithm.PositionAt(time, EditorClock.CurrentTime, scrollInfo.TimeRange.Value, hoc.DrawHeight);
|
|
||||||
|
|
||||||
if (scrollInfo.Direction.Value == ScrollingDirection.Down)
|
|
||||||
{
|
|
||||||
// as explained above
|
|
||||||
pos = hoc.DrawHeight - pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
return hoc.ToScreenSpace(new Vector2(hoc.DrawWidth / 2, pos));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override DrawableRuleset<ManiaHitObject> CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null)
|
protected override DrawableRuleset<ManiaHitObject> CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null)
|
||||||
{
|
{
|
||||||
drawableRuleset = new DrawableManiaEditRuleset(ruleset, beatmap, mods);
|
drawableRuleset = new DrawableManiaEditRuleset(ruleset, beatmap, mods);
|
||||||
|
@ -17,6 +17,7 @@ using osu.Game.Rulesets.UI.Scrolling;
|
|||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.UI
|
namespace osu.Game.Rulesets.Mania.UI
|
||||||
{
|
{
|
||||||
@ -142,5 +143,54 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos)
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos)
|
||||||
// This probably shouldn't exist as is, but the columns in the stage are separated by a 1px border
|
// This probably shouldn't exist as is, but the columns in the stage are separated by a 1px border
|
||||||
=> DrawRectangle.Inflate(new Vector2(Stage.COLUMN_SPACING / 2, 0)).Contains(ToLocalSpace(screenSpacePos));
|
=> DrawRectangle.Inflate(new Vector2(Stage.COLUMN_SPACING / 2, 0)).Contains(ToLocalSpace(screenSpacePos));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Given a time, return the screen space position within this column.
|
||||||
|
/// </summary>
|
||||||
|
public Vector2 ScreenSpacePositionAtTime(double time)
|
||||||
|
{
|
||||||
|
var pos = ScrollingInfo.Algorithm.PositionAt(time, Time.Current, ScrollingInfo.TimeRange.Value, HitObjectContainer.DrawHeight);
|
||||||
|
|
||||||
|
switch (ScrollingInfo.Direction.Value)
|
||||||
|
{
|
||||||
|
case ScrollingDirection.Down:
|
||||||
|
// We're dealing with screen coordinates in which the position decreases towards the centre of the screen resulting in an increase in start time.
|
||||||
|
// The scrolling algorithm instead assumes a top anchor meaning an increase in time corresponds to an increase in position,
|
||||||
|
// so when scrolling downwards the coordinates need to be flipped.
|
||||||
|
pos = HitObjectContainer.DrawHeight - pos;
|
||||||
|
|
||||||
|
// Blueprints are centred on the mouse position, such that the hitobject position is anchored at the top or bottom of the blueprint depending on the scroll direction.
|
||||||
|
pos -= DefaultNotePiece.NOTE_HEIGHT / 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ScrollingDirection.Up:
|
||||||
|
pos += DefaultNotePiece.NOTE_HEIGHT / 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return HitObjectContainer.ToScreenSpace(new Vector2(HitObjectContainer.DrawWidth / 2, pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Given a position in screen space, return the time within this column.
|
||||||
|
/// </summary>
|
||||||
|
public double TimeAtScreenSpacePosition(Vector2 screenSpacePosition)
|
||||||
|
{
|
||||||
|
// convert to local space of column so we can snap and fetch correct location.
|
||||||
|
Vector2 localPosition = HitObjectContainer.ToLocalSpace(screenSpacePosition);
|
||||||
|
|
||||||
|
switch (ScrollingInfo.Direction.Value)
|
||||||
|
{
|
||||||
|
case ScrollingDirection.Down:
|
||||||
|
// as above
|
||||||
|
localPosition.Y = HitObjectContainer.DrawHeight - localPosition.Y;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// offset for the fact that blueprints are centered, as above.
|
||||||
|
localPosition.Y -= DefaultNotePiece.NOTE_HEIGHT / 2;
|
||||||
|
|
||||||
|
return ScrollingInfo.Algorithm.TimeAt(localPosition.Y, Time.Current, ScrollingInfo.TimeRange.Value, HitObjectContainer.DrawHeight);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,10 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles
|
|||||||
return base.OnMouseDown(e);
|
return base.OnMouseDown(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void UpdatePosition(SnapResult result) => HitObject.Position = ToLocalSpace(result.ScreenSpacePosition);
|
public override void UpdatePosition(SnapResult result)
|
||||||
|
{
|
||||||
|
base.UpdatePosition(result);
|
||||||
|
HitObject.Position = ToLocalSpace(result.ScreenSpacePosition);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,6 +69,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
|
|
||||||
public override void UpdatePosition(SnapResult result)
|
public override void UpdatePosition(SnapResult result)
|
||||||
{
|
{
|
||||||
|
base.UpdatePosition(result);
|
||||||
|
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case PlacementState.Initial:
|
case PlacementState.Initial:
|
||||||
|
@ -59,9 +59,5 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void UpdatePosition(SnapResult result)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ using osu.Framework.Input.Bindings;
|
|||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Rulesets.Taiko.Audio;
|
using osu.Game.Rulesets.Taiko.Audio;
|
||||||
|
using osu.Game.Screens.Play;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.UI
|
namespace osu.Game.Rulesets.Taiko.UI
|
||||||
@ -145,6 +146,9 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
centreHit.Colour = colours.Pink;
|
centreHit.Colour = colours.Pink;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Resolved(canBeNull: true)]
|
||||||
|
private GameplayClock gameplayClock { get; set; }
|
||||||
|
|
||||||
public bool OnPressed(TaikoAction action)
|
public bool OnPressed(TaikoAction action)
|
||||||
{
|
{
|
||||||
Drawable target = null;
|
Drawable target = null;
|
||||||
@ -157,6 +161,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
target = centreHit;
|
target = centreHit;
|
||||||
back = centre;
|
back = centre;
|
||||||
|
|
||||||
|
if (gameplayClock?.IsSeeking != true)
|
||||||
drumSample.Centre?.Play();
|
drumSample.Centre?.Play();
|
||||||
}
|
}
|
||||||
else if (action == RimAction)
|
else if (action == RimAction)
|
||||||
@ -164,6 +169,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
target = rimHit;
|
target = rimHit;
|
||||||
back = rim;
|
back = rim;
|
||||||
|
|
||||||
|
if (gameplayClock?.IsSeeking != true)
|
||||||
drumSample.Rim?.Play();
|
drumSample.Rim?.Play();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,8 +244,6 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
public void BeginPlacement(HitObject hitObject)
|
public void BeginPlacement(HitObject hitObject)
|
||||||
{
|
{
|
||||||
EditorBeatmap.PlacementObject.Value = hitObject;
|
EditorBeatmap.PlacementObject.Value = hitObject;
|
||||||
|
|
||||||
hitObject.StartTime = SnapScreenSpacePositionToValidTime(inputManager.CurrentState.Mouse.Position).Time ?? EditorClock.CurrentTime;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EndPlacement(HitObject hitObject, bool commit)
|
public void EndPlacement(HitObject hitObject, bool commit)
|
||||||
@ -256,7 +254,8 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
{
|
{
|
||||||
EditorBeatmap.Add(hitObject);
|
EditorBeatmap.Add(hitObject);
|
||||||
|
|
||||||
adjustableClock.Seek(hitObject.GetEndTime());
|
if (adjustableClock.CurrentTime < hitObject.StartTime)
|
||||||
|
adjustableClock.Seek(hitObject.StartTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
showGridFor(Enumerable.Empty<HitObject>());
|
showGridFor(Enumerable.Empty<HitObject>());
|
||||||
|
@ -83,11 +83,18 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
PlacementActive = false;
|
PlacementActive = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Resolved(canBeNull: true)]
|
||||||
|
private IFrameBasedClock editorClock { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the position of this <see cref="PlacementBlueprint"/> to a new screen-space position.
|
/// Updates the position of this <see cref="PlacementBlueprint"/> to a new screen-space position.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="snapResult">The snap result information.</param>
|
/// <param name="snapResult">The snap result information.</param>
|
||||||
public abstract void UpdatePosition(SnapResult snapResult);
|
public virtual void UpdatePosition(SnapResult snapResult)
|
||||||
|
{
|
||||||
|
if (!PlacementActive)
|
||||||
|
HitObject.StartTime = snapResult.Time ?? editorClock?.CurrentTime ?? Time.Current;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invokes <see cref="Objects.HitObject.ApplyDefaults(ControlPointInfo,BeatmapDifficulty)"/>,
|
/// Invokes <see cref="Objects.HitObject.ApplyDefaults(ControlPointInfo,BeatmapDifficulty)"/>,
|
||||||
|
@ -11,13 +11,13 @@ using osu.Framework.Extensions.TypeExtensions;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Primitives;
|
using osu.Framework.Graphics.Primitives;
|
||||||
using osu.Framework.Threading;
|
using osu.Framework.Threading;
|
||||||
using osu.Framework.Audio;
|
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Screens.Play;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Objects.Drawables
|
namespace osu.Game.Rulesets.Objects.Drawables
|
||||||
@ -96,8 +96,6 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
protected virtual float SamplePlaybackPosition => 0.5f;
|
protected virtual float SamplePlaybackPosition => 0.5f;
|
||||||
|
|
||||||
private readonly BindableDouble balanceAdjust = new BindableDouble();
|
|
||||||
|
|
||||||
private BindableList<HitSampleInfo> samplesBindable;
|
private BindableList<HitSampleInfo> samplesBindable;
|
||||||
private Bindable<double> startTimeBindable;
|
private Bindable<double> startTimeBindable;
|
||||||
private Bindable<bool> userPositionalHitSounds;
|
private Bindable<bool> userPositionalHitSounds;
|
||||||
@ -173,7 +171,6 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
}
|
}
|
||||||
|
|
||||||
Samples = new SkinnableSound(samples.Select(s => HitObject.SampleControlPoint.ApplyTo(s)));
|
Samples = new SkinnableSound(samples.Select(s => HitObject.SampleControlPoint.ApplyTo(s)));
|
||||||
Samples.AddAdjustment(AdjustableProperty.Balance, balanceAdjust);
|
|
||||||
AddInternal(Samples);
|
AddInternal(Samples);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -352,6 +349,9 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Resolved(canBeNull: true)]
|
||||||
|
private GameplayClock gameplayClock { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Plays all the hit sounds for this <see cref="DrawableHitObject"/>.
|
/// Plays all the hit sounds for this <see cref="DrawableHitObject"/>.
|
||||||
/// This is invoked automatically when this <see cref="DrawableHitObject"/> is hit.
|
/// This is invoked automatically when this <see cref="DrawableHitObject"/> is hit.
|
||||||
@ -360,8 +360,11 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
{
|
{
|
||||||
const float balance_adjust_amount = 0.4f;
|
const float balance_adjust_amount = 0.4f;
|
||||||
|
|
||||||
balanceAdjust.Value = balance_adjust_amount * (userPositionalHitSounds.Value ? SamplePlaybackPosition - 0.5f : 0);
|
if (Samples != null && gameplayClock?.IsSeeking != true)
|
||||||
Samples?.Play();
|
{
|
||||||
|
Samples.Balance.Value = balance_adjust_amount * (userPositionalHitSounds.Value ? SamplePlaybackPosition - 0.5f : 0);
|
||||||
|
Samples.Play();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
|
@ -29,14 +29,16 @@ namespace osu.Game.Rulesets.UI
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal bool FrameStablePlayback = true;
|
internal bool FrameStablePlayback = true;
|
||||||
|
|
||||||
[Cached]
|
public GameplayClock GameplayClock => stabilityGameplayClock;
|
||||||
public GameplayClock GameplayClock { get; }
|
|
||||||
|
[Cached(typeof(GameplayClock))]
|
||||||
|
private readonly StabilityGameplayClock stabilityGameplayClock;
|
||||||
|
|
||||||
public FrameStabilityContainer(double gameplayStartTime = double.MinValue)
|
public FrameStabilityContainer(double gameplayStartTime = double.MinValue)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
GameplayClock = new GameplayClock(framedClock = new FramedClock(manualClock = new ManualClock()));
|
stabilityGameplayClock = new StabilityGameplayClock(framedClock = new FramedClock(manualClock = new ManualClock()));
|
||||||
|
|
||||||
this.gameplayStartTime = gameplayStartTime;
|
this.gameplayStartTime = gameplayStartTime;
|
||||||
}
|
}
|
||||||
@ -57,7 +59,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
{
|
{
|
||||||
if (clock != null)
|
if (clock != null)
|
||||||
{
|
{
|
||||||
parentGameplayClock = clock;
|
stabilityGameplayClock.ParentGameplayClock = parentGameplayClock = clock;
|
||||||
GameplayClock.IsPaused.BindTo(clock.IsPaused);
|
GameplayClock.IsPaused.BindTo(clock.IsPaused);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -187,5 +189,17 @@ namespace osu.Game.Rulesets.UI
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ReplayInputHandler ReplayInputHandler { get; set; }
|
public ReplayInputHandler ReplayInputHandler { get; set; }
|
||||||
|
|
||||||
|
private class StabilityGameplayClock : GameplayClock
|
||||||
|
{
|
||||||
|
public IFrameBasedClock ParentGameplayClock;
|
||||||
|
|
||||||
|
public StabilityGameplayClock(FramedClock underlyingClock)
|
||||||
|
: base(underlyingClock)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool IsSeeking => ParentGameplayClock != null && Math.Abs(CurrentTime - ParentGameplayClock.CurrentTime) > 200;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,12 +15,12 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
|||||||
protected readonly IBindable<ScrollingDirection> Direction = new Bindable<ScrollingDirection>();
|
protected readonly IBindable<ScrollingDirection> Direction = new Bindable<ScrollingDirection>();
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private IScrollingInfo scrollingInfo { get; set; }
|
protected IScrollingInfo ScrollingInfo { get; private set; }
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
Direction.BindTo(scrollingInfo.Direction);
|
Direction.BindTo(ScrollingInfo.Direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected sealed override HitObjectContainer CreateHitObjectContainer() => new ScrollingHitObjectContainer();
|
protected sealed override HitObjectContainer CreateHitObjectContainer() => new ScrollingHitObjectContainer();
|
||||||
|
@ -31,6 +31,11 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
public bool IsRunning => underlyingClock.IsRunning;
|
public bool IsRunning => underlyingClock.IsRunning;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether an ongoing seek operation is active.
|
||||||
|
/// </summary>
|
||||||
|
public virtual bool IsSeeking => false;
|
||||||
|
|
||||||
public void ProcessFrame()
|
public void ProcessFrame()
|
||||||
{
|
{
|
||||||
// we do not want to process the underlying clock.
|
// we do not want to process the underlying clock.
|
||||||
|
@ -4,11 +4,11 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio;
|
|
||||||
using osu.Framework.Audio.Sample;
|
|
||||||
using osu.Framework.Audio.Track;
|
using osu.Framework.Audio.Track;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
|
using osu.Framework.Graphics.Audio;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
|
|
||||||
namespace osu.Game.Skinning
|
namespace osu.Game.Skinning
|
||||||
@ -17,25 +17,32 @@ namespace osu.Game.Skinning
|
|||||||
{
|
{
|
||||||
private readonly ISampleInfo[] hitSamples;
|
private readonly ISampleInfo[] hitSamples;
|
||||||
|
|
||||||
private List<(AdjustableProperty property, BindableDouble bindable)> adjustments;
|
|
||||||
|
|
||||||
private SampleChannel[] channels;
|
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private ISampleStore samples { get; set; }
|
private ISampleStore samples { get; set; }
|
||||||
|
|
||||||
|
public SkinnableSound(ISampleInfo hitSamples)
|
||||||
|
: this(new[] { hitSamples })
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public SkinnableSound(IEnumerable<ISampleInfo> hitSamples)
|
public SkinnableSound(IEnumerable<ISampleInfo> hitSamples)
|
||||||
{
|
{
|
||||||
this.hitSamples = hitSamples.ToArray();
|
this.hitSamples = hitSamples.ToArray();
|
||||||
}
|
InternalChild = samplesContainer = new AudioContainer<DrawableSample>();
|
||||||
|
|
||||||
public SkinnableSound(ISampleInfo hitSamples)
|
|
||||||
{
|
|
||||||
this.hitSamples = new[] { hitSamples };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool looping;
|
private bool looping;
|
||||||
|
|
||||||
|
private readonly AudioContainer<DrawableSample> samplesContainer;
|
||||||
|
|
||||||
|
public BindableNumber<double> Volume => samplesContainer.Volume;
|
||||||
|
|
||||||
|
public BindableNumber<double> Balance => samplesContainer.Balance;
|
||||||
|
|
||||||
|
public BindableNumber<double> Frequency => samplesContainer.Frequency;
|
||||||
|
|
||||||
|
public BindableNumber<double> Tempo => samplesContainer.Tempo;
|
||||||
|
|
||||||
public bool Looping
|
public bool Looping
|
||||||
{
|
{
|
||||||
get => looping;
|
get => looping;
|
||||||
@ -45,33 +52,23 @@ namespace osu.Game.Skinning
|
|||||||
|
|
||||||
looping = value;
|
looping = value;
|
||||||
|
|
||||||
channels?.ForEach(c => c.Looping = looping);
|
samplesContainer.ForEach(c => c.Looping = looping);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Play() => channels?.ForEach(c => c.Play());
|
public void Play() => samplesContainer.ForEach(c =>
|
||||||
|
|
||||||
public void Stop() => channels?.ForEach(c => c.Stop());
|
|
||||||
|
|
||||||
public void AddAdjustment(AdjustableProperty type, BindableDouble adjustBindable)
|
|
||||||
{
|
{
|
||||||
if (adjustments == null) adjustments = new List<(AdjustableProperty, BindableDouble)>();
|
if (c.AggregateVolume.Value > 0)
|
||||||
|
c.Play();
|
||||||
|
});
|
||||||
|
|
||||||
adjustments.Add((type, adjustBindable));
|
public void Stop() => samplesContainer.ForEach(c => c.Stop());
|
||||||
channels?.ForEach(c => c.AddAdjustment(type, adjustBindable));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RemoveAdjustment(AdjustableProperty type, BindableDouble adjustBindable)
|
|
||||||
{
|
|
||||||
adjustments?.Remove((type, adjustBindable));
|
|
||||||
channels?.ForEach(c => c.RemoveAdjustment(type, adjustBindable));
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool IsPresent => Scheduler.HasPendingTasks;
|
public override bool IsPresent => Scheduler.HasPendingTasks;
|
||||||
|
|
||||||
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
||||||
{
|
{
|
||||||
channels = hitSamples.Select(s =>
|
var channels = hitSamples.Select(s =>
|
||||||
{
|
{
|
||||||
var ch = skin.GetSample(s);
|
var ch = skin.GetSample(s);
|
||||||
|
|
||||||
@ -88,27 +85,12 @@ namespace osu.Game.Skinning
|
|||||||
{
|
{
|
||||||
ch.Looping = looping;
|
ch.Looping = looping;
|
||||||
ch.Volume.Value = s.Volume / 100.0;
|
ch.Volume.Value = s.Volume / 100.0;
|
||||||
|
|
||||||
if (adjustments != null)
|
|
||||||
{
|
|
||||||
foreach (var (property, bindable) in adjustments)
|
|
||||||
ch.AddAdjustment(property, bindable);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ch;
|
return ch;
|
||||||
}).Where(c => c != null).ToArray();
|
}).Where(c => c != null);
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
samplesContainer.ChildrenEnumerable = channels.Select(c => new DrawableSample(c));
|
||||||
{
|
|
||||||
base.Dispose(isDisposing);
|
|
||||||
|
|
||||||
if (channels != null)
|
|
||||||
{
|
|
||||||
foreach (var c in channels)
|
|
||||||
c.Dispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,9 +71,12 @@ namespace osu.Game.Tests.Visual
|
|||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
currentBlueprint.UpdatePosition(new SnapResult(InputManager.CurrentState.Mouse.Position, null));
|
currentBlueprint.UpdatePosition(SnapForBlueprint(currentBlueprint));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected virtual SnapResult SnapForBlueprint(PlacementBlueprint blueprint) =>
|
||||||
|
new SnapResult(InputManager.CurrentState.Mouse.Position, null);
|
||||||
|
|
||||||
public override void Add(Drawable drawable)
|
public override void Add(Drawable drawable)
|
||||||
{
|
{
|
||||||
base.Add(drawable);
|
base.Add(drawable);
|
||||||
@ -81,7 +84,7 @@ namespace osu.Game.Tests.Visual
|
|||||||
if (drawable is PlacementBlueprint blueprint)
|
if (drawable is PlacementBlueprint blueprint)
|
||||||
{
|
{
|
||||||
blueprint.Show();
|
blueprint.Show();
|
||||||
blueprint.UpdatePosition(new SnapResult(InputManager.CurrentState.Mouse.Position, null));
|
blueprint.UpdatePosition(SnapForBlueprint(blueprint));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user