mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-01-02 19:38:24 -05:00
This commit imports the C# engine code and game files, excluding C++ source code. [Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
257 lines
6.3 KiB
C#
257 lines
6.3 KiB
C#
using static Sandbox.Component;
|
|
|
|
namespace Sandbox;
|
|
|
|
/// <summary>
|
|
/// Used to abstract the listening of collision events
|
|
/// </summary>
|
|
class CollisionEventSystem : IDisposable
|
|
{
|
|
private readonly PhysicsBody _body;
|
|
private readonly GameObject _gameObject;
|
|
|
|
public Action<Collision> OnCollisionStart;
|
|
public Action<CollisionStop> OnCollisionStop;
|
|
public Action<Collision> OnCollisionUpdate;
|
|
|
|
public IReadOnlyCollection<Collider> Touching => _touchingColliders is null ? Array.Empty<Collider>() : _touchingColliders;
|
|
|
|
private record struct TouchingPair( Collider Self, Collider Other, GameObject GameObject );
|
|
private HashSet<TouchingPair> _touchingPairs;
|
|
private HashSet<Collider> _touchingColliders;
|
|
private HashSet<GameObject> _touchingObjects;
|
|
|
|
private HashSet<TouchingPair> TouchingPairs => _touchingPairs ??= new();
|
|
private HashSet<Collider> TouchingColliders => _touchingColliders ??= new();
|
|
private HashSet<GameObject> TouchingObjects => _touchingObjects ??= new();
|
|
|
|
public CollisionEventSystem( PhysicsBody body, GameObject go = null )
|
|
{
|
|
_body = body;
|
|
_gameObject = go ?? body.GameObject;
|
|
|
|
_body.OnIntersectionStart += OnIntersectionStart;
|
|
_body.OnIntersectionEnd += OnIntersectionEnd;
|
|
_body.OnIntersectionUpdate += OnIntersectionUpdate;
|
|
_body.OnTriggerBegin += OnTriggerBegin;
|
|
_body.OnTriggerEnd += OnTriggerEnd;
|
|
}
|
|
|
|
private void OnTriggerBegin( PhysicsIntersection c )
|
|
{
|
|
var self = new CollisionSource( c.Self );
|
|
var other = new CollisionSource( c.Other );
|
|
var gameObject = other.Body.GameObject;
|
|
|
|
OnObjectTriggerStart( self.Collider, gameObject );
|
|
OnColliderTriggerStart( self.Collider, other.Collider, gameObject );
|
|
}
|
|
|
|
private void OnTriggerEnd( PhysicsIntersectionEnd c )
|
|
{
|
|
var self = new CollisionSource( c.Self );
|
|
var other = new CollisionSource( c.Other );
|
|
var gameObject = other.Body.GameObject;
|
|
|
|
OnColliderTriggerStop( self.Collider, other.Collider, gameObject );
|
|
|
|
if ( IsTouching( other.Component ) )
|
|
return;
|
|
|
|
OnObjectTriggerStop( self.Collider, gameObject );
|
|
}
|
|
|
|
private void OnIntersectionStart( PhysicsIntersection c )
|
|
{
|
|
var self = new CollisionSource( c.Self );
|
|
var other = new CollisionSource( c.Other );
|
|
var o = new Collision( self, other, c.Contact );
|
|
|
|
OnCollisionStart?.Invoke( o );
|
|
|
|
_gameObject.Components.ExecuteEnabledInSelfAndDescendants<ICollisionListener>( x => x.OnCollisionStart( o ) );
|
|
}
|
|
|
|
private bool IsTouching( Component other )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
private void OnIntersectionEnd( PhysicsIntersectionEnd c )
|
|
{
|
|
var self = new CollisionSource( c.Self );
|
|
var other = new CollisionSource( c.Other );
|
|
var o = new CollisionStop( self, other );
|
|
|
|
OnCollisionStop?.Invoke( o );
|
|
|
|
_gameObject.Components.ExecuteEnabledInSelfAndDescendants<ICollisionListener>( x => x.OnCollisionStop( o ) );
|
|
}
|
|
|
|
private void OnIntersectionUpdate( PhysicsIntersection c )
|
|
{
|
|
var self = new CollisionSource( c.Self );
|
|
var other = new CollisionSource( c.Other );
|
|
var o = new Collision( self, other, c.Contact );
|
|
|
|
OnCollisionUpdate?.Invoke( o );
|
|
|
|
_gameObject.Components.ExecuteEnabledInSelfAndDescendants<ICollisionListener>( x => x.OnCollisionUpdate( o ) );
|
|
}
|
|
|
|
private void OnColliderTriggerStart( Collider self, Collider other, GameObject go )
|
|
{
|
|
if ( self is null )
|
|
return;
|
|
|
|
if ( other is null )
|
|
return;
|
|
|
|
if ( !TouchingPairs.Any( x => x.Other == other ) )
|
|
{
|
|
TouchingColliders.Add( other );
|
|
|
|
other.OnComponentDisabled += RemoveDeactivated;
|
|
}
|
|
|
|
if ( !TouchingPairs.Add( new TouchingPair( self, other, go ) ) )
|
|
return;
|
|
|
|
if ( !self.IsValid() )
|
|
return;
|
|
|
|
if ( !other.IsValid() )
|
|
return;
|
|
|
|
if ( !self.IsTrigger )
|
|
return;
|
|
|
|
self.OnTriggerEnter?.InvokeWithWarning( other );
|
|
|
|
_gameObject.Components.ExecuteEnabledInSelfAndDescendants<ITriggerListener>( x => x.OnTriggerEnter( self, other ) );
|
|
}
|
|
|
|
private void OnColliderTriggerStop( Collider self, Collider other, GameObject go )
|
|
{
|
|
if ( self is null )
|
|
return;
|
|
|
|
if ( other is null )
|
|
return;
|
|
|
|
foreach ( var shape in self.Shapes )
|
|
{
|
|
if ( other.Shapes.Any( x => shape.IsTouching( x, true ) ) )
|
|
return;
|
|
}
|
|
|
|
if ( !TouchingPairs.Remove( new TouchingPair( self, other, go ) ) )
|
|
return;
|
|
|
|
if ( !TouchingPairs.Any( x => x.Other == other ) )
|
|
{
|
|
TouchingColliders.Remove( other );
|
|
|
|
other.OnComponentDisabled -= RemoveDeactivated;
|
|
}
|
|
|
|
if ( !self.IsValid() )
|
|
return;
|
|
|
|
if ( !other.IsValid() )
|
|
return;
|
|
|
|
if ( !self.IsTrigger )
|
|
return;
|
|
|
|
self.OnTriggerExit?.InvokeWithWarning( other );
|
|
|
|
_gameObject.Components.ExecuteEnabledInSelfAndDescendants<ITriggerListener>( x => x.OnTriggerExit( self, other ) );
|
|
}
|
|
|
|
private void OnObjectTriggerStart( Collider self, GameObject other )
|
|
{
|
|
if ( self is null )
|
|
return;
|
|
|
|
if ( other is null )
|
|
return;
|
|
|
|
if ( !TouchingObjects.Add( other ) )
|
|
return;
|
|
|
|
if ( !self.IsValid() )
|
|
return;
|
|
|
|
if ( !other.IsValid() )
|
|
return;
|
|
|
|
if ( !self.IsTrigger )
|
|
return;
|
|
|
|
self.OnObjectTriggerEnter?.InvokeWithWarning( other );
|
|
|
|
_gameObject.Components.ExecuteEnabledInSelfAndDescendants<ITriggerListener>( x => x.OnTriggerEnter( self, other ) );
|
|
}
|
|
|
|
private void OnObjectTriggerStop( Collider self, GameObject other )
|
|
{
|
|
if ( self is null )
|
|
return;
|
|
|
|
if ( other is null )
|
|
return;
|
|
|
|
if ( TouchingPairs.Any( x => x.GameObject == other ) )
|
|
return;
|
|
|
|
if ( !TouchingObjects.Remove( other ) )
|
|
return;
|
|
|
|
if ( !self.IsValid() )
|
|
return;
|
|
|
|
if ( !other.IsValid() )
|
|
return;
|
|
|
|
if ( !self.IsTrigger )
|
|
return;
|
|
|
|
self.OnObjectTriggerExit?.InvokeWithWarning( other );
|
|
|
|
_gameObject.Components.ExecuteEnabledInSelfAndDescendants<ITriggerListener>( x => x.OnTriggerExit( self, other ) );
|
|
}
|
|
|
|
private void RemoveDeactivated()
|
|
{
|
|
Action actions = default;
|
|
|
|
foreach ( var pair in TouchingPairs )
|
|
{
|
|
if ( pair.Other.Active )
|
|
continue;
|
|
|
|
actions += () => OnColliderTriggerStop( pair.Self, pair.Other, pair.GameObject );
|
|
actions += () => OnObjectTriggerStop( pair.Self, pair.GameObject );
|
|
}
|
|
|
|
actions?.InvokeWithWarning();
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_touchingPairs?.Clear();
|
|
_touchingColliders?.Clear();
|
|
_touchingObjects?.Clear();
|
|
|
|
if ( !_body.IsValid() )
|
|
return;
|
|
|
|
_body.OnIntersectionStart -= OnIntersectionStart;
|
|
_body.OnIntersectionEnd -= OnIntersectionEnd;
|
|
_body.OnIntersectionUpdate -= OnIntersectionUpdate;
|
|
_body.OnTriggerBegin -= OnTriggerBegin;
|
|
_body.OnTriggerEnd -= OnTriggerEnd;
|
|
}
|
|
}
|