1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 07:42:57 +08:00

Add hitobject overlays to selected hitobjects

This commit is contained in:
smoogipoo 2018-02-20 18:01:45 +09:00
parent 2a5bfdb4b8
commit ad2f556133
12 changed files with 315 additions and 26 deletions

View File

@ -0,0 +1,26 @@
// 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.Game.Rulesets.Edit.Layers.Selection;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays;
using osu.Game.Rulesets.Osu.Objects.Drawables;
namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection
{
public class OsuHitObjectOverlayLayer : HitObjectOverlayLayer
{
protected override HitObjectOverlay CreateOverlayFor(DrawableHitObject hitObject)
{
switch (hitObject)
{
case DrawableHitCircle circle:
return new HitCircleOverlay(circle);
case DrawableSlider slider:
return new SliderOverlay(slider);
}
return base.CreateOverlayFor(hitObject);
}
}
}

View File

@ -0,0 +1,33 @@
// 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.Allocation;
using osu.Game.Graphics;
using osu.Game.Rulesets.Edit.Layers.Selection;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
{
public class HitCircleOverlay : HitObjectOverlay
{
public HitCircleOverlay(DrawableHitCircle hitCircle)
: base(hitCircle)
{
Origin = Anchor.Centre;
Position = hitCircle.Position;
Size = hitCircle.Size;
Scale = hitCircle.Scale;
AddInternal(new RingPiece());
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Colour = colours.Yellow;
}
}
}

View File

@ -0,0 +1,45 @@
// 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.Allocation;
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Rulesets.Edit.Layers.Selection;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
using OpenTK;
namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
{
public class SliderCircleOverlay : HitObjectOverlay
{
public SliderCircleOverlay(DrawableHitCircle sliderHead, DrawableSlider slider)
: this(sliderHead, sliderHead.Position, slider)
{
}
public SliderCircleOverlay(DrawableSliderTail sliderTail, DrawableSlider slider)
: this(sliderTail, ((Slider)slider.HitObject).Curve.PositionAt(1) + slider.HitObject.StackOffset, slider)
{
}
private SliderCircleOverlay(DrawableOsuHitObject hitObject, Vector2 position, DrawableSlider slider)
: base(hitObject)
{
Origin = Anchor.Centre;
Position = position;
Size = slider.HeadCircle.Size;
Scale = slider.HeadCircle.Scale;
AddInternal(new RingPiece());
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Colour = colours.Yellow;
}
}
}

View File

@ -0,0 +1,55 @@
// 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.Allocation;
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Rulesets.Edit.Layers.Selection;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
{
public class SliderOverlay : HitObjectOverlay
{
private readonly SliderBody body;
private readonly DrawableSlider hitObject;
public SliderOverlay(DrawableSlider slider)
: base(slider)
{
hitObject = slider;
var obj = (Slider)slider.HitObject;
InternalChildren = new Drawable[]
{
body = new SliderBody(obj)
{
AccentColour = Color4.Transparent,
Position = obj.StackedPosition,
PathWidth = obj.Scale * 64
},
new SliderCircleOverlay(slider.HeadCircle, slider),
new SliderCircleOverlay(slider.TailCircle, slider),
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
body.BorderColour = colours.Yellow;
}
protected override void Update()
{
base.Update();
hitObject.GetCurrentProgress(out int span, out double progress);
body.UpdateProgress(progress, span);
}
}
}

View File

@ -5,7 +5,9 @@ using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Layers.Selection;
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Osu.Edit.Layers.Selection;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.UI;
using osu.Game.Rulesets.UI;
@ -29,5 +31,7 @@ namespace osu.Game.Rulesets.Osu.Edit
};
protected override ScalableContainer CreateLayerContainer() => new ScalableContainer(OsuPlayfield.BASE_SIZE.X) { RelativeSizeAxes = Axes.Both };
protected override HitObjectOverlayLayer CreateHitObjectOverlayLayer() => new OsuHitObjectOverlayLayer();
}
}

View File

