mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-14 19:29:36 +01:00
Double-buffer contact events (#6295)
* Add * a * Fix test * note * Fix reflection
This commit is contained in:
@@ -35,7 +35,7 @@ END TEMPLATE-->
|
||||
|
||||
### Breaking changes
|
||||
|
||||
*None yet*
|
||||
* StartCollide and EndCollide events are now buffered until the end of physics substeps instead of being raised during the CollideContacts step. EndCollide events are double-buffered and any new ones raised while the events are being dispatched will now go out on the next tick / substep.
|
||||
|
||||
### New features
|
||||
|
||||
|
||||
@@ -106,6 +106,7 @@ public sealed partial class PhysicsSystem
|
||||
}
|
||||
|
||||
UpdateIsTouching(contacts);
|
||||
DispatchEvents();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -353,8 +353,8 @@ public abstract partial class SharedPhysicsSystem
|
||||
{
|
||||
var ev1 = new EndCollideEvent(aUid, bUid, contact.FixtureAId, contact.FixtureBId, fixtureA, fixtureB, bodyA, bodyB);
|
||||
var ev2 = new EndCollideEvent(bUid, aUid, contact.FixtureBId, contact.FixtureAId, fixtureB, fixtureA, bodyB, bodyA);
|
||||
RaiseLocalEvent(aUid, ref ev1);
|
||||
RaiseLocalEvent(bUid, ref ev2);
|
||||
_endCollideEvents[_endEventIndex].Add(ev1);
|
||||
_endCollideEvents[_endEventIndex].Add(ev2);
|
||||
}
|
||||
|
||||
if (contact.Manifold.PointCount > 0 && contact.FixtureA?.Hard == true && contact.FixtureB?.Hard == true)
|
||||
@@ -603,8 +603,9 @@ public abstract partial class SharedPhysicsSystem
|
||||
var ev1 = new StartCollideEvent(uidA, uidB, contact.FixtureAId, contact.FixtureBId, fixtureA, fixtureB, bodyA, bodyB, points, contact.Manifold.PointCount, worldNormal);
|
||||
var ev2 = new StartCollideEvent(uidB, uidA, contact.FixtureBId, contact.FixtureAId, fixtureB, fixtureA, bodyB, bodyA, points, contact.Manifold.PointCount, worldNormal);
|
||||
|
||||
RaiseLocalEvent(uidA, ref ev1, true);
|
||||
RaiseLocalEvent(uidB, ref ev2, true);
|
||||
_startCollideEvents.Add(ev1);
|
||||
_startCollideEvents.Add(ev2);
|
||||
|
||||
break;
|
||||
}
|
||||
case ContactStatus.Touching:
|
||||
@@ -626,8 +627,8 @@ public abstract partial class SharedPhysicsSystem
|
||||
var ev1 = new EndCollideEvent(uidA, uidB, contact.FixtureAId, contact.FixtureBId, fixtureA, fixtureB, bodyA, bodyB);
|
||||
var ev2 = new EndCollideEvent(uidB, uidA, contact.FixtureBId, contact.FixtureAId, fixtureB, fixtureA, bodyB, bodyA);
|
||||
|
||||
RaiseLocalEvent(uidA, ref ev1);
|
||||
RaiseLocalEvent(uidB, ref ev2);
|
||||
_endCollideEvents[_endEventIndex].Add(ev1);
|
||||
_endCollideEvents[_endEventIndex].Add(ev2);
|
||||
break;
|
||||
}
|
||||
case ContactStatus.NoContact:
|
||||
|
||||
30
Robust.Shared/Physics/Systems/SharedPhysicsSystem.Events.cs
Normal file
30
Robust.Shared/Physics/Systems/SharedPhysicsSystem.Events.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
namespace Robust.Shared.Physics.Systems;
|
||||
|
||||
public abstract partial class SharedPhysicsSystem
|
||||
{
|
||||
protected void DispatchEvents()
|
||||
{
|
||||
// This will raise events even if the contact is gone which is fine I think
|
||||
// because otherwise we may get issues with events not getting raised in some cases.
|
||||
|
||||
// Swap the end index over so new events happen next tick.
|
||||
_endEventIndex = 1 - _endEventIndex;
|
||||
|
||||
// Raises all the buffered events once physics step is done.
|
||||
foreach (var ev in _startCollideEvents)
|
||||
{
|
||||
var elem = ev;
|
||||
RaiseLocalEvent(ev.OurEntity, ref elem);
|
||||
}
|
||||
|
||||
_startCollideEvents.Clear();
|
||||
|
||||
foreach (var ev in _endCollideEvents[1 - _endEventIndex])
|
||||
{
|
||||
var elem = ev;
|
||||
RaiseLocalEvent(ev.OurEntity, ref elem);
|
||||
}
|
||||
|
||||
_endCollideEvents[1 - _endEventIndex].Clear();
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Prometheus;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Containers;
|
||||
@@ -50,6 +51,25 @@ namespace Robust.Shared.Physics.Systems
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly CollisionWakeSystem _wakeSystem = default!;
|
||||
|
||||
/*
|
||||
* Events
|
||||
*/
|
||||
|
||||
// Buffer events to avoid enumeration issues.
|
||||
|
||||
private readonly List<StartCollideEvent> _startCollideEvents = new();
|
||||
|
||||
// Double-buffer end-collide events like box2d-v3 because we can raise these while destroying contacts
|
||||
// whereas with start events they only ever get made during the collision step.
|
||||
private readonly List<EndCollideEvent>[]
|
||||
_endCollideEvents =
|
||||
[
|
||||
new List<EndCollideEvent>(),
|
||||
new List<EndCollideEvent>()
|
||||
];
|
||||
|
||||
private int _endEventIndex = 0;
|
||||
|
||||
private int _substeps;
|
||||
|
||||
/// <summary>
|
||||
@@ -302,6 +322,8 @@ namespace Robust.Shared.Physics.Systems
|
||||
|
||||
Step(frameTime, prediction);
|
||||
|
||||
DispatchEvents();
|
||||
|
||||
var updateAfterSolve = new PhysicsUpdateAfterSolveEvent(prediction, frameTime);
|
||||
RaiseLocalEvent(ref updateAfterSolve);
|
||||
|
||||
|
||||
@@ -30,14 +30,90 @@ using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Collision.Shapes;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Physics.Events;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.UnitTesting.Server;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.Physics;
|
||||
|
||||
[TestFixture]
|
||||
public sealed class Collision_Test
|
||||
public sealed partial class Collision_Test
|
||||
{
|
||||
/// <summary>
|
||||
/// Asserts that collision events even go out.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CollisionEvents()
|
||||
{
|
||||
var sim = RobustServerSimulation.NewSimulation()
|
||||
.RegisterEntitySystems(fac =>
|
||||
{
|
||||
fac.LoadExtraSystemType<CollisionSubSystem>();
|
||||
})
|
||||
.RegisterComponents(fac =>
|
||||
{
|
||||
fac.RegisterClass<CollisionSubComponent>();
|
||||
})
|
||||
.InitializeInstance();
|
||||
|
||||
var entManager = sim.Resolve<IEntityManager>();
|
||||
|
||||
var broadphase = entManager.System<SharedBroadphaseSystem>();
|
||||
var fixtures = entManager.System<FixtureSystem>();
|
||||
var physics = entManager.System<SharedPhysicsSystem>();
|
||||
var sub = entManager.System<CollisionSubSystem>();
|
||||
|
||||
var map = sim.CreateMap();
|
||||
|
||||
var bodyAUid = entManager.SpawnAttachedTo(null, new EntityCoordinates(map.Uid, Vector2.Zero));
|
||||
var bodyBUid = entManager.SpawnAttachedTo(null, new EntityCoordinates(map.Uid, Vector2.Zero));
|
||||
var bodyA = entManager.AddComponent<PhysicsComponent>(bodyAUid);
|
||||
var bodyB = entManager.AddComponent<PhysicsComponent>(bodyBUid);
|
||||
entManager.AddComponent<CollisionSubComponent>(bodyAUid);
|
||||
physics.SetBodyType(bodyAUid, BodyType.Dynamic);
|
||||
physics.SetBodyType(bodyBUid, BodyType.Dynamic);
|
||||
|
||||
fixtures.CreateFixture(bodyAUid, "fix1", new Fixture(new PhysShapeCircle(0.5f), 1, 1, true));
|
||||
fixtures.CreateFixture(bodyBUid, "fix1", new Fixture(new PhysShapeCircle(0.5f), 1, 1, true));
|
||||
|
||||
physics.SetCanCollide(bodyAUid, true, body: bodyA);
|
||||
physics.SetCanCollide(bodyBUid, true, body: bodyB);
|
||||
physics.WakeBody(bodyAUid);
|
||||
|
||||
Assert.That(physics.IsHardCollidable(bodyAUid, bodyBUid));
|
||||
broadphase.FindNewContacts();
|
||||
|
||||
Assert.That(bodyA.ContactCount, Is.EqualTo(1));
|
||||
Assert.That(bodyB.ContactCount, Is.EqualTo(1));
|
||||
|
||||
physics.Update(0.016f);
|
||||
Assert.That(sub._counter, Is.GreaterThan(0));
|
||||
}
|
||||
|
||||
[Reflect(false)]
|
||||
private sealed class CollisionSubSystem : EntitySystem
|
||||
{
|
||||
public int _counter;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<CollisionSubComponent, StartCollideEvent>(OnStartCollide);
|
||||
}
|
||||
|
||||
private void OnStartCollide(Entity<CollisionSubComponent> ent, ref StartCollideEvent args)
|
||||
{
|
||||
_counter++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed partial class CollisionSubComponent : Component
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestHardCollidable()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user