// Creating player
Entity* entity = new Entity;
entity->add(new PositionComponent)
entity->add(new VelocityComponent)
entity->add(new CollisionComponent)
...
class CollisionComponent {
// ???
}
// Creating player
Entity* entity = new Entity;
entity->add(new PositionComponent)
entity->add(new VelocityComponent)
entity->add(new CollisionComponent)
...
class CollisionComponent {
// ???
}
This is how I would do it:
The CollisionComponent should only contain data related to the physics system (is this a rigid body, a trigger region?, friction, restitution, etc)
PhysicSystem: does all physics related math and outputs a list of collisions that occurred.
struct Collision
{
handle object_a;
handle object_b;
//.... other collision info
}
CollisionHandlerSystem (this is a high level game specific system):
Takes the list of collisions (from above) as input and does the necessary processing depending on the types of objects involved in the collisions.
This is where you would handle the logic you mentioned. This system could generate a list of messages that could be consumed by other systems, like "destroy entity X", "damage entity Y", etc (good for multi threading) or talk directly with other systems (careful with race conditions).
You could also create another component (CollisionCallbackComponent) and system (or the CollisionHandlerSystem could do it) to call entity specific collision callbacks.
This way you could efficiently handle global game logic, and still provide support of custom callbacks.
Hi,
I use a method which is similar to tiago's but is extremely simplified. I use Bullet physics as my collision detection system, artemis-cpp for the ECS architecture.
Here is the system logic
For your case, you can store additional info in the collided component, and use that to update the transforms of your entities so they no longer collide.
Ofcourse this might be a poor method to do this, but hopefully you can see how a collision detection system can cooperate with entities and systems.
When I once thought about collision in ECS I saw several problems when selecting a collision response: (1) An entity may have more than a single collision volume, (2) the response depends on the types of both participants, and (3) simultaneous collisions involving the same entity have to be handled / resolved in a meaningful manner.
For example, in an RPG with a sense system, we want to support damage of course, but perhaps distinguishing different body parts so that the collision volumes for this are more detailed than the "physical" one used to avoid running into a wall. The sense system may introduce different collision volumes for the visual, aural, and olfactory senses, and that for both roles as detector and as stimulus.
Well, the conclusion to me was that I moved away from a unified collision system and towards dedicated systems. To serve this, there is also no longer a single unified collision component but components that match the systems, of course. The collision volumes are then parameters to that components, and the components declare the owning entity to participate of the respective systems and also in which role this happens. While all such systems use the same volume intersection services, each one manages the own set of colliders and each one has its own set of rules of how to respond to a detected collision.
How could I deal with the various collision responses that are required? When a player collides with an NPC, they should be translated apart so they no longer overlap. When a projectile collides with an NPC the NPC should take damage and when it hits a wall it should just vanish.
So you want to vary the details of an implementation without modifying the interface? There are tools for that, usually virtual functions and messages/events.
Here's my take, which I've seen in several engines and works fairly well:
class PhysicsCollider : public Component {
...
// Should we bother testing
bool ShouldTestCollisions( PhysicsCollider& target );
bool ShouldNotifyOnEnter() { return notifyOnEnter; }
bool ShouldNotifyOnExit() { return notifyOnExit; }
bool ShouldNotifyOnOverlap() { return notifyOnOverlap; }
// Event handlers
virtual void OnColliderEnter( Physics& physics, PhysicsCollider& target, CollisionInfo& ci) { notifyOnEnter=false;}
virtual void OnColliderExit( Physics& physics, PhysicsCollider& target, CollisionInfo& ci) { notifyOnExit=false;}
virtual void OnContinuedCollision( Physics& physics, PhysicsCollider& target, CollisionInfo& ci) { notifyOnOverlap=false;}
...
// Physics interaction
CollisionInfo& TestCollision(...); // base common functionality
protected:
bool hasTestCollision = false;
virtual void OnTestCollision( Physics& physics, PhysicsCollider& target, CollisionInfo& ci) { hasTestCollision = false; }
...
}
... elsewhere ...
PhysicsCollider::TestCollision(...) {
...
if(hasTestCollision) {
OnTestCollision( ... );
...
Or if you prefer, implement it with messages/events.
Variants might be if you only want to notify on certain types of collisions, pass that to the ShouldXxx tests.
Since I have some time to show it, note that with this type of system where many functions may not be implemented, it is often best to write a non-virtual query to see if you should make the call. Assuming C++, the tests get completely inlined and magically reduce down to a simple boolean test. Otherwise you will need to make relatively expensive virtual function calls on a bunch of items that never implement the functions. Also it allows derived classes to toggle their implementations, perhaps you want to disable certain notifications for a time, then turn them back on again after something happened.
Then you've got all your derived classes you can make:
class SphereCollider: public PhysicsCollider {...}
class BoxCollider: public PhysicsCollider {...}
class CapsuleCollider: public PhysicsCollider {...}
class MeshCollider: public PhysicsCollider {...}
class AutoHullCollider: public PhysicsCollider {...}
or whatever else your game needs.
Well, the conclusion to me was that I moved away from a unified collision system and towards dedicated systems.
interesting. when thinking about ECS systems, it always seemed to me that different types of update (or collision response) methods would call for different types of entities or entities with different components or different processing methods for the same component type. sounds like you reached that conclusion as well.