How to design my collision code properly?

Started by
4 comments, last by Heelp 7 years, 7 months ago

Guys, I have problems figuring out the best way to structure my collision algorithms.

Until now I had only two:

1. Collision between a ray and an AABB.

2. Collision between a point and an AABB.

Now that I'm starting to need more different collisions, my code is getting pretty awful.

The first reason for this is the following:

I have two base classes. The class Object is the base class for all static objects.( map, bullets, houses, etc ) and the class Character is the base class for all animated objects.( player, enemies, etc. ). Class Player and class Enemy inherit from the base class Character.

The problem now is that when I want to check a collision between an Object and an Enemy, I need to declare a function something like this:

check_collision( Object& obj, Enemy& enemy ).

And when I need to check collision between an object and a player, I need the same function, but with different parameters:

check_collision( Object& obj, Player& player )

And if I create another class that inherits from Character. I need to make a third check_collision() function. Is it ok to overload the function like that, or is it better to somehow use polymorphism?

And the second problem is that I have different kinds of collisions and I don't know how to structure my code.

1st way is to make the function based on states like that:

check_collision( RAY_AABB, Object& obj, Player& player )

check_collision( SPHERE_AABB, Object& obj, Player& player )

or is it better to just make a different name for the function like this, CheckCollisionRayAABB, and CheckCollisionSphereAABB?

The question is kind of stupid, but I find it really hard to design and name the functions properly, maybe because English is not my mother language, I don't know.

Anyway, thanks for reading, I hope you have some suggestions.

Advertisement

The problem now is that when I want to check a collision between an Object and an Enemy, I need to declare a function something like this: check_collision( Object& obj, Enemy& enemy ). And when I need to check collision between an object and a player, I need the same function, but with different parameters: check_collision( Object& obj, Player& player ) And if I create another class that inherits from Character. I need to make a third check_collision() function. Is it ok to overload the function like that, or is it better to somehow use polymorphism?

One way you can deal with this, is to use Composition instead of Inheritance.

If you abstract the type of the objects (Player, Enemy, etc), then the collision code doesn't have to know the object type, only the collision polygon/surface.

And the second problem is that I have different kinds of collisions and I don't know how to structure my code.

1st way is to make the function based on states like that:

check_collision( RAY_AABB, Object& obj, Player& player )

check_collision( SPHERE_AABB, Object& obj, Player& player )

or is it better to just make a different name for the function like this, CheckCollisionRayAABB, and CheckCollisionSphereAABB?

For the collision checks, in my opinion, it is fine, and simpler, to overload the same function.

But again, if you abstract the collision surface from the game Object (Player, Enemy, etc), then your collision functions only need to know the type of collision surface, which would, in turn, simplify them even more.

So, instead of


check_collision( RAY_AABB, Object& obj, Player& player )
check_collision( SPHERE_AABB, Object& obj, Player& player )
check_collision( SPHERE_RAY, Object& obj, Player& player )

you'd have


check_collision( Coll_AABB &aabb, Coll_Sphere &sphere )
check_collision( Coll_AABB &aabb, Coll_Ray &ray)
check_collision( Coll_Sphere &sphere , Coll_Ray &ray ) 

The object types are stripped away, and only the collision models/surfaces remain.

It's just an idea, though. If implementing it will cost you more (in programming time) than what it will bring, just update what you have.

Hope it helps.

__SKYe got it exactly, and this is an excellent example of why to prefer composition over inheritance.

All I'd add is that you'll probably want to also write some inline functions for reverse order tests:

ret_type check_collision(A& a, B& b) { /* code */ }

inline ret_type check_collision(B& b, A& a) { return check_collision(a, b); }

And also look for other places where you can delegate. For example, sphere v sphere can be delegated to sphere v point.

The composition approach also makes it easy to create composites, where you have a collection of collision primitives wrapped in an object that allows them behave as a single collider.

void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.

No chance I could have solved this by myself. Good thing I decided to post here. Thanks for the clear answers. :)

Guys, I don't understand how to make the classes to use composition, what to do?

My character class just has a few pointers to models, and the models have the bounding boxes in them ( in the Model class)



#ifndef CHARACTER_H
#define CHARACTER_H

#include "camera.h"
#include "model.h"
#include "timer.h"

#include <glm/gtc/matrix_transform.hpp>
#include <glm/glm.hpp>
#include <glm/gtx/transform.hpp>
#include <glm/gtc/quaternion.hpp>
#include <glm/gtx/quaternion.hpp>


//Base class
class Character
{

protected:
    Model *idleAnimation;
    Model *runAnimation;
    Model *punchAnimation;
    Model *knockdownAnimation;
    Model *deathAnimation;
    Model *currentAnimation;
    Model *bulletModel;

    Shader *shaderanim;
    Shader *shadershadow;

    Timer animTimer;

    glm::vec3 currentPosition{ 0.0f };
    glm::vec3 previousPosition{ 0.0f };
    glm::vec4 rotation{ 0.0f };
    glm::vec3 scale{ 1.0f };
    glm::mat4 modelMatrix{ 1.0f };
    glm::mat4 AABBmodelMatrix{ 1.0f };
    glm::mat4 mvpMatrix{ 1.0f };
    float deltaTime;
    int currentAnimState = ANIM_RUN;
    GLuint boneLocation[ MAX_BONES ];


public:
    Character( vector<Shader*> shaders );
    ~Character();

    Camera mainCam;
    Camera moveHelperCam;

    void setScale( glm::vec3 sc );
    void setRotation( glm::vec4 rot );
    void setPosition( glm::vec3 pos );
    void setModelMatrix( glm::mat4 modelmat );
    void setDeltaTime( float delta );
    void setCurrentAnim( int anim );

    glm::vec3 getScale();
    glm::vec4 getRotation();
    glm::vec3 getPosition();
    glm::mat4 getModelMatrix();
    int getCurrentAnim();
    Model* getCurrentModel();

    glm::vec3 getInterpolatedPosition( glm::vec3 previousPos, glm::vec3 currentPos, float interpRatio );
    glm::vec3 getInterpolatedRotation( glm::quat previousRot, glm::quat currentRot, float interpRatio );

    void storeBoneLocations( Shader* shader );
    void setBoneTransform( GLuint index, const Matrix4f& Transform );
    void renderModel( Camera& playerCam, Shader& shader, Shader& shaderAABB, float extrapolRatio );

};

#endif


then your collision functions only need to know the type of collision surface, which would, in turn, simplify them even more.

?? :huh:

I fixed it, extremely easy solution. Instead of passing class Player and class Enemy as an argument, I just pass their models. And in the model Class I make a struct called BoundingObject which decides how collision is done, and now my collision is done like this: checkCollision( Model& model, Model& model2 ) I don't know if this is called composition, but it works perfectly fine. I thought the term "composition" means something very complicated but it's just an object inside another object.

This topic is closed to new replies.

Advertisement