Reputation: 17
I'm trying to make a 2D platform based game (in SFML) for my university work. I'm not asking for anyone to write some code for me but if anyone could offer some pointers I'd be extremely grateful :)
At present I have around 13 classes, including:
BaseEntity (Most game objects derive from this)
Player (Inherits from BE)
Beetle (Inherits from BE - the game is called 'Beetle Dodger' so there will be moving beetles as a threat to the player)
Gem
MagicGem (players needs these to advance through the levels)
Platform
SolidBlock (inherits from Platform)
DownBlock (inherits from Platform - player can fall through but not jump up through this block)
UpBlock (as above but vice versa)
GameSound
Game (The game manager)
I've built most of the games 'building blocks' so to speak - every class has its own update function which is called in Game::Update. The same applies to each object's Draw() function. Every class holds a pointer to the game window so it can achieve these things and they're also passed a number of variables from Game, such as what key is currently being pressed and also the elapsed time (for movement calcs).
All seemed fine and dandy up to here - then I met collisions. Whilst I understand the basis of how they work, I've tried two or three different approaches to implementing them. At first I began with just having the Player class hold a bunch of functions such as CollidesWith( Beetle& b ) and CollidesWith( Platform& plat ). Obviously this is extremely strenuous when testing against every object in my level (including gems of course) and I began to consider how to implement broad phase collision detection. I then tried using an AABB class defined by 2 2DVectors (SFML's built in class). And this is where I got slightly stuck and decided to come and ask for help here. I went back to testing collisions using purely the size of the sprites (as they are defined by a box - the same as AABB's, right?) but I'm not sure this is/was a wise path to take.
Before I make a serious mess up of what're the good foundations of a game, can anyone offer some friendly advice on a good way to implement broad phase and narrow phase collision detection? I had narrow working quite well at one stage and then I realised that a player could still move through the side of a platform, heh.
Should I create a dedicated class for collisions? Or should I continue as I was, using the size of the sprites of each object (each object has it's own sprite and image - in fact I'll show an example):
class BaseEntity
{
public:
// Constructors
BaseEntity();
BaseEntity(sf::RenderWindow* gameWin, string imgPath, sf::Vector2f position = sf::Vector2f(0,0), sf::Vector2f velocity = sf::Vector2f(0,0));
virtual ~BaseEntity();
// Setters
void SetCurrentPos(sf::Vector2f newPos); // Set the current position
void SetPreviousPos(sf::Vector2f newPrevPos); // Shouldn't be needed but there may be rare circumstances
void SetCurrentVel(sf::Vector2f newVel); // Set the velocity
// Getters
sf::Vector2f GetCurrentPos(); // Returns the current position values
sf::Vector2f GetPreviousPos(); // Returns the previous position values
sf::Vector2f GetCurrentVel(); // Returns the current velocity values
void virtual SetSprite(string imgPath); // Set up the images for the sprite
void virtual Update(float elapsedTime); // The function that handles the updating of movement
void virtual Draw(); // The function that handles the 'Draw' aspect of this object
protected:
sf::RenderWindow* p_GameWin; // A pointer to the game window (used for drawing)
sf::Vector2f currentPos;
sf::Vector2f previousPos;
sf::Vector2f currentVel;
sf::Sprite mySprite; // This objects sprite
sf::Image myImage; // This objects image
};
The player class inherits from this and has a few extra functions such as CheckBoundaries, CollidesWith, Jump and also holds a few variables - bool isColliding may be of interest in this scenario.
Cheers guys, and sorry for the essay!
Upvotes: 0
Views: 3528
Reputation: 436
As you have found out solving the collision cannot be considered just at the level of a single game object. You need an object that can keep track of all the objects that participate in collision, and can read from them all the properties that affect collision. If this is done then it can solve collision globally for all the objects once every game update tick.
What I recommend is creating an interface through which you can get all the information required to process the collision detection. All objects that participate in collision will need to inherit from this. The interface will allow you to smoothly transition from the per object case and the global case.
This is just an example to help you understand. You need to adapt it to your own code.
class ICollidable
{
public:
// we use these function to retrieve collision relevant information
// (can be optimised)
virtual sf::Vector2f GetPosition() = 0; // objects have position
virtual sf::Vector2f GetSize() = 0; // objects have a size
// using this function, we notify the object that it collided with something else
virtual void ProcessCollision(ICollidable* other) = 0;
// if you use virtual methods, you need a virtual destructor
virtual ~ICollidable{};
};
Now you can create a collision system. A collision system would hold a list of ICollidable
objects that can interact with each other. You can even choose some objects to not participate in collisions at all. This will be responsible to solving the collision at the global level.
class CollisionSystem
{
private:
// hold all objects that participate in collision
std::vector<ICollidable*> objectList;
public:
void AddToCollisionList(ICollidable* obj);
void RemoveFromCollisionList(ICollidable* obj);
void ProcessCollisionList();
}
The CollisionSystem::ProcessCollisionList();
contains the implementation of the collision checking algorithm. It will get the position and size of each object. Base on that information it will decide that two objects collide and call ICollidable::ProcessCollision(ICollidable& other);
for each object that collides. This method will be overriden by subclasses to provide class specific functionality.
Inside CollisionSystem
, you can use data structures like quad trees or binary trees to speed up the time it takes to solve all collisions. As a first step I recommend just sorting on X axis. By keeping the list sorted you only have to check the neighbors that are not further away than the size of you object.
If the requirements for your game change, you can update with better collision checking algorithm, you can add more attributes to ICollidable
. In general you also need to process physics, you could also provide the functionality for that through ICollidable
.
And as a tip, if two objects collide, I recommend immediately moving them away from each other so that they don't collide in the next game tick.
Upvotes: 1
Reputation: 118
Depending on the amount of entities you are dealing with, just checking collision for every object in the game might have a huge cost, in terms of memory and/or in terms of performance.
You might want to pre-treat your objects to classify them by an axis, like increasing x coordinate, to simplify the collision checking. It could be even better to prepare all your objects and sort them before the game even starts, as an initiation to a level for example. I think that would be the way i'd choose to do it, for a first try.
Upvotes: 0