Skip to content

Commit ba96b63

Browse files
Fix ShapeCast on sensors (space-wizards#6588)
* Filter hard-only fixtures in sensor-only raycasts * Align * Tests * raycast
1 parent 471e92d commit ba96b63

2 files changed

Lines changed: 124 additions & 0 deletions

File tree

Robust.Shared.IntegrationTests/Physics/RayCast_Test.cs

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.Linq;
23
using System.Numerics;
34
using NUnit.Framework;
@@ -17,6 +18,13 @@ namespace Robust.UnitTesting.Shared.Physics;
1718
[TestFixture]
1819
internal sealed class RayCast_Test
1920
{
21+
public enum SensorCastKind
22+
{
23+
RayAll,
24+
RayClosest,
25+
Shape,
26+
}
27+
2028
private static TestCaseData[] _rayCases =
2129
{
2230
// Ray goes through
@@ -55,6 +63,34 @@ internal sealed class RayCast_Test
5563
new(new SlimPolygon(Box2.UnitCentered), new Transform(Vector2.Zero, Angle.Zero), -Vector2.UnitY, null),
5664
};
5765

66+
private static TestCaseData[] _sensorCases =
67+
{
68+
new TestCaseData(SensorCastKind.RayAll, true, false)
69+
.SetName("RayCast all returns hard fixtures without sensor query flag"),
70+
new TestCaseData(SensorCastKind.RayAll, true, true)
71+
.SetName("RayCast all returns hard fixtures with sensor query flag"),
72+
new TestCaseData(SensorCastKind.RayAll, false, false)
73+
.SetName("RayCast all filters sensor fixtures without sensor query flag"),
74+
new TestCaseData(SensorCastKind.RayAll, false, true)
75+
.SetName("RayCast all returns sensor fixtures with sensor query flag"),
76+
new TestCaseData(SensorCastKind.RayClosest, true, false)
77+
.SetName("RayCast closest returns hard fixtures without sensor query flag"),
78+
new TestCaseData(SensorCastKind.RayClosest, true, true)
79+
.SetName("RayCast closest returns hard fixtures with sensor query flag"),
80+
new TestCaseData(SensorCastKind.RayClosest, false, false)
81+
.SetName("RayCast closest filters sensor fixtures without sensor query flag"),
82+
new TestCaseData(SensorCastKind.RayClosest, false, true)
83+
.SetName("RayCast closest returns sensor fixtures with sensor query flag"),
84+
new TestCaseData(SensorCastKind.Shape, true, false)
85+
.SetName("ShapeCast returns hard fixtures without sensor query flag"),
86+
new TestCaseData(SensorCastKind.Shape, true, true)
87+
.SetName("ShapeCast returns hard fixtures with sensor query flag"),
88+
new TestCaseData(SensorCastKind.Shape, false, false)
89+
.SetName("ShapeCast filters sensor fixtures without sensor query flag"),
90+
new TestCaseData(SensorCastKind.Shape, false, true)
91+
.SetName("ShapeCast returns sensor fixtures with sensor query flag"),
92+
};
93+
5894
[Test, TestCaseSource(nameof(_rayCases))]
5995
public void RayCast(Vector2 origin, Vector2 direction, Vector2? point)
6096
{
@@ -113,6 +149,55 @@ public void ShapeCast(IPhysShape shape, Transform origin, Vector2 direction, Vec
113149
}
114150
}
115151

152+
[Test, TestCaseSource(nameof(_sensorCases))]
153+
public void SensorFixtureCasts(SensorCastKind kind, bool hardFixture, bool includeSensors)
154+
{
155+
var sim = RobustServerSimulation.NewSimulation().RegisterEntitySystems(f =>
156+
{
157+
f.LoadExtraSystemType<RayCastSystem>();
158+
}).InitializeInstance();
159+
SetupSensorFixture(sim, out var mapId, out var target, hardFixture);
160+
var raycast = sim.System<RayCastSystem>();
161+
162+
var flags = QueryFlags.Dynamic | QueryFlags.Static;
163+
if (includeSensors)
164+
flags |= QueryFlags.Sensors;
165+
166+
var filter = new QueryFilter
167+
{
168+
LayerBits = 1,
169+
Flags = flags,
170+
};
171+
172+
var hits = kind switch
173+
{
174+
SensorCastKind.RayAll => raycast.CastRay(
175+
mapId,
176+
Vector2.UnitX / 2f,
177+
Vector2.UnitY * 3f,
178+
filter),
179+
SensorCastKind.RayClosest => raycast.CastRayClosest(
180+
mapId,
181+
Vector2.UnitX / 2f,
182+
Vector2.UnitY * 3f,
183+
filter),
184+
SensorCastKind.Shape => raycast.CastShape(
185+
mapId,
186+
new PhysShapeCircle(0.1f),
187+
new Transform(Vector2.UnitX / 2f, Angle.Zero),
188+
Vector2.UnitY * 3f,
189+
filter,
190+
RayCastSystem.RayCastAllCallback),
191+
_ => throw new ArgumentOutOfRangeException(nameof(kind), kind, null),
192+
};
193+
194+
var expected = hardFixture || includeSensors
195+
? new[] { target }
196+
: Array.Empty<EntityUid>();
197+
198+
Assert.That(hits.Results.Select(hit => hit.Entity).ToArray(), Is.EqualTo(expected));
199+
}
200+
116201
private void Setup(ISimulation sim, out MapId mapId)
117202
{
118203
var entManager = sim.Resolve<IEntityManager>();
@@ -142,4 +227,38 @@ private void Setup(ISimulation sim, out MapId mapId)
142227
entManager.System<SharedTransformSystem>().SetLocalRotation(grid.Owner, Angle.FromDegrees(90));
143228
entManager.System<SharedTransformSystem>().SetLocalPosition(grid.Owner, Vector2.UnitX / 2f);
144229
}
230+
231+
private void SetupSensorFixture(ISimulation sim, out MapId mapId, out EntityUid target, bool hard)
232+
{
233+
var entManager = sim.Resolve<IEntityManager>();
234+
var mapSystem = entManager.System<SharedMapSystem>();
235+
var fixtureSystem = entManager.System<FixtureSystem>();
236+
var physicsSystem = entManager.System<SharedPhysicsSystem>();
237+
238+
mapSystem.CreateMap(out mapId);
239+
var grid = mapSystem.CreateGridEntity(mapId);
240+
241+
for (var i = 0; i < 3; i++)
242+
{
243+
mapSystem.SetTile(grid, new Vector2i(0, i), new Tile(1));
244+
}
245+
246+
target = SpawnCastTarget(entManager, fixtureSystem, physicsSystem, grid.Owner, new Vector2(0.5f, 1f), hard);
247+
}
248+
249+
private EntityUid SpawnCastTarget(
250+
IEntityManager entManager,
251+
FixtureSystem fixtureSystem,
252+
SharedPhysicsSystem physicsSystem,
253+
EntityUid parent,
254+
Vector2 position,
255+
bool hard)
256+
{
257+
var uid = entManager.SpawnEntity(null, new EntityCoordinates(parent, position));
258+
var physics = entManager.AddComponent<PhysicsComponent>(uid);
259+
fixtureSystem.CreateFixture(uid, "fix1", new Fixture(new PhysShapeCircle(0.25f), 1, 1, hard));
260+
physicsSystem.SetCanCollide(uid, true, body: physics);
261+
Assert.That(physics.CanCollide);
262+
return uid;
263+
}
145264
}

Robust.Shared/Physics/Systems/RayCastSystem.Geometry.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,11 @@ private static float RayCastCallback(RayCastInput input, FixtureProxy proxy, ref
112112
return input.MaxFraction;
113113
}
114114

115+
if ((worldContext.Filter.Flags & QueryFlags.Sensors) == 0x0 && !proxy.Fixture.Hard)
116+
{
117+
return input.MaxFraction;
118+
}
119+
115120
if (worldContext.Filter.IsIgnored?.Invoke(proxy.Entity) == true)
116121
{
117122
return input.MaxFraction;

0 commit comments

Comments
 (0)