mirror of
https://github.com/ppy/osu.git
synced 2024-12-15 06:35:05 +08:00
Merge pull request #29403 from smoogipoo/fix-catch-missing-objects
Fix fruit positions getting mangled when exploded from the plate
This commit is contained in:
commit
1b29b9bbb8
@ -248,7 +248,8 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
{
|
{
|
||||||
AddStep("enable hit lighting", () => config.SetValue(OsuSetting.HitLighting, true));
|
AddStep("enable hit lighting", () => config.SetValue(OsuSetting.HitLighting, true));
|
||||||
AddStep("catch fruit", () => attemptCatch(new Fruit()));
|
AddStep("catch fruit", () => attemptCatch(new Fruit()));
|
||||||
AddAssert("correct hit lighting colour", () => catcher.ChildrenOfType<HitExplosion>().First()?.Entry?.ObjectColour == this.ChildrenOfType<DrawableCatchHitObject>().First().AccentColour.Value);
|
AddAssert("correct hit lighting colour",
|
||||||
|
() => catcher.ChildrenOfType<HitExplosion>().First()?.Entry?.ObjectColour == this.ChildrenOfType<DrawableCatchHitObject>().First().AccentColour.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -259,6 +260,16 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
AddAssert("no hit lighting", () => !catcher.ChildrenOfType<HitExplosion>().Any());
|
AddAssert("no hit lighting", () => !catcher.ChildrenOfType<HitExplosion>().Any());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestAllExplodedObjectsAtUniquePositions()
|
||||||
|
{
|
||||||
|
AddStep("catch normal fruit", () => attemptCatch(new Fruit()));
|
||||||
|
AddStep("catch normal fruit", () => attemptCatch(new Fruit { IndexInBeatmap = 2, LastInCombo = true }));
|
||||||
|
AddAssert("two fruit at distinct x coordinates",
|
||||||
|
() => this.ChildrenOfType<CaughtFruit>().Select(f => f.DrawPosition.X).Distinct(),
|
||||||
|
() => Has.Exactly(2).Items);
|
||||||
|
}
|
||||||
|
|
||||||
private void checkPlate(int count) => AddAssert($"{count} objects on the plate", () => catcher.CaughtObjects.Count() == count);
|
private void checkPlate(int count) => AddAssert($"{count} objects on the plate", () => catcher.CaughtObjects.Count() == count);
|
||||||
|
|
||||||
private void checkState(CatcherAnimationState state) => AddAssert($"catcher state is {state}", () => catcher.CurrentState == state);
|
private void checkState(CatcherAnimationState state) => AddAssert($"catcher state is {state}", () => catcher.CurrentState == state);
|
||||||
|
@ -21,11 +21,9 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
|||||||
public Bindable<Color4> AccentColour { get; } = new Bindable<Color4>();
|
public Bindable<Color4> AccentColour { get; } = new Bindable<Color4>();
|
||||||
public Bindable<bool> HyperDash { get; } = new Bindable<bool>();
|
public Bindable<bool> HyperDash { get; } = new Bindable<bool>();
|
||||||
public Bindable<int> IndexInBeatmap { get; } = new Bindable<int>();
|
public Bindable<int> IndexInBeatmap { get; } = new Bindable<int>();
|
||||||
|
public Vector2 DisplayPosition => DrawPosition;
|
||||||
public Vector2 DisplaySize => Size * Scale;
|
public Vector2 DisplaySize => Size * Scale;
|
||||||
|
|
||||||
public float DisplayRotation => Rotation;
|
public float DisplayRotation => Rotation;
|
||||||
|
|
||||||
public double DisplayStartTime => HitObject.StartTime;
|
public double DisplayStartTime => HitObject.StartTime;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -44,19 +42,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
|||||||
Size = new Vector2(CatchHitObject.OBJECT_RADIUS * 2);
|
Size = new Vector2(CatchHitObject.OBJECT_RADIUS * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Copies the hit object visual state from another <see cref="IHasCatchObjectState"/> object.
|
|
||||||
/// </summary>
|
|
||||||
public virtual void CopyStateFrom(IHasCatchObjectState objectState)
|
|
||||||
{
|
|
||||||
HitObject = objectState.HitObject;
|
|
||||||
Scale = Vector2.Divide(objectState.DisplaySize, Size);
|
|
||||||
Rotation = objectState.DisplayRotation;
|
|
||||||
AccentColour.Value = objectState.AccentColour.Value;
|
|
||||||
HyperDash.Value = objectState.HyperDash.Value;
|
|
||||||
IndexInBeatmap.Value = objectState.IndexInBeatmap.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void FreeAfterUse()
|
protected override void FreeAfterUse()
|
||||||
{
|
{
|
||||||
ClearTransforms();
|
ClearTransforms();
|
||||||
@ -64,5 +49,16 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
|||||||
|
|
||||||
base.FreeAfterUse();
|
base.FreeAfterUse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void RestoreState(CatchObjectState state)
|
||||||
|
{
|
||||||
|
HitObject = state.HitObject;
|
||||||
|
AccentColour.Value = state.AccentColour;
|
||||||
|
HyperDash.Value = state.HyperDash;
|
||||||
|
IndexInBeatmap.Value = state.IndexInBeatmap;
|
||||||
|
Position = state.DisplayPosition;
|
||||||
|
Scale = Vector2.Divide(state.DisplaySize, Size);
|
||||||
|
Rotation = state.DisplayRotation;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected readonly Container ScalingContainer;
|
protected readonly Container ScalingContainer;
|
||||||
|
|
||||||
|
public Vector2 DisplayPosition => DrawPosition;
|
||||||
|
|
||||||
public Vector2 DisplaySize => ScalingContainer.Size * ScalingContainer.Scale;
|
public Vector2 DisplaySize => ScalingContainer.Size * ScalingContainer.Scale;
|
||||||
|
|
||||||
public float DisplayRotation => ScalingContainer.Rotation;
|
public float DisplayRotation => ScalingContainer.Rotation;
|
||||||
@ -95,5 +97,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
|||||||
|
|
||||||
base.OnFree();
|
base.OnFree();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void RestoreState(CatchObjectState state) => throw new NotSupportedException("Cannot restore state into a drawable catch hitobject.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,17 +13,35 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
|||||||
public interface IHasCatchObjectState
|
public interface IHasCatchObjectState
|
||||||
{
|
{
|
||||||
PalpableCatchHitObject HitObject { get; }
|
PalpableCatchHitObject HitObject { get; }
|
||||||
|
|
||||||
double DisplayStartTime { get; }
|
|
||||||
|
|
||||||
Bindable<Color4> AccentColour { get; }
|
Bindable<Color4> AccentColour { get; }
|
||||||
|
|
||||||
Bindable<bool> HyperDash { get; }
|
Bindable<bool> HyperDash { get; }
|
||||||
|
|
||||||
Bindable<int> IndexInBeatmap { get; }
|
Bindable<int> IndexInBeatmap { get; }
|
||||||
|
double DisplayStartTime { get; }
|
||||||
|
Vector2 DisplayPosition { get; }
|
||||||
Vector2 DisplaySize { get; }
|
Vector2 DisplaySize { get; }
|
||||||
|
|
||||||
float DisplayRotation { get; }
|
float DisplayRotation { get; }
|
||||||
|
|
||||||
|
void RestoreState(CatchObjectState state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class HasCatchObjectStateExtensions
|
||||||
|
{
|
||||||
|
public static CatchObjectState SaveState(this IHasCatchObjectState target) => new CatchObjectState(
|
||||||
|
target.HitObject,
|
||||||
|
target.AccentColour.Value,
|
||||||
|
target.HyperDash.Value,
|
||||||
|
target.IndexInBeatmap.Value,
|
||||||
|
target.DisplayPosition,
|
||||||
|
target.DisplaySize,
|
||||||
|
target.DisplayRotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly record struct CatchObjectState(
|
||||||
|
PalpableCatchHitObject HitObject,
|
||||||
|
Color4 AccentColour,
|
||||||
|
bool HyperDash,
|
||||||
|
int IndexInBeatmap,
|
||||||
|
Vector2 DisplayPosition,
|
||||||
|
Vector2 DisplaySize,
|
||||||
|
float DisplayRotation);
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// 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;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
@ -362,7 +363,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
if (caughtObject == null) return;
|
if (caughtObject == null) return;
|
||||||
|
|
||||||
caughtObject.CopyStateFrom(drawableObject);
|
caughtObject.RestoreState(drawableObject.SaveState());
|
||||||
caughtObject.Anchor = Anchor.TopCentre;
|
caughtObject.Anchor = Anchor.TopCentre;
|
||||||
caughtObject.Position = position;
|
caughtObject.Position = position;
|
||||||
caughtObject.Scale *= caught_fruit_scale_adjust;
|
caughtObject.Scale *= caught_fruit_scale_adjust;
|
||||||
@ -411,41 +412,50 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private CaughtObject getDroppedObject(CaughtObject caughtObject)
|
private CaughtObject getDroppedObject(CatchObjectState state)
|
||||||
{
|
{
|
||||||
var droppedObject = getCaughtObject(caughtObject.HitObject);
|
var droppedObject = getCaughtObject(state.HitObject);
|
||||||
Debug.Assert(droppedObject != null);
|
Debug.Assert(droppedObject != null);
|
||||||
|
|
||||||
droppedObject.CopyStateFrom(caughtObject);
|
droppedObject.RestoreState(state);
|
||||||
droppedObject.Anchor = Anchor.TopLeft;
|
droppedObject.Anchor = Anchor.TopLeft;
|
||||||
droppedObject.Position = caughtObjectContainer.ToSpaceOfOtherDrawable(caughtObject.DrawPosition, droppedObjectTarget);
|
droppedObject.Position = caughtObjectContainer.ToSpaceOfOtherDrawable(state.DisplayPosition, droppedObjectTarget);
|
||||||
|
|
||||||
return droppedObject;
|
return droppedObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void clearPlate(DroppedObjectAnimation animation)
|
private void clearPlate(DroppedObjectAnimation animation)
|
||||||
{
|
{
|
||||||
var caughtObjects = caughtObjectContainer.Children.ToArray();
|
int caughtCount = caughtObjectContainer.Children.Count;
|
||||||
|
CatchObjectState[] states = ArrayPool<CatchObjectState>.Shared.Rent(caughtCount);
|
||||||
|
|
||||||
caughtObjectContainer.Clear(false);
|
try
|
||||||
|
{
|
||||||
|
for (int i = 0; i < caughtCount; i++)
|
||||||
|
states[i] = caughtObjectContainer.Children[i].SaveState();
|
||||||
|
|
||||||
// Use the already returned PoolableDrawables for new objects
|
caughtObjectContainer.Clear(false);
|
||||||
var droppedObjects = caughtObjects.Select(getDroppedObject).ToArray();
|
|
||||||
|
|
||||||
droppedObjectTarget.AddRange(droppedObjects);
|
for (int i = 0; i < caughtCount; i++)
|
||||||
|
{
|
||||||
foreach (var droppedObject in droppedObjects)
|
CaughtObject obj = getDroppedObject(states[i]);
|
||||||
applyDropAnimation(droppedObject, animation);
|
droppedObjectTarget.Add(obj);
|
||||||
|
applyDropAnimation(obj, animation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ArrayPool<CatchObjectState>.Shared.Return(states);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeFromPlate(CaughtObject caughtObject, DroppedObjectAnimation animation)
|
private void removeFromPlate(CaughtObject caughtObject, DroppedObjectAnimation animation)
|
||||||
{
|
{
|
||||||
|
CatchObjectState state = caughtObject.SaveState();
|
||||||
caughtObjectContainer.Remove(caughtObject, false);
|
caughtObjectContainer.Remove(caughtObject, false);
|
||||||
|
|
||||||
var droppedObject = getDroppedObject(caughtObject);
|
var droppedObject = getDroppedObject(state);
|
||||||
|
|
||||||
droppedObjectTarget.Add(droppedObject);
|
droppedObjectTarget.Add(droppedObject);
|
||||||
|
|
||||||
applyDropAnimation(droppedObject, animation);
|
applyDropAnimation(droppedObject, animation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user