@ -21,6 +21,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private readonly List<Drawable> components = new List<Drawable>();
public readonly DrawableHitCircle HeadCircle;
public readonly DrawableSliderTail TailCircle;
public readonly SliderBody Body;
public readonly SliderBall Ball;
@ -29,7 +31,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
slider = s;
DrawableSliderTail tail;
Container<DrawableSliderTick> ticks;
Container<DrawableRepeatPoint> repeatPoints;
@ -51,7 +52,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Alpha = 0
},
HeadCircle = new DrawableHitCircle(s.HeadCircle),
tail = new DrawableSliderTail(s.TailCircle)
TailCircle = new DrawableSliderTail(s.TailCircle)
};
components.Add(Body);
@ -59,8 +60,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
AddNested(HeadCircle);
AddNested(tail);
components.Add(tail);
AddNested(TailCircle);
components.Add(TailCircle);
foreach (var tick in s.NestedHitObjects.OfType<SliderTick>())
{
@ -96,10 +97,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Tracking = Ball.Tracking;
double progress = MathHelper.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1);
int span = slider.SpanAt(progress);
progress = slider.ProgressAt(progress);
GetCurrentProgress(out int span, out double progress);
if (span > currentSpan)
currentSpan = span;
@ -155,6 +153,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
}
}
/// <summary>
/// Finds the progress along the slider at the current time.
/// </summary>
/// <param name="span">The current span.</param>
/// <param name="progress">The current progress in the current span.</param>
public void GetCurrentProgress(out int span, out double progress)
{
double offset = MathHelper.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1);
span = slider.SpanAt(offset);
progress = slider.ProgressAt(offset);
}
public Drawable ProxiedLayer => HeadCircle.ApproachCircle;
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => Body.ReceiveMouseInputAt(screenSpacePos);

View File

@ -64,6 +64,10 @@
<ItemGroup>
<Compile Include="Beatmaps\OsuBeatmapConverter.cs" />
<Compile Include="Beatmaps\OsuBeatmapProcessor.cs" />
<Compile Include="Edit\Layers\Selection\OsuHitObjectOverlayLayer.cs" />
<Compile Include="Edit\Layers\Selection\Overlays\HitCircleOverlay.cs" />
<Compile Include="Edit\Layers\Selection\Overlays\SliderCircleOverlay.cs" />
<Compile Include="Edit\Layers\Selection\Overlays\SliderOverlay.cs" />
<Compile Include="Edit\OsuEditPlayfield.cs" />
<Compile Include="Edit\OsuEditRulesetContainer.cs" />
<Compile Include="Edit\OsuHitObjectComposer.cs" />

View File

@ -6,10 +6,13 @@ using System.Collections.Generic;
using osu.Framework.Allocation;
using OpenTK;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Layers.Selection;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Edit;
using osu.Game.Rulesets.Osu.Edit.Layers.Selection;
using osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Tests.Beatmaps;
@ -21,7 +24,15 @@ namespace osu.Game.Tests.Visual
{
typeof(SelectionBox),
typeof(SelectionLayer),
typeof(CaptureBox)
typeof(CaptureBox),
typeof(HitObjectComposer),
typeof(OsuHitObjectComposer),
typeof(HitObjectOverlayLayer),
typeof(OsuHitObjectOverlayLayer),
typeof(HitObjectOverlay),
typeof(HitCircleOverlay),
typeof(SliderOverlay),
typeof(SliderCircleOverlay)
};
[BackgroundDependencyLoader]

View File

