using System;
using System.Collections.Generic;
using System.Linq;
using Chernobyl.Collections.Generic;
using Chernobyl.ComponentModel;
using Chernobyl.Creation;
using Chernobyl.Event;
using Chernobyl.Readiness;
using Chernobyl.Switch;
using Chernobyl.Update;
using Chernobyl.Values;
namespace Chernobyl.Mathematics.Collision
{
///
/// A type that actively checks for collisions between the
/// and the instances contained
/// within .
///
/// The type of the
/// instances that are to be checked against for
/// collision with . This ICollidable type must derive
/// from .
/// The type that will
/// be checked for collisions against the
/// instances contained within
/// .
public class CollisionChecker : Updateable
where TContainedCollidable : IExtendedCollidable
{
///
/// Initializes a new instance of the
/// class.
///
/// The objects that are to be checked for
/// collision against .
/// The object that is to be checked for collision
/// against the objects in .
public CollisionChecker(ICollidableEnumerable collidables,
TCollider collider)
{
_previousCollidables = new LinkedList();
_currentCollidables = new LinkedList();
_startedColliding = new LinkedList();
Collider = collider;
Collidables = collidables;
}
///
/// An event that is raised when the has began
/// colliding with one or more
/// instances in . Any
/// that is added to this event
/// will get invoked immediately if a collision is already occurring.
///
public event EventHandler CollisionStarted
{
add
{
_collisionStarted += value;
if (_startedColliding.Any() == true)
value(this, new CollisionEventArgs(_startedColliding, Collider));
}
remove { _collisionStarted -= value; }
}
///
/// An event that is raised when the has stopped
/// colliding with one or more
/// instances in that were previously being
/// collided with.
///
public event EventHandler CollisionEnded;
///
/// The objects that are to be checked for collision against
/// .
///
public ICollidableEnumerable Collidables
{
get { return _collidables; }
private set
{
if(value == null)
throw new ArgumentNullException("value", "You must provide " +
"a non-null collection of collidables.");
_collidables = value;
}
}
///
/// The object that is to be checked for collision against the objects
/// in .
///
public TCollider Collider { get; private set; }
///
/// Figures out which items are currently colliding and which are no
/// longer colliding. The events and
/// as needed from this method.
///
/// The amount of time that has
/// passed since this update was last called.
public override void Update(TimeSpan deltaTime)
{
// get our collision point and update our collidable with this new
// point
IEnumerable collidables = Collidables.GetCollidables(Collider);
// Inform both sides of the collision, collect the items that have
// just started colliding with each other, and removed
_startedColliding.Clear();
foreach (TContainedCollidable coll in collidables)
{
if (_previousCollidables.Remove(coll) == false)
{
// Note that we could perform the ICollidable.HandleCollisionStarted(ICollidable)
// but that would create problems if a user removed an instance
// from Collidables while we are iterating through it. So we
// invoke those method later in this method.
_startedColliding.AddLast(coll);
}
// Save this collidable for the next update.
_currentCollidables.AddLast(coll);
}
// Let client code know of the newly hit items.
if (_startedColliding.Any() == true)
{
foreach (TContainedCollidable collidable in _startedColliding)
{
// we have never hit this collidable; call the start of the
// collision handling and add it to our previous collidables
// list
Collidables.HandleCollisionStarted(collidable);
collidable.HandleCollisionStarted(Collidables);
}
if(_collisionStarted != null)
_collisionStarted(this, new CollisionEventArgs(_startedColliding, Collider));
}
// Any collidables left were not hit this time around so we handle
// the collision ending and remove them from our previous collidables
// list
if (_previousCollidables.Any() == true)
{
if(CollisionEnded != null)
CollisionEnded(this, new CollisionEventArgs(_previousCollidables, Collider));
foreach (ICollidable2D coll in _previousCollidables)
{
Collidables.HandleCollisionEnded(coll);
coll.HandleCollisionEnded(Collidables);
}
}
// now update our previous collidable list with our current collidables
_previousCollidables.AddRange(_currentCollidables);
_currentCollidables.Clear();
// The base class will update the children.
base.Update(deltaTime);
}
///
/// An that contains data regarding collision.
///
public class CollisionEventArgs : EventArgs
{
///
/// Initializes a new instance of the
///
/// class.
///
/// The instances that were involved in the
/// collision.
/// The instance that collided with the
/// .
public CollisionEventArgs(IEnumerable collidables,
TCollider collider)
{
Collider = collider;
Collidables = collidables;
}
///
/// The instances that were involved in the collision.
///
public IEnumerable Collidables { get; private set; }
///
/// The instance that collided with the .
///
public TCollider Collider { get; private set; }
}
///
/// instances that were hit
/// previously and may need to have their
/// method
/// invoked when they have stopped colliding.
///
LinkedList _previousCollidables;
///
/// The list that is used to hold the
/// currently being collided with in the
/// method.
///
LinkedList _currentCollidables;
///
/// The list of instances
/// from that have just started colliding with
/// .
///
LinkedList _startedColliding;
///
/// The backing field to .
///
ICollidableEnumerable _collidables;
///
/// The backing field to .
///
EventHandler _collisionStarted;
}
}