Featured image of post Game Engine II Collision System

Game Engine II Collision System

The details of Collision System.

What the system does

This system is a collision system, it provides two different collision components, SphereCollisionComponent and BoxCollisionComponent. You can use this system to generate OnComponentHit event, OnComponentBeginOverlap event and OnComponentEndOverlap event.

Honeycam 2024-11-14 10-35-31

This system uses the BVH structure and some other methods to reduce the performance cost. It also uses linear interpolation and binary search to avoid tunneling happen when the actor moves too fast.

How to use the system

Set Up

The set up is very easy:

  1. Download the Zip file I provided and unzip
  2. Put it to your Engine folder
  3. Set the correct props for Collision system just like what you did for Graphics system
  4. Add Math system as the reference of Collision system
  5. Add Collision system as the reference of MyGame

Collision Manager

Collision manager handles collision detection from collision component set and broadcast certain events when those events happened, it also builds BVH for those components. The way it works is pretty complicated but users only need to know how to use the interfaces provided.

Collision manager uses the singleton pattern, if you want to use collision manager, Call the function CollisionManager::GetCollisionManager() first to get the variable.

1
2
3
4
5
6
void AddCollisionComponent(BaseCollisionComponent& comp);
void RemoveCollisionComponent(BaseCollisionComponent& comp);
static CollisionManager* GetCollisionManager();
void Begin();
void Update();
void Destroy();
  • You mush call AddCollisionComponent() to add the component to the component set, if it is not set to the collision set, it would not do the collision detection;

  • You mush call RemoveCollisionComponent() before you destroy the component;

  • You should call Begin() in the beginning of the game after you add all the components to the component set;

  • You should call Update() in each update and call Destroy() when you finish the game;

Collision Component

SphereCollisionComponent and BoxCollisionComponent are the child classes of BaseCollisionComponent

Set Size

SphereCollisionComponent:

1
inline void SetRadius(float i_radius) { radius = i_radius; }

BoxCollisionComponent:

1
inline void SetExtend(Math::sVector i_extend) { extend = i_extend; }

Set Initial Position

1
inline void SetPosition(Math::sVector i_position) { position = i_position; }

Notice: This function is only used to set the initial position. If you want to change the component’s position when it moves, please see 2.3.5 Movement

Collision Event

You can set the collision event of collision component to NoCollision, Overlap, or Hit.

1
2
3
4
5
6
enum class CollisionEvent 
{
    NoCollision,
    Overlap,
    Hit
};

The function you need to set the collision event is SetCollisionEvent(). By default, it would be set to Hit.

1
inline void SetCollisionEvent(CollisionEvent i_collisionEvent = CollisionEvent::Hit) { collisionEvent = i_collisionEvent; }

Rules:

Actor A Actor B Collision Event Generated
Hit Hit Hit Event
Hit Overlap Overlap Event
Overlap Hit Overlap Event
Overlap Overlap Overlap Event
NoCollision Any None
Any NoCollision None

Collision Component Type

You can set the component type to Static or Dynamic.

1
2
3
4
5
enum class CollisionComponentType
{
    Static,
    Dynamic
};

The function you need to set the component type is SetCollisionComponentType(). By default, it would be set to Dynamic.

1
inline void SetCollisionComponentType(CollisionComponentType i_collisionComponentType = CollisionComponentType::Dynamic) { collisionComponentType = i_collisionComponentType; }

Because the StaticBVH would be only built once when CollisionManager::GetCollisionManager()->Begin() is called, while the DynamicBVH would be built every time CollisionManager::GetCollisionManager()->Update() is called. So it is good for performance if the component type is set to static.

“You must set the component type to Dynamic if you want the component to move."

Movement

If you want to move the component to a new position, there are some steps you need to follow.

  1. Again, please make sure the component type has set to Dynamic

  2. Must call TryMoveTo() first.