@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Edit
protected ICompositionTool CurrentTool { get; private set; }
private RulesetContainer rulesetContainer;
private readonly Container[] layerContainers = new Container[2];
private readonly ScalableContainer[] layerContainers = new ScalableContainer[2];
protected HitObjectComposer(Ruleset ruleset)
{
@ -49,20 +49,6 @@ namespace osu.Game.Rulesets.Edit
return;
}
layerContainers[0] = CreateLayerContainer();
layerContainers[0].Child = new Container
{
Name = "Border",
RelativeSizeAxes = Axes.Both,
Masking = true,
BorderColour = Color4.White,
BorderThickness = 2,
Child = new Box { RelativeSizeAxes = Axes.Both, Alpha = 0, AlwaysPresent = true }
};
layerContainers[1] = CreateLayerContainer();
layerContainers[1].Child = new SelectionLayer(rulesetContainer.Playfield);
RadioButtonCollection toolboxCollection;
InternalChild = new GridContainer
{
@ -87,9 +73,9 @@ namespace osu.Game.Rulesets.Edit
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
layerContainers[0],
createBottomLayer(),
rulesetContainer,
layerContainers[1]
createTopLayer()
}
}
},
@ -112,6 +98,40 @@ namespace osu.Game.Rulesets.Edit
toolboxCollection.Items[0].Select();
}
private ScalableContainer createBottomLayer()
{
layerContainers[0] = CreateLayerContainer();
layerContainers[0].Child = new Container
{
Name = "Border",
RelativeSizeAxes = Axes.Both,
Masking = true,
BorderColour = Color4.White,
BorderThickness = 2,
Child = new Box { RelativeSizeAxes = Axes.Both, Alpha = 0, AlwaysPresent = true }
};
return layerContainers[0];
}
private ScalableContainer createTopLayer()
{
var overlayLayer = CreateHitObjectOverlayLayer();
var selectionLayer = new SelectionLayer(rulesetContainer.Playfield);
selectionLayer.ObjectSelected += overlayLayer.AddOverlay;
selectionLayer.ObjectDeselected += overlayLayer.RemoveOverlay;
layerContainers[1] = CreateLayerContainer();
layerContainers[1].Children = new Drawable[]
{
overlayLayer,
selectionLayer,
};
return layerContainers[1];
}
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
@ -135,5 +155,10 @@ namespace osu.Game.Rulesets.Edit
/// Creates a <see cref="ScalableContainer"/> which provides a layer above or below the <see cref="Playfield"/>.
/// </summary>
protected virtual ScalableContainer CreateLayerContainer() => new ScalableContainer();
/// <summary>
/// Creates the <see cref="HitObjectOverlayLayer"/> which overlays selected <see cref="DrawableHitObject"/>s.
/// </summary>
protected virtual HitObjectOverlayLayer CreateHitObjectOverlayLayer() => new HitObjectOverlayLayer();
}
}

View File

@ -0,0 +1,20 @@
// 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.Containers;
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Edit.Layers.Selection
{
public class HitObjectOverlay : CompositeDrawable
{
// ReSharper disable once NotAccessedField.Local
// This will be used later to handle drag movement, etc
private readonly DrawableHitObject hitObject;
public HitObjectOverlay(DrawableHitObject hitObject)
{
this.hitObject = hitObject;
}
}
}

View File

@ -0,0 +1,53 @@
// 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.Collections.Generic;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Edit.Layers.Selection
{
public class HitObjectOverlayLayer : CompositeDrawable
{
private readonly Dictionary<DrawableHitObject, HitObjectOverlay> existingOverlays = new Dictionary<DrawableHitObject, HitObjectOverlay>();
public HitObjectOverlayLayer()
{
RelativeSizeAxes = Axes.Both;
}
/// <summary>
/// Adds an overlay for a <see cref="DrawableHitObject"/> which adds movement support.
/// </summary>
/// <param name="hitObject">The <see cref="DrawableHitObject"/> to create an overlay for.</param>
public void AddOverlay(DrawableHitObject hitObject)
{
var overlay = CreateOverlayFor(hitObject);
if (overlay == null)
return;
existingOverlays[hitObject] = overlay;
AddInternal(overlay);
}
/// <summary>
/// Removes the overlay for a <see cref="DrawableHitObject"/>.
/// </summary>
/// <param name="hitObject">The <see cref="DrawableHitObject"/> to remove the overlay for.</param>
public void RemoveOverlay(DrawableHitObject hitObject)
{
if (!existingOverlays.TryGetValue(hitObject, out var existing))
return;
existing.Hide();
existing.Expire();
}
/// <summary>
/// Creates a <see cref="HitObjectOverlay"/> for a specific <see cref="DrawableHitObject"/>.
/// </summary>
/// <param name="hitObject">The <see cref="DrawableHitObject"/> to create the overlay for.</param>
protected virtual HitObjectOverlay CreateOverlayFor(DrawableHitObject hitObject) => null;
}
}

View File

@ -347,6 +347,8 @@
<Compile Include="Rulesets\Configuration\IRulesetConfigManager.cs" />
<Compile Include="Rulesets\Configuration\RulesetConfigManager.cs" />
<Compile Include="Rulesets\Edit\Layers\Selection\CaptureBox.cs" />
<Compile Include="Rulesets\Edit\Layers\Selection\HitObjectOverlay.cs" />
<Compile Include="Rulesets\Edit\Layers\Selection\HitObjectOverlayLayer.cs" />
<Compile Include="Rulesets\Mods\IApplicableFailOverride.cs" />
<Compile Include="Rulesets\Mods\IApplicableMod.cs" />
<Compile Include="Rulesets\Mods\IApplicableToBeatmapConverter.cs" />