diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs
index 956d0e0c14..182cc51572 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs
@@ -106,20 +106,37 @@ namespace osu.Game.Rulesets.Catch.Tests
public void TestCatcherCatchWidth()
{
float halfWidth = Catcher.CalculateCatchWidth(new BeatmapDifficulty { CircleSize = 0 }) / 2;
+
+ AddStep("move catcher to center", () => catcher.X = CatchPlayfield.CENTER_X);
+
+ float leftPlateBounds = CatchPlayfield.CENTER_X - halfWidth;
+ float rightPlateBounds = CatchPlayfield.CENTER_X + halfWidth;
+
AddStep("catch fruit", () =>
{
- attemptCatch(new Fruit { X = -halfWidth + 1 });
- attemptCatch(new Fruit { X = halfWidth - 1 });
+ attemptCatch(new Fruit { X = leftPlateBounds + 1 });
+ attemptCatch(new Fruit { X = rightPlateBounds - 1 });
});
checkPlate(2);
+
AddStep("miss fruit", () =>
{
- attemptCatch(new Fruit { X = -halfWidth - 1 });
- attemptCatch(new Fruit { X = halfWidth + 1 });
+ attemptCatch(new Fruit { X = leftPlateBounds - 1 });
+ attemptCatch(new Fruit { X = rightPlateBounds + 1 });
});
checkPlate(2);
}
+ [Test]
+ public void TestFruitClampedToCatchableRegion()
+ {
+ AddStep("catch fruit left", () => attemptCatch(new Fruit { X = -CatchPlayfield.WIDTH }));
+ checkPlate(1);
+ AddStep("move catcher to right", () => catcher.X = CatchPlayfield.WIDTH);
+ AddStep("catch fruit right", () => attemptCatch(new Fruit { X = CatchPlayfield.WIDTH * 2 }));
+ checkPlate(2);
+ }
+
[Test]
public void TestFruitChangesCatcherState()
{
diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
index 6e01c44e1f..cd2b8348e2 100644
--- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
+++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
@@ -3,6 +3,7 @@
#nullable disable
+using System;
using Newtonsoft.Json;
using osu.Framework.Bindables;
using osu.Game.Beatmaps;
@@ -69,7 +70,7 @@ namespace osu.Game.Rulesets.Catch.Objects
/// This value is the original value plus the offset applied by the beatmap processing.
/// Use if a value not affected by the offset is desired.
///
- public float EffectiveX => OriginalX + XOffset;
+ public float EffectiveX => Math.Clamp(OriginalX + XOffset, 0, CatchPlayfield.WIDTH);
public double TimePreempt { get; set; } = 1000;
diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
index 311e15116e..015457e84f 100644
--- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
+++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
@@ -11,6 +11,7 @@ using Newtonsoft.Json;
using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
@@ -84,8 +85,8 @@ namespace osu.Game.Rulesets.Catch.Objects
AddNested(new TinyDroplet
{
StartTime = t + lastEvent.Value.Time,
- X = OriginalX + Path.PositionAt(
- lastEvent.Value.PathProgress + (t / sinceLastTick) * (e.PathProgress - lastEvent.Value.PathProgress)).X,
+ X = ClampToPlayfield(EffectiveX + Path.PositionAt(
+ lastEvent.Value.PathProgress + (t / sinceLastTick) * (e.PathProgress - lastEvent.Value.PathProgress)).X),
});
}
}
@@ -102,7 +103,7 @@ namespace osu.Game.Rulesets.Catch.Objects
{
Samples = dropletSamples,
StartTime = e.Time,
- X = OriginalX + Path.PositionAt(e.PathProgress).X,
+ X = ClampToPlayfield(EffectiveX + Path.PositionAt(e.PathProgress).X),
});
break;
@@ -113,14 +114,16 @@ namespace osu.Game.Rulesets.Catch.Objects
{
Samples = this.GetNodeSamples(nodeIndex++),
StartTime = e.Time,
- X = OriginalX + Path.PositionAt(e.PathProgress).X,
+ X = ClampToPlayfield(EffectiveX + Path.PositionAt(e.PathProgress).X),
});
break;
}
}
}
- public float EndX => OriginalX + this.CurvePositionAt(1).X;
+ public float EndX => ClampToPlayfield(EffectiveX + this.CurvePositionAt(1).X);
+
+ public float ClampToPlayfield(float value) => Math.Clamp(value, 0, CatchPlayfield.WIDTH);
[JsonIgnore]
public double Duration