1
2
3
4
5
6
7
void eae6320::AControlledActor::Update(const float i_elapsedSecondCount_sinceLastSimulationUpdate)
{
    if (rigidBodyState->velocity.GetLength() != 0)
    {
        SphereComp->TryMoveTo(rigidBodyState->PredictFuturePosition(i_elapsedSecondCount_sinceLastSimulationUpdate));
    }
}
  1. Notify Collision manager to do the collision detection by calling Update(). Collision manager would decide whether components can move to the new position and set them to the correct position.
1
2
//Update Collision
Collision::CollisionManager::GetCollisionManager()->Update();

Bind Event

There are four events provided by collision component

1
2
3
4
Delegate<const BaseCollisionComponent&> OnComponentHit;
Delegate<const BaseCollisionComponent&> OnComponentBeginOverlap;
Delegate<const BaseCollisionComponent&> OnComponentEndOverlap;
Delegate<const Math::sVector&> UpdatePositionAfterCollision;
  • OnComponentHit event would broadcast to the functions binded to it the info of the component it hits.
  • OnComponentBeginOverlap event would broadcast to the functions binded to it the info of the component it just overlaps.
  • OnComponentEndOverlap event would broadcast to the functions binded to it the info of the component it just finishes overlapping with.
  • UpdatePositionAfterCollision event would broadcast to the functions binded to it the position of itself after collision detection.

Example of binding event

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
eae6320::AControlledActor::AControlledActor(eae6320::Graphics::Mesh* i_mesh, eae6320::Graphics::Effect* i_effect)
{
    //Bind Event
    SphereComp->UpdatePositionAfterCollision.Add(this, &AControlledActor::UpdatePosition);
}

void eae6320::AControlledActor::UpdatePosition(const Math::sVector& safePosition)
{
    SetPosition(safePosition);
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
eae6320::AOverlapBeginTestActor::AOverlapBeginTestActor(eae6320::Graphics::Mesh* i_mesh, eae6320::Graphics::Effect* i_effect)
{
    //Bind Event
    boxComp->OnComponentBeginOverlap.Add(this, &AOverlapBeginTestActor::OnComponentBeginOverlap);
}

void eae6320::AOverlapBeginTestActor::OnComponentBeginOverlap(const Collision::BaseCollisionComponent&)
{
    //Your code here
}

What you need to notice

When doing the final project, I found that deleting the pointer variable of the collision component added to the collision manager in the middle of the game would crash the game (even if you have already called RemoveCollisionComponent() to remove it). So if you want to use this system, please do not delete the pointer variables of the collision component in the middle of the game. Instead, delete them at the end of the game, which means you should delete all those variables at your CleanUp() function. I may fix this issue if I have enough time but for now, please follow it.

How I used what I have learned this semester

  • The way to set up an engine system and make it work with other system;
  • The way to make graphics debugging, thanks for that I did not stuck on any issue for a long time;
  • Taking the advantage of logging system and use it to test whether the demo works as I expected;

Anything learned or struggled

  • How to build BVH;
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
struct BVHNode 
{
    BoundingBox bounds;
    std::unique_ptr<BVHNode> left;
    std::unique_ptr<BVHNode> right;
    std::vector<BaseCollisionComponent*> components;

    bool IsLeaf() const 
    {
        return left == nullptr && right == nullptr;
    }
};
  • The way to use custom hash rule to store pairs in hash set;
1
2
3
4
5
6
7
8
// custom hash function
struct CollisionPairHash {
    std::size_t operator()(const CollisionPair& pair) const {
        return std::hash<void*>()(pair.componentA) ^ std::hash<void*>()(pair.componentB);
    }
};

std::unordered_set<CollisionPair, CollisionPairHash> currentOverlaps;
  • How the delegate system works;

  • The optimization methods for collision system;

  • The way to handle tunneling problem;

1
2
Math::sVector Lerp(const Math::sVector& start, const Math::sVector& end, float t);
Math::sVector PerformBinarySearch(const Math::sVector& start, const Math::sVector& end, float low, float high, BaseCollisionComponent& comp);

Collision System Download

Download and have a try: CollisionSystem

Licensed under CC BY-NC-SA 4.0
comments powered by Disqus