mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 10:33:30 +08:00
Merge pull request #10826 from smoogipoo/osu-hitobject-pooling-playfield
Change osu! ruleset to use pooled hitobjects
This commit is contained in:
commit
aae59dc3cf
@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
|
|||||||
Mod = new OsuModSpunOut(),
|
Mod = new OsuModSpunOut(),
|
||||||
Autoplay = false,
|
Autoplay = false,
|
||||||
Beatmap = singleSpinnerBeatmap,
|
Beatmap = singleSpinnerBeatmap,
|
||||||
PassCondition = () => Player.ChildrenOfType<DrawableSpinner>().Single().Progress >= 1
|
PassCondition = () => Player.ChildrenOfType<DrawableSpinner>().SingleOrDefault()?.Progress >= 1
|
||||||
});
|
});
|
||||||
|
|
||||||
[TestCase(null)]
|
[TestCase(null)]
|
||||||
@ -45,7 +45,11 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
|
|||||||
Mods = mods,
|
Mods = mods,
|
||||||
Autoplay = false,
|
Autoplay = false,
|
||||||
Beatmap = singleSpinnerBeatmap,
|
Beatmap = singleSpinnerBeatmap,
|
||||||
PassCondition = () => Precision.AlmostEquals(Player.ChildrenOfType<SpinnerSpmCounter>().Single().SpinsPerMinute, 286, 1)
|
PassCondition = () =>
|
||||||
|
{
|
||||||
|
var counter = Player.ChildrenOfType<SpinnerSpmCounter>().SingleOrDefault();
|
||||||
|
return counter != null && Precision.AlmostEquals(counter.SpinsPerMinute, 286, 1);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
config.BindWith(OsuRulesetSetting.SnakingOutSliders, snakingOut);
|
config.BindWith(OsuRulesetSetting.SnakingOutSliders, snakingOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
private DrawableSlider slider;
|
private DrawableSlider drawableSlider;
|
||||||
|
|
||||||
[SetUpSteps]
|
[SetUpSteps]
|
||||||
public override void SetUpSteps()
|
public override void SetUpSteps()
|
||||||
@ -68,7 +68,8 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
AddUntilStep("wait for track to start running", () => Beatmap.Value.Track.IsRunning);
|
AddUntilStep("wait for track to start running", () => Beatmap.Value.Track.IsRunning);
|
||||||
|
|
||||||
double startTime = hitObjects[sliderIndex].StartTime;
|
double startTime = hitObjects[sliderIndex].StartTime;
|
||||||
retrieveDrawableSlider(sliderIndex);
|
addSeekStep(startTime);
|
||||||
|
retrieveDrawableSlider((Slider)hitObjects[sliderIndex]);
|
||||||
setSnaking(true);
|
setSnaking(true);
|
||||||
|
|
||||||
ensureSnakingIn(startTime + fade_in_modifier);
|
ensureSnakingIn(startTime + fade_in_modifier);
|
||||||
@ -93,7 +94,8 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
AddUntilStep("wait for track to start running", () => Beatmap.Value.Track.IsRunning);
|
AddUntilStep("wait for track to start running", () => Beatmap.Value.Track.IsRunning);
|
||||||
|
|
||||||
double startTime = hitObjects[sliderIndex].StartTime;
|
double startTime = hitObjects[sliderIndex].StartTime;
|
||||||
retrieveDrawableSlider(sliderIndex);
|
addSeekStep(startTime);
|
||||||
|
retrieveDrawableSlider((Slider)hitObjects[sliderIndex]);
|
||||||
setSnaking(false);
|
setSnaking(false);
|
||||||
|
|
||||||
ensureNoSnakingIn(startTime + fade_in_modifier);
|
ensureNoSnakingIn(startTime + fade_in_modifier);
|
||||||
@ -127,9 +129,8 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
checkPositionChange(16600, sliderRepeat, positionDecreased);
|
checkPositionChange(16600, sliderRepeat, positionDecreased);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void retrieveDrawableSlider(int index) =>
|
private void retrieveDrawableSlider(Slider slider) => AddUntilStep($"retrieve slider @ {slider.StartTime}", () =>
|
||||||
AddStep($"retrieve {(index + 1).ToOrdinalWords()} slider", () =>
|
(drawableSlider = (DrawableSlider)Player.DrawableRuleset.Playfield.AllHitObjects.SingleOrDefault(d => d.HitObject == slider)) != null);
|
||||||
slider = (DrawableSlider)Player.DrawableRuleset.Playfield.AllHitObjects.ElementAt(index));
|
|
||||||
|
|
||||||
private void ensureSnakingIn(double startTime) => checkPositionChange(startTime, sliderEnd, positionIncreased);
|
private void ensureSnakingIn(double startTime) => checkPositionChange(startTime, sliderEnd, positionIncreased);
|
||||||
private void ensureNoSnakingIn(double startTime) => checkPositionChange(startTime, sliderEnd, positionRemainsSame);
|
private void ensureNoSnakingIn(double startTime) => checkPositionChange(startTime, sliderEnd, positionRemainsSame);
|
||||||
@ -150,13 +151,13 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
private double timeAtRepeat(double startTime, int repeatIndex) => startTime + 100 + duration_of_span * repeatIndex;
|
private double timeAtRepeat(double startTime, int repeatIndex) => startTime + 100 + duration_of_span * repeatIndex;
|
||||||
private Func<Vector2> positionAtRepeat(int repeatIndex) => repeatIndex % 2 == 0 ? (Func<Vector2>)sliderStart : sliderEnd;
|
private Func<Vector2> positionAtRepeat(int repeatIndex) => repeatIndex % 2 == 0 ? (Func<Vector2>)sliderStart : sliderEnd;
|
||||||
|
|
||||||
private List<Vector2> sliderCurve => ((PlaySliderBody)slider.Body.Drawable).CurrentCurve;
|
private List<Vector2> sliderCurve => ((PlaySliderBody)drawableSlider.Body.Drawable).CurrentCurve;
|
||||||
private Vector2 sliderStart() => sliderCurve.First();
|
private Vector2 sliderStart() => sliderCurve.First();
|
||||||
private Vector2 sliderEnd() => sliderCurve.Last();
|
private Vector2 sliderEnd() => sliderCurve.Last();
|
||||||
|
|
||||||
private Vector2 sliderRepeat()
|
private Vector2 sliderRepeat()
|
||||||
{
|
{
|
||||||
var drawable = Player.DrawableRuleset.Playfield.AllHitObjects.ElementAt(1);
|
var drawable = Player.DrawableRuleset.Playfield.AllHitObjects.SingleOrDefault(d => d.HitObject == hitObjects[1]);
|
||||||
var repeat = drawable.ChildrenOfType<Container<DrawableSliderRepeat>>().First().Children.First();
|
var repeat = drawable.ChildrenOfType<Container<DrawableSliderRepeat>>().First().Children.First();
|
||||||
return repeat.Position;
|
return repeat.Position;
|
||||||
}
|
}
|
||||||
|
63
osu.Game.Rulesets.Osu/Edit/DrawableOsuEditPool.cs
Normal file
63
osu.Game.Rulesets.Osu/Edit/DrawableOsuEditPool.cs
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
// 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;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Edit
|
||||||
|
{
|
||||||
|
public class DrawableOsuEditPool<T> : DrawableOsuPool<T>
|
||||||
|
where T : DrawableHitObject, new()
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Hit objects are intentionally made to fade out at a constant slower rate than in gameplay.
|
||||||
|
/// This allows a mapper to gain better historical context and use recent hitobjects as reference / snap points.
|
||||||
|
/// </summary>
|
||||||
|
private const double editor_hit_object_fade_out_extension = 700;
|
||||||
|
|
||||||
|
public DrawableOsuEditPool(Func<DrawableHitObject, double, bool> checkHittable, Action<Drawable> onLoaded, int initialSize, int? maximumSize = null)
|
||||||
|
: base(checkHittable, onLoaded, initialSize, maximumSize)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override T CreateNewDrawable() => base.CreateNewDrawable().With(d => d.ApplyCustomUpdateState += updateState);
|
||||||
|
|
||||||
|
private void updateState(DrawableHitObject hitObject, ArmedState state)
|
||||||
|
{
|
||||||
|
if (state == ArmedState.Idle)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// adjust the visuals of certain object types to make them stay on screen for longer than usual.
|
||||||
|
switch (hitObject)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
// there are quite a few drawable hit types we don't want to extend (spinners, ticks etc.)
|
||||||
|
return;
|
||||||
|
|
||||||
|
case DrawableSlider _:
|
||||||
|
// no specifics to sliders but let them fade slower below.
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DrawableHitCircle circle: // also handles slider heads
|
||||||
|
circle.ApproachCircle
|
||||||
|
.FadeOutFromOne(editor_hit_object_fade_out_extension)
|
||||||
|
.Expire();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the existing fade out transform
|
||||||
|
var existing = hitObject.Transforms.LastOrDefault(t => t.TargetMember == nameof(Alpha));
|
||||||
|
|
||||||
|
if (existing == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
hitObject.RemoveTransform(existing);
|
||||||
|
|
||||||
|
using (hitObject.BeginAbsoluteSequence(existing.StartTime))
|
||||||
|
hitObject.FadeOut(editor_hit_object_fade_out_extension).Expire();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,13 +2,9 @@
|
|||||||
// 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 System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using osu.Framework.Graphics.Pooling;
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
|
||||||
using osu.Game.Rulesets.Osu.UI;
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -17,62 +13,21 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
{
|
{
|
||||||
public class DrawableOsuEditRuleset : DrawableOsuRuleset
|
public class DrawableOsuEditRuleset : DrawableOsuRuleset
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Hit objects are intentionally made to fade out at a constant slower rate than in gameplay.
|
|
||||||
/// This allows a mapper to gain better historical context and use recent hitobjects as reference / snap points.
|
|
||||||
/// </summary>
|
|
||||||
private const double editor_hit_object_fade_out_extension = 700;
|
|
||||||
|
|
||||||
public DrawableOsuEditRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods)
|
public DrawableOsuEditRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||||
: base(ruleset, beatmap, mods)
|
: base(ruleset, beatmap, mods)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override DrawableHitObject<OsuHitObject> CreateDrawableRepresentation(OsuHitObject h)
|
protected override Playfield CreatePlayfield() => new OsuEditPlayfield();
|
||||||
=> base.CreateDrawableRepresentation(h)?.With(d => d.ApplyCustomUpdateState += updateState);
|
|
||||||
|
|
||||||
private void updateState(DrawableHitObject hitObject, ArmedState state)
|
|
||||||
{
|
|
||||||
if (state == ArmedState.Idle)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// adjust the visuals of certain object types to make them stay on screen for longer than usual.
|
|
||||||
switch (hitObject)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
// there are quite a few drawable hit types we don't want to extent (spinners, ticks etc.)
|
|
||||||
return;
|
|
||||||
|
|
||||||
case DrawableSlider _:
|
|
||||||
// no specifics to sliders but let them fade slower below.
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DrawableHitCircle circle: // also handles slider heads
|
|
||||||
circle.ApproachCircle
|
|
||||||
.FadeOutFromOne(editor_hit_object_fade_out_extension)
|
|
||||||
.Expire();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the existing fade out transform
|
|
||||||
var existing = hitObject.Transforms.LastOrDefault(t => t.TargetMember == nameof(Alpha));
|
|
||||||
|
|
||||||
if (existing == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
hitObject.RemoveTransform(existing);
|
|
||||||
|
|
||||||
using (hitObject.BeginAbsoluteSequence(existing.StartTime))
|
|
||||||
hitObject.FadeOut(editor_hit_object_fade_out_extension).Expire();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Playfield CreatePlayfield() => new OsuPlayfieldNoCursor();
|
|
||||||
|
|
||||||
public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new OsuPlayfieldAdjustmentContainer { Size = Vector2.One };
|
public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new OsuPlayfieldAdjustmentContainer { Size = Vector2.One };
|
||||||
|
|
||||||
private class OsuPlayfieldNoCursor : OsuPlayfield
|
private class OsuEditPlayfield : OsuPlayfield
|
||||||
{
|
{
|
||||||
protected override GameplayCursorContainer CreateCursor() => null;
|
protected override GameplayCursorContainer CreateCursor() => null;
|
||||||
|
|
||||||
|
protected override DrawablePool<TDrawable> CreatePool<TDrawable>(int initialSize, int? maximumSize = null)
|
||||||
|
=> new DrawableOsuEditPool<TDrawable>(CheckHittable, OnHitObjectLoaded, initialSize, maximumSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,10 +28,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
public override void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables)
|
public override void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables)
|
||||||
{
|
{
|
||||||
foreach (var d in drawables)
|
foreach (var d in drawables)
|
||||||
{
|
|
||||||
d.HitObjectApplied += applyFadeInAdjustment;
|
d.HitObjectApplied += applyFadeInAdjustment;
|
||||||
applyFadeInAdjustment(d);
|
|
||||||
}
|
|
||||||
|
|
||||||
base.ApplyToDrawableHitObjects(drawables);
|
base.ApplyToDrawableHitObjects(drawables);
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
private Container scaleContainer;
|
private Container scaleContainer;
|
||||||
private InputManager inputManager;
|
private InputManager inputManager;
|
||||||
|
|
||||||
|
public DrawableHitCircle()
|
||||||
|
: this(null)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public DrawableHitCircle([CanBeNull] HitCircle h = null)
|
public DrawableHitCircle([CanBeNull] HitCircle h = null)
|
||||||
: base(h)
|
: base(h)
|
||||||
{
|
{
|
||||||
|
@ -83,7 +83,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
private OsuInputManager osuActionInputManager;
|
private OsuInputManager osuActionInputManager;
|
||||||
internal OsuInputManager OsuActionInputManager => osuActionInputManager ??= GetContainingInputManager() as OsuInputManager;
|
internal OsuInputManager OsuActionInputManager => osuActionInputManager ??= GetContainingInputManager() as OsuInputManager;
|
||||||
|
|
||||||
protected virtual void Shake(double maximumLength) => shakeContainer.Shake(maximumLength);
|
public virtual void Shake(double maximumLength) => shakeContainer.Shake(maximumLength);
|
||||||
|
|
||||||
protected override void UpdateInitialTransforms()
|
protected override void UpdateInitialTransforms()
|
||||||
{
|
{
|
||||||
|
32
osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuPool.cs
Normal file
32
osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuPool.cs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// 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;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Pooling;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||||
|
{
|
||||||
|
public class DrawableOsuPool<T> : DrawablePool<T>
|
||||||
|
where T : DrawableHitObject, new()
|
||||||
|
{
|
||||||
|
private readonly Func<DrawableHitObject, double, bool> checkHittable;
|
||||||
|
private readonly Action<Drawable> onLoaded;
|
||||||
|
|
||||||
|
public DrawableOsuPool(Func<DrawableHitObject, double, bool> checkHittable, Action<Drawable> onLoaded, int initialSize, int? maximumSize = null)
|
||||||
|
: base(initialSize, maximumSize)
|
||||||
|
{
|
||||||
|
this.checkHittable = checkHittable;
|
||||||
|
this.onLoaded = onLoaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override T CreateNewDrawable() => base.CreateNewDrawable().With(o =>
|
||||||
|
{
|
||||||
|
var osuObject = (DrawableOsuHitObject)(object)o;
|
||||||
|
|
||||||
|
osuObject.CheckHittable = checkHittable;
|
||||||
|
osuObject.OnLoadComplete += onLoaded;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -40,6 +40,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
private Container<DrawableSliderTail> tailContainer;
|
private Container<DrawableSliderTail> tailContainer;
|
||||||
private Container<DrawableSliderTick> tickContainer;
|
private Container<DrawableSliderTick> tickContainer;
|
||||||
private Container<DrawableSliderRepeat> repeatContainer;
|
private Container<DrawableSliderRepeat> repeatContainer;
|
||||||
|
private Container<PausableSkinnableSound> samplesContainer;
|
||||||
|
|
||||||
|
public DrawableSlider()
|
||||||
|
: this(null)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public DrawableSlider([CanBeNull] Slider s = null)
|
public DrawableSlider([CanBeNull] Slider s = null)
|
||||||
: base(s)
|
: base(s)
|
||||||
@ -63,6 +69,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
Alpha = 0
|
Alpha = 0
|
||||||
},
|
},
|
||||||
headContainer = new Container<DrawableSliderHead> { RelativeSizeAxes = Axes.Both },
|
headContainer = new Container<DrawableSliderHead> { RelativeSizeAxes = Axes.Both },
|
||||||
|
samplesContainer = new Container<PausableSkinnableSound> { RelativeSizeAxes = Axes.Both }
|
||||||
};
|
};
|
||||||
|
|
||||||
PositionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition);
|
PositionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition);
|
||||||
@ -100,7 +107,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
{
|
{
|
||||||
base.LoadSamples();
|
base.LoadSamples();
|
||||||
|
|
||||||
slidingSample?.Expire();
|
samplesContainer.Clear();
|
||||||
slidingSample = null;
|
slidingSample = null;
|
||||||
|
|
||||||
var firstSample = HitObject.Samples.FirstOrDefault();
|
var firstSample = HitObject.Samples.FirstOrDefault();
|
||||||
@ -110,7 +117,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
var clone = HitObject.SampleControlPoint.ApplyTo(firstSample);
|
var clone = HitObject.SampleControlPoint.ApplyTo(firstSample);
|
||||||
clone.Name = "sliderslide";
|
clone.Name = "sliderslide";
|
||||||
|
|
||||||
AddInternal(slidingSample = new PausableSkinnableSound(clone)
|
samplesContainer.Add(slidingSample = new PausableSkinnableSound(clone)
|
||||||
{
|
{
|
||||||
Looping = true
|
Looping = true
|
||||||
});
|
});
|
||||||
@ -159,10 +166,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
{
|
{
|
||||||
base.ClearNestedHitObjects();
|
base.ClearNestedHitObjects();
|
||||||
|
|
||||||
headContainer.Clear();
|
headContainer.Clear(false);
|
||||||
tailContainer.Clear();
|
tailContainer.Clear(false);
|
||||||
repeatContainer.Clear();
|
repeatContainer.Clear(false);
|
||||||
tickContainer.Clear();
|
tickContainer.Clear(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject)
|
protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject)
|
||||||
@ -173,17 +180,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
return new DrawableSliderTail(tail);
|
return new DrawableSliderTail(tail);
|
||||||
|
|
||||||
case SliderHeadCircle head:
|
case SliderHeadCircle head:
|
||||||
return new DrawableSliderHead(HitObject, head)
|
return new DrawableSliderHead(head);
|
||||||
{
|
|
||||||
OnShake = Shake,
|
|
||||||
CheckHittable = (d, t) => CheckHittable?.Invoke(d, t) ?? true
|
|
||||||
};
|
|
||||||
|
|
||||||
case SliderTick tick:
|
case SliderTick tick:
|
||||||
return new DrawableSliderTick(tick) { Position = tick.Position - HitObject.Position };
|
return new DrawableSliderTick(tick);
|
||||||
|
|
||||||
case SliderRepeat repeat:
|
case SliderRepeat repeat:
|
||||||
return new DrawableSliderRepeat(repeat, this) { Position = repeat.Position - HitObject.Position };
|
return new DrawableSliderRepeat(repeat);
|
||||||
}
|
}
|
||||||
|
|
||||||
return base.CreateNestedHitObject(hitObject);
|
return base.CreateNestedHitObject(hitObject);
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||||
@ -14,21 +16,43 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
|
|
||||||
protected override OsuSkinComponents CirclePieceComponent => OsuSkinComponents.SliderHeadHitCircle;
|
protected override OsuSkinComponents CirclePieceComponent => OsuSkinComponents.SliderHeadHitCircle;
|
||||||
|
|
||||||
private readonly Slider slider;
|
private DrawableSlider drawableSlider;
|
||||||
|
|
||||||
public DrawableSliderHead(Slider slider, SliderHeadCircle h)
|
private Slider slider => drawableSlider?.HitObject;
|
||||||
|
|
||||||
|
public DrawableSliderHead()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public DrawableSliderHead(SliderHeadCircle h)
|
||||||
: base(h)
|
: base(h)
|
||||||
{
|
{
|
||||||
this.slider = slider;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
pathVersion.BindTo(slider.Path.Version);
|
|
||||||
|
|
||||||
PositionBindable.BindValueChanged(_ => updatePosition());
|
PositionBindable.BindValueChanged(_ => updatePosition());
|
||||||
pathVersion.BindValueChanged(_ => updatePosition(), true);
|
pathVersion.BindValueChanged(_ => updatePosition());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnFree(HitObject hitObject)
|
||||||
|
{
|
||||||
|
base.OnFree(hitObject);
|
||||||
|
|
||||||
|
pathVersion.UnbindFrom(drawableSlider.PathVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnParentReceived(DrawableHitObject parent)
|
||||||
|
{
|
||||||
|
base.OnParentReceived(parent);
|
||||||
|
|
||||||
|
drawableSlider = (DrawableSlider)parent;
|
||||||
|
|
||||||
|
pathVersion.BindTo(drawableSlider.PathVersion);
|
||||||
|
|
||||||
|
OnShake = drawableSlider.Shake;
|
||||||
|
CheckHittable = (d, t) => drawableSlider.CheckHittable?.Invoke(d, t) ?? true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
@ -44,8 +68,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
|
|
||||||
public Action<double> OnShake;
|
public Action<double> OnShake;
|
||||||
|
|
||||||
protected override void Shake(double maximumLength) => OnShake?.Invoke(maximumLength);
|
public override void Shake(double maximumLength) => OnShake?.Invoke(maximumLength);
|
||||||
|
|
||||||
private void updatePosition() => Position = HitObject.Position - slider.Position;
|
private void updatePosition()
|
||||||
|
{
|
||||||
|
if (slider != null)
|
||||||
|
Position = HitObject.Position - slider.Position;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
{
|
{
|
||||||
public class DrawableSliderRepeat : DrawableOsuHitObject, ITrackSnaking
|
public class DrawableSliderRepeat : DrawableOsuHitObject, ITrackSnaking
|
||||||
{
|
{
|
||||||
private readonly SliderRepeat sliderRepeat;
|
public new SliderRepeat HitObject => (SliderRepeat)base.HitObject;
|
||||||
private readonly DrawableSlider drawableSlider;
|
|
||||||
|
|
||||||
private double animDuration;
|
private double animDuration;
|
||||||
|
|
||||||
@ -27,11 +26,16 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
|
|
||||||
public override bool DisplayResult => false;
|
public override bool DisplayResult => false;
|
||||||
|
|
||||||
public DrawableSliderRepeat(SliderRepeat sliderRepeat, DrawableSlider drawableSlider)
|
private DrawableSlider drawableSlider;
|
||||||
|
|
||||||
|
public DrawableSliderRepeat()
|
||||||
|
: base(null)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public DrawableSliderRepeat(SliderRepeat sliderRepeat)
|
||||||
: base(sliderRepeat)
|
: base(sliderRepeat)
|
||||||
{
|
{
|
||||||
this.sliderRepeat = sliderRepeat;
|
|
||||||
this.drawableSlider = drawableSlider;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -53,18 +57,27 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ScaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue), true);
|
ScaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnParentReceived(DrawableHitObject parent)
|
||||||
|
{
|
||||||
|
base.OnParentReceived(parent);
|
||||||
|
|
||||||
|
drawableSlider = (DrawableSlider)parent;
|
||||||
|
|
||||||
|
Position = HitObject.Position - drawableSlider.Position;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||||
{
|
{
|
||||||
if (sliderRepeat.StartTime <= Time.Current)
|
if (HitObject.StartTime <= Time.Current)
|
||||||
ApplyResult(r => r.Type = drawableSlider.Tracking.Value ? r.Judgement.MaxResult : r.Judgement.MinResult);
|
ApplyResult(r => r.Type = drawableSlider.Tracking.Value ? r.Judgement.MaxResult : r.Judgement.MinResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UpdateInitialTransforms()
|
protected override void UpdateInitialTransforms()
|
||||||
{
|
{
|
||||||
animDuration = Math.Min(300, sliderRepeat.SpanDuration);
|
animDuration = Math.Min(300, HitObject.SpanDuration);
|
||||||
|
|
||||||
this.Animate(
|
this.Animate(
|
||||||
d => d.FadeIn(animDuration),
|
d => d.FadeIn(animDuration),
|
||||||
@ -100,7 +113,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
// When the repeat is hit, the arrow should fade out on spot rather than following the slider
|
// When the repeat is hit, the arrow should fade out on spot rather than following the slider
|
||||||
if (IsHit) return;
|
if (IsHit) return;
|
||||||
|
|
||||||
bool isRepeatAtEnd = sliderRepeat.RepeatIndex % 2 == 0;
|
bool isRepeatAtEnd = HitObject.RepeatIndex % 2 == 0;
|
||||||
List<Vector2> curve = ((PlaySliderBody)drawableSlider.Body.Drawable).CurrentCurve;
|
List<Vector2> curve = ((PlaySliderBody)drawableSlider.Body.Drawable).CurrentCurve;
|
||||||
|
|
||||||
Position = isRepeatAtEnd ? end : start;
|
Position = isRepeatAtEnd ? end : start;
|
||||||
|
@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
{
|
{
|
||||||
public class DrawableSliderTail : DrawableOsuHitObject, IRequireTracking, ITrackSnaking
|
public class DrawableSliderTail : DrawableOsuHitObject, IRequireTracking, ITrackSnaking
|
||||||
{
|
{
|
||||||
private readonly SliderTailCircle tailCircle;
|
public new SliderTailCircle HitObject => (SliderTailCircle)base.HitObject;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The judgement text is provided by the <see cref="DrawableSlider"/>.
|
/// The judgement text is provided by the <see cref="DrawableSlider"/>.
|
||||||
@ -25,10 +25,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
private SkinnableDrawable circlePiece;
|
private SkinnableDrawable circlePiece;
|
||||||
private Container scaleContainer;
|
private Container scaleContainer;
|
||||||
|
|
||||||
|
public DrawableSliderTail()
|
||||||
|
: base(null)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public DrawableSliderTail(SliderTailCircle tailCircle)
|
public DrawableSliderTail(SliderTailCircle tailCircle)
|
||||||
: base(tailCircle)
|
: base(tailCircle)
|
||||||
{
|
{
|
||||||
this.tailCircle = tailCircle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -52,7 +56,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
ScaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue), true);
|
ScaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UpdateInitialTransforms()
|
protected override void UpdateInitialTransforms()
|
||||||
@ -92,6 +96,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateSnakingPosition(Vector2 start, Vector2 end) =>
|
public void UpdateSnakingPosition(Vector2 start, Vector2 end) =>
|
||||||
Position = tailCircle.RepeatIndex % 2 == 0 ? end : start;
|
Position = HitObject.RepeatIndex % 2 == 0 ? end : start;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
|
|
||||||
private SkinnableDrawable scaleContainer;
|
private SkinnableDrawable scaleContainer;
|
||||||
|
|
||||||
|
public DrawableSliderTick()
|
||||||
|
: base(null)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public DrawableSliderTick(SliderTick sliderTick)
|
public DrawableSliderTick(SliderTick sliderTick)
|
||||||
: base(sliderTick)
|
: base(sliderTick)
|
||||||
{
|
{
|
||||||
@ -54,7 +59,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
};
|
};
|
||||||
|
|
||||||
ScaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue), true);
|
ScaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnParentReceived(DrawableHitObject parent)
|
||||||
|
{
|
||||||
|
base.OnParentReceived(parent);
|
||||||
|
|
||||||
|
Position = HitObject.Position - ((DrawableSlider)parent).HitObject.Position;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||||
|
@ -29,10 +29,16 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
|
|
||||||
private Container<DrawableSpinnerTick> ticks;
|
private Container<DrawableSpinnerTick> ticks;
|
||||||
private SpinnerBonusDisplay bonusDisplay;
|
private SpinnerBonusDisplay bonusDisplay;
|
||||||
|
private Container<PausableSkinnableSound> samplesContainer;
|
||||||
|
|
||||||
private Bindable<bool> isSpinning;
|
private Bindable<bool> isSpinning;
|
||||||
private bool spinnerFrequencyModulate;
|
private bool spinnerFrequencyModulate;
|
||||||
|
|
||||||
|
public DrawableSpinner()
|
||||||
|
: this(null)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public DrawableSpinner([CanBeNull] Spinner s = null)
|
public DrawableSpinner([CanBeNull] Spinner s = null)
|
||||||
: base(s)
|
: base(s)
|
||||||
{
|
{
|
||||||
@ -70,7 +76,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Y = -120,
|
Y = -120,
|
||||||
}
|
},
|
||||||
|
samplesContainer = new Container<PausableSkinnableSound> { RelativeSizeAxes = Axes.Both }
|
||||||
};
|
};
|
||||||
|
|
||||||
PositionBindable.BindValueChanged(pos => Position = pos.NewValue);
|
PositionBindable.BindValueChanged(pos => Position = pos.NewValue);
|
||||||
@ -92,7 +99,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
{
|
{
|
||||||
base.LoadSamples();
|
base.LoadSamples();
|
||||||
|
|
||||||
spinningSample?.Expire();
|
samplesContainer.Clear();
|
||||||
spinningSample = null;
|
spinningSample = null;
|
||||||
|
|
||||||
var firstSample = HitObject.Samples.FirstOrDefault();
|
var firstSample = HitObject.Samples.FirstOrDefault();
|
||||||
@ -102,7 +109,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
var clone = HitObject.SampleControlPoint.ApplyTo(firstSample);
|
var clone = HitObject.SampleControlPoint.ApplyTo(firstSample);
|
||||||
clone.Name = "spinnerspin";
|
clone.Name = "spinnerspin";
|
||||||
|
|
||||||
AddInternal(spinningSample = new PausableSkinnableSound(clone)
|
samplesContainer.Add(spinningSample = new PausableSkinnableSound(clone)
|
||||||
{
|
{
|
||||||
Volume = { Value = 0 },
|
Volume = { Value = 0 },
|
||||||
Looping = true,
|
Looping = true,
|
||||||
@ -155,7 +162,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
protected override void ClearNestedHitObjects()
|
protected override void ClearNestedHitObjects()
|
||||||
{
|
{
|
||||||
base.ClearNestedHitObjects();
|
base.ClearNestedHitObjects();
|
||||||
ticks.Clear();
|
ticks.Clear(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject)
|
protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject)
|
||||||
|
@ -5,6 +5,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
{
|
{
|
||||||
public class DrawableSpinnerBonusTick : DrawableSpinnerTick
|
public class DrawableSpinnerBonusTick : DrawableSpinnerTick
|
||||||
{
|
{
|
||||||
|
public DrawableSpinnerBonusTick()
|
||||||
|
: base(null)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public DrawableSpinnerBonusTick(SpinnerBonusTick spinnerTick)
|
public DrawableSpinnerBonusTick(SpinnerBonusTick spinnerTick)
|
||||||
: base(spinnerTick)
|
: base(spinnerTick)
|
||||||
{
|
{
|
||||||
|
@ -7,6 +7,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
{
|
{
|
||||||
public override bool DisplayResult => false;
|
public override bool DisplayResult => false;
|
||||||
|
|
||||||
|
public DrawableSpinnerTick()
|
||||||
|
: base(null)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public DrawableSpinnerTick(SpinnerTick spinnerTick)
|
public DrawableSpinnerTick(SpinnerTick spinnerTick)
|
||||||
: base(spinnerTick)
|
: base(spinnerTick)
|
||||||
{
|
{
|
||||||
|
@ -12,7 +12,6 @@ using osu.Game.Rulesets.Mods;
|
|||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.Configuration;
|
using osu.Game.Rulesets.Osu.Configuration;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
|
||||||
using osu.Game.Rulesets.Osu.Replays;
|
using osu.Game.Rulesets.Osu.Replays;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
@ -24,11 +23,15 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
{
|
{
|
||||||
protected new OsuRulesetConfigManager Config => (OsuRulesetConfigManager)base.Config;
|
protected new OsuRulesetConfigManager Config => (OsuRulesetConfigManager)base.Config;
|
||||||
|
|
||||||
|
public new OsuPlayfield Playfield => (OsuPlayfield)base.Playfield;
|
||||||
|
|
||||||
public DrawableOsuRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null)
|
public DrawableOsuRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null)
|
||||||
: base(ruleset, beatmap, mods)
|
: base(ruleset, beatmap, mods)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override DrawableHitObject<OsuHitObject> CreateDrawableRepresentation(OsuHitObject h) => null;
|
||||||
|
|
||||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; // always show the gameplay cursor
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; // always show the gameplay cursor
|
||||||
|
|
||||||
protected override Playfield CreatePlayfield() => new OsuPlayfield();
|
protected override Playfield CreatePlayfield() => new OsuPlayfield();
|
||||||
@ -39,23 +42,6 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
|
|
||||||
protected override ResumeOverlay CreateResumeOverlay() => new OsuResumeOverlay();
|
protected override ResumeOverlay CreateResumeOverlay() => new OsuResumeOverlay();
|
||||||
|
|
||||||
public override DrawableHitObject<OsuHitObject> CreateDrawableRepresentation(OsuHitObject h)
|
|
||||||
{
|
|
||||||
switch (h)
|
|
||||||
{
|
|
||||||
case HitCircle circle:
|
|
||||||
return new DrawableHitCircle(circle);
|
|
||||||
|
|
||||||
case Slider slider:
|
|
||||||
return new DrawableSlider(slider);
|
|
||||||
|
|
||||||
case Spinner spinner:
|
|
||||||
return new DrawableSpinner(spinner);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new OsuFramedReplayInputHandler(replay);
|
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new OsuFramedReplayInputHandler(replay);
|
||||||
|
|
||||||
protected override ReplayRecorder CreateReplayRecorder(Replay replay) => new OsuReplayRecorder(replay);
|
protected override ReplayRecorder CreateReplayRecorder(Replay replay) => new OsuReplayRecorder(replay);
|
||||||
|
@ -13,6 +13,7 @@ using osu.Game.Rulesets.Judgements;
|
|||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.Configuration;
|
using osu.Game.Rulesets.Osu.Configuration;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables.Connections;
|
using osu.Game.Rulesets.Osu.Objects.Drawables.Connections;
|
||||||
using osu.Game.Rulesets.Osu.Scoring;
|
using osu.Game.Rulesets.Osu.Scoring;
|
||||||
@ -26,6 +27,8 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
{
|
{
|
||||||
public class OsuPlayfield : Playfield
|
public class OsuPlayfield : Playfield
|
||||||
{
|
{
|
||||||
|
public readonly Func<DrawableHitObject, double, bool> CheckHittable;
|
||||||
|
|
||||||
private readonly PlayfieldBorder playfieldBorder;
|
private readonly PlayfieldBorder playfieldBorder;
|
||||||
private readonly ProxyContainer approachCircles;
|
private readonly ProxyContainer approachCircles;
|
||||||
private readonly ProxyContainer spinnerProxies;
|
private readonly ProxyContainer spinnerProxies;
|
||||||
@ -78,6 +81,7 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
};
|
};
|
||||||
|
|
||||||
hitPolicy = new OrderedHitPolicy(HitObjectContainer);
|
hitPolicy = new OrderedHitPolicy(HitObjectContainer);
|
||||||
|
CheckHittable = hitPolicy.IsHittable;
|
||||||
|
|
||||||
var hitWindows = new OsuHitWindows();
|
var hitWindows = new OsuHitWindows();
|
||||||
|
|
||||||
@ -85,45 +89,70 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
poolDictionary.Add(result, new DrawableJudgementPool(result));
|
poolDictionary.Add(result, new DrawableJudgementPool(result));
|
||||||
|
|
||||||
AddRangeInternal(poolDictionary.Values);
|
AddRangeInternal(poolDictionary.Values);
|
||||||
|
|
||||||
|
NewResult += onNewResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader(true)]
|
[BackgroundDependencyLoader(true)]
|
||||||
private void load(OsuRulesetConfigManager config)
|
private void load(OsuRulesetConfigManager config)
|
||||||
{
|
{
|
||||||
config?.BindWith(OsuRulesetSetting.PlayfieldBorderStyle, playfieldBorder.PlayfieldBorderStyle);
|
config?.BindWith(OsuRulesetSetting.PlayfieldBorderStyle, playfieldBorder.PlayfieldBorderStyle);
|
||||||
|
|
||||||
|
registerPool<HitCircle, DrawableHitCircle>(10, 100);
|
||||||
|
|
||||||
|
registerPool<Slider, DrawableSlider>(10, 100);
|
||||||
|
registerPool<SliderHeadCircle, DrawableSliderHead>(10, 100);
|
||||||
|
registerPool<SliderTailCircle, DrawableSliderTail>(10, 100);
|
||||||
|
registerPool<SliderTick, DrawableSliderTick>(10, 100);
|
||||||
|
registerPool<SliderRepeat, DrawableSliderRepeat>(5, 50);
|
||||||
|
|
||||||
|
registerPool<Spinner, DrawableSpinner>(2, 20);
|
||||||
|
registerPool<SpinnerTick, DrawableSpinnerTick>(10, 100);
|
||||||
|
registerPool<SpinnerBonusTick, DrawableSpinnerBonusTick>(10, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Add(DrawableHitObject h)
|
private void registerPool<TObject, TDrawable>(int initialSize, int? maximumSize = null)
|
||||||
|
where TObject : HitObject
|
||||||
|
where TDrawable : DrawableHitObject, new()
|
||||||
|
=> RegisterPool<TObject, TDrawable>(CreatePool<TDrawable>(initialSize, maximumSize));
|
||||||
|
|
||||||
|
protected virtual DrawablePool<TDrawable> CreatePool<TDrawable>(int initialSize, int? maximumSize = null)
|
||||||
|
where TDrawable : DrawableHitObject, new()
|
||||||
|
=> new DrawableOsuPool<TDrawable>(CheckHittable, OnHitObjectLoaded, initialSize, maximumSize);
|
||||||
|
|
||||||
|
protected override HitObjectLifetimeEntry CreateLifetimeEntry(HitObject hitObject) => new OsuHitObjectLifetimeEntry(hitObject);
|
||||||
|
|
||||||
|
protected override void OnHitObjectAdded(HitObject hitObject)
|
||||||
{
|
{
|
||||||
DrawableOsuHitObject osuHitObject = (DrawableOsuHitObject)h;
|
base.OnHitObjectAdded(hitObject);
|
||||||
|
followPoints.AddFollowPoints((OsuHitObject)hitObject);
|
||||||
h.OnNewResult += onNewResult;
|
|
||||||
h.OnLoadComplete += d =>
|
|
||||||
{
|
|
||||||
if (d is DrawableSpinner)
|
|
||||||
spinnerProxies.Add(d.CreateProxy());
|
|
||||||
|
|
||||||
if (d is IDrawableHitObjectWithProxiedApproach c)
|
|
||||||
approachCircles.Add(c.ProxiedLayer.CreateProxy());
|
|
||||||
};
|
|
||||||
|
|
||||||
base.Add(h);
|
|
||||||
|
|
||||||
osuHitObject.CheckHittable = hitPolicy.IsHittable;
|
|
||||||
|
|
||||||
followPoints.AddFollowPoints(osuHitObject.HitObject);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool Remove(DrawableHitObject h)
|
protected override void OnHitObjectRemoved(HitObject hitObject)
|
||||||
{
|
{
|
||||||
DrawableOsuHitObject osuHitObject = (DrawableOsuHitObject)h;
|
base.OnHitObjectRemoved(hitObject);
|
||||||
|
followPoints.RemoveFollowPoints((OsuHitObject)hitObject);
|
||||||
|
}
|
||||||
|
|
||||||
bool result = base.Remove(h);
|
public void OnHitObjectLoaded(Drawable drawable)
|
||||||
|
{
|
||||||
|
switch (drawable)
|
||||||
|
{
|
||||||
|
case DrawableSliderHead _:
|
||||||
|
case DrawableSliderTail _:
|
||||||
|
case DrawableSliderTick _:
|
||||||
|
case DrawableSliderRepeat _:
|
||||||
|
case DrawableSpinnerTick _:
|
||||||
|
break;
|
||||||
|
|
||||||
if (result)
|
case DrawableSpinner _:
|
||||||
followPoints.RemoveFollowPoints(osuHitObject.HitObject);
|
spinnerProxies.Add(drawable.CreateProxy());
|
||||||
|
break;
|
||||||
|
|
||||||
return result;
|
case IDrawableHitObjectWithProxiedApproach approach:
|
||||||
|
approachCircles.Add(approach.ProxiedLayer.CreateProxy());
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onNewResult(DrawableHitObject judgedObject, JudgementResult result)
|
private void onNewResult(DrawableHitObject judgedObject, JudgementResult result)
|
||||||
@ -166,5 +195,15 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
return judgement;
|
return judgement;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class OsuHitObjectLifetimeEntry : HitObjectLifetimeEntry
|
||||||
|
{
|
||||||
|
public OsuHitObjectLifetimeEntry(HitObject hitObject)
|
||||||
|
: base(hitObject)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override double InitialLifetimeOffset => ((OsuHitObject)HitObject).TimePreempt;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.IO.Stores;
|
using osu.Framework.IO.Stores;
|
||||||
using osu.Framework.Testing;
|
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Tests.Beatmaps;
|
using osu.Game.Tests.Beatmaps;
|
||||||
@ -12,7 +11,6 @@ using static osu.Game.Skinning.LegacySkinConfiguration;
|
|||||||
|
|
||||||
namespace osu.Game.Tests.Gameplay
|
namespace osu.Game.Tests.Gameplay
|
||||||
{
|
{
|
||||||
[HeadlessTest]
|
|
||||||
public class TestSceneHitObjectSamples : HitObjectSampleTest
|
public class TestSceneHitObjectSamples : HitObjectSampleTest
|
||||||
{
|
{
|
||||||
protected override Ruleset CreatePlayerRuleset() => new OsuRuleset();
|
protected override Ruleset CreatePlayerRuleset() => new OsuRuleset();
|
||||||
|
@ -23,11 +23,13 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
DrawableSample[] samples = null;
|
DrawableSample[] samples = null;
|
||||||
ISamplePlaybackDisabler sampleDisabler = null;
|
ISamplePlaybackDisabler sampleDisabler = null;
|
||||||
|
|
||||||
AddStep("get variables", () =>
|
AddUntilStep("get variables", () =>
|
||||||
{
|
{
|
||||||
sampleDisabler = Player;
|
sampleDisabler = Player;
|
||||||
slider = Player.ChildrenOfType<DrawableSlider>().OrderBy(s => s.HitObject.StartTime).First();
|
slider = Player.ChildrenOfType<DrawableSlider>().OrderBy(s => s.HitObject.StartTime).FirstOrDefault();
|
||||||
samples = slider.ChildrenOfType<DrawableSample>().ToArray();
|
samples = slider?.ChildrenOfType<DrawableSample>().ToArray();
|
||||||
|
|
||||||
|
return slider != null;
|
||||||
});
|
});
|
||||||
|
|
||||||
AddUntilStep("wait for slider sliding then seek", () =>
|
AddUntilStep("wait for slider sliding then seek", () =>
|
||||||
|
@ -9,11 +9,14 @@ using System.Threading.Tasks;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
using osu.Framework.IO.Stores;
|
using osu.Framework.IO.Stores;
|
||||||
|
using osu.Framework.Testing;
|
||||||
using osu.Framework.Timing;
|
using osu.Framework.Timing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.Formats;
|
using osu.Game.Beatmaps.Formats;
|
||||||
using osu.Game.IO;
|
using osu.Game.IO;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Screens.Ranking;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osu.Game.Storyboards;
|
using osu.Game.Storyboards;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
@ -21,6 +24,7 @@ using osu.Game.Users;
|
|||||||
|
|
||||||
namespace osu.Game.Tests.Beatmaps
|
namespace osu.Game.Tests.Beatmaps
|
||||||
{
|
{
|
||||||
|
[HeadlessTest]
|
||||||
public abstract class HitObjectSampleTest : PlayerTestScene
|
public abstract class HitObjectSampleTest : PlayerTestScene
|
||||||
{
|
{
|
||||||
protected abstract IResourceStore<byte[]> Resources { get; }
|
protected abstract IResourceStore<byte[]> Resources { get; }
|
||||||
@ -44,7 +48,9 @@ namespace osu.Game.Tests.Beatmaps
|
|||||||
private readonly TestResourceStore beatmapSkinResourceStore = new TestResourceStore();
|
private readonly TestResourceStore beatmapSkinResourceStore = new TestResourceStore();
|
||||||
private SkinSourceDependencyContainer dependencies;
|
private SkinSourceDependencyContainer dependencies;
|
||||||
private IBeatmap currentTestBeatmap;
|
private IBeatmap currentTestBeatmap;
|
||||||
|
|
||||||
protected sealed override bool HasCustomSteps => true;
|
protected sealed override bool HasCustomSteps => true;
|
||||||
|
protected override bool Autoplay => true;
|
||||||
|
|
||||||
protected sealed override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
protected sealed override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||||
=> new DependencyContainer(dependencies = new SkinSourceDependencyContainer(base.CreateChildDependencies(parent)));
|
=> new DependencyContainer(dependencies = new SkinSourceDependencyContainer(base.CreateChildDependencies(parent)));
|
||||||
@ -54,6 +60,8 @@ namespace osu.Game.Tests.Beatmaps
|
|||||||
protected sealed override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null)
|
protected sealed override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null)
|
||||||
=> new TestWorkingBeatmap(beatmapInfo, beatmapSkinResourceStore, beatmap, storyboard, Clock, Audio);
|
=> new TestWorkingBeatmap(beatmapInfo, beatmapSkinResourceStore, beatmap, storyboard, Clock, Audio);
|
||||||
|
|
||||||
|
protected override TestPlayer CreatePlayer(Ruleset ruleset) => new TestPlayer(false);
|
||||||
|
|
||||||
protected void CreateTestWithBeatmap(string filename)
|
protected void CreateTestWithBeatmap(string filename)
|
||||||
{
|
{
|
||||||
CreateTest(() =>
|
CreateTest(() =>
|
||||||
@ -73,6 +81,9 @@ namespace osu.Game.Tests.Beatmaps
|
|||||||
currentTestBeatmap.BeatmapInfo.Ruleset = rulesetStore.GetRuleset(currentTestBeatmap.BeatmapInfo.RulesetID);
|
currentTestBeatmap.BeatmapInfo.Ruleset = rulesetStore.GetRuleset(currentTestBeatmap.BeatmapInfo.RulesetID);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
AddStep("seek to completion", () => Player.GameplayClockContainer.Seek(Player.DrawableRuleset.Objects.Last().GetEndTime()));
|
||||||
|
AddUntilStep("results displayed", () => Stack.CurrentScreen is ResultsScreen);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void SetupSkins(string beatmapFile, string userFile)
|
protected void SetupSkins(string beatmapFile, string userFile)
|
||||||
|
Loading…
Reference in New Issue
Block a user