I'm developing a base for a 2D game. My general design is something like this:
class Entity:
Every object class (like a wall, an enemy, floor etc.) derives
from this class. Most of the functions are pure virtual. There is
a hitbox as well.
class Scene:
Contains pointers to Entity-objects. When an Entity-pointer is added,
it will be given a Scene-pointer to its parent so it may access that.
Scene also has a collision-detecting function:
getIntersections<T> (Entity *)
getIntersections<T...> (Entity *)
(both return a std::vector<Entity *>)
This basically gets all Entity *s intersecting the parameter-Entity * (by checking the hitbox) and then tries to dynamic_cast<T *> them. All matching Entity *s (not the casted ones) are then returned. The variadic template is used for checking for more then one intersecting class.
My basic idea behind that was, if I had a Player-class (which represents the player obviously), and some other classes like Enemy, Wall, etc., it would be an easy task to check if a Player-object was colliding with one(or more) of these:
// (inside Player::tick(); quick and dirty)
{
if ( (this->parentScene->getIntersections<Wall>(this)).empty() )
// player does not collide with a wall, just move.
else
// player does collide with a wall, do whatever.
}
However, I have got two questions on that:
Does my (general) design show up flaws for the need of dynamic_cast<T *> as replacement for instanceof (like there is in Java for example)
Is it a performant solution on the task? Since for every collision check, Scene basically loops through every Entity * it contains, checks if it collides with the given Entity * and finally casts it to check if it derives from another given class. If that is not performant, what changes were to make in my design to make it performant?
On the performance part it would be better to separate entities in separate vectors by primitive type. Not only you can specifically test a plane vs a sphere for example it eliminates the need to have dynamic_cast entirely ( -> extra speedup ). Also since you already separated the types in vectors you can just ignore the virtual functions and go for non virtual call providing additional performance improvement;
So your scene would look something like this:
class scene
{
std::vector<PlaneEntity> m_planes;
std::vector<CircleEntity> m_circles;
};
And regarding the design of this it is much easier to select the right algorithm when intersecting primities:
This is an example on how a basic collision checking would look based on this design:
void colide(const PlaneEntity & e)
{
for each plane
call plane vs plane collision
for each circle
call plane vs circle collision;
};
void colide(const CircleEntity & e)
{
for each plane
call plane vs circle collision;
for each circle
call circle vs circle collision;
};
So to answer your question:
1: it is not a rule weather to use or not dynamic_cast, it is known that not using it is better for performance.
2: The design described above is not performant at all. Also your design with dynamic cast is even slower. To improve this you need to look into accelerations structures (this is huge topic so i can't explaing everything here) to speed up collision detection. They basically reduce the number of collision check for each primitive giving a massive improvement in performance. Basic structures are KD-Tree, Quad-Trees, Spacial-Hash , Grid, etc. You can find lots of code for each just by googleing them.
Hope this helps,
Raxvan.
Related
I have a class item. Each instance of this class is an object in 3D space, can be basic shapes like cylinder, sphere and cone. The class Item has a convenient API for geometry (radius, top radius, bot radius, length) and transformations (rotation, translation, scale).
enum ItemType {
Sphere = 1,
Cone
}
class Item
{
// ...
public:
ItemType type();
void setType(const ItemType &t);
float radius();
float length();
float topRadius();
float botRadious();
QMatrix4x4 transformations();
void setRadius(const float &r);
void setLength(const float &l);
void setTopRadius(const float &tr);
void setBotRadius(const float &br);
void setTransformations(const QMatrix4x4 &matrix);
// ...
}
Frequently, I want to glue multiple objects together to form a unified shape. For example, two spheres and a cone are connected below. The geometry and transformations of the unified object is dependent upon those of two spheres and one cone.
The problem is:
Convenient handling of the unified object is not possible
By handling, I mean, for example, transforming. Like changing the length of the unified object which requires, changing the length of the middle cone and location of the two spheres accordingly.
class Item has API for convenient handling of each individual object, but not the unified one
For handling of the unified object, I have to work with three different objects which is torturous
The question is:
Which design patterns are best suited to conveniently handle the unified objects?
Note: This question is about object oriented software design and software patterns, it has nothing to do specifically with C++. The only part that is C++ specific is the use of the virtual keyword, but even that, is just the C++-specific keyword that gives you polymorphism, which is again, an object-oriented principle, not something unique to C++.
So, what you first of all need to do, is to extract a true interface for what you call "API". I would call this Primitive3D, and it would be a class containing nothing but pure virtual methods. (In C++, that would be virtual function(parameters) = 0.)
Then, each one of your primitives would be implementing the interface by providing an implementation for each pure virtual method. If you have some basic functionality that all implementations will share, then in addition to implementing this interface you can also keep a common base class. However, the introduction of the interface will keep your options more open.
Then, introduce a new primitive, called perhaps Conglomerate. Again, that would be yet one more class implementing Primitive3D. That class would provide its own implementations for setting various attributes like length and transformation, and these implementations would work by setting some of the attributes of contained primitives.
The Conglomerate class would also provide a few functions which are specific to it and cannot be found in the Primitive3D interface. You would use these functions to configure the conglomerate, at the very least to populate the conglomerate with its contents.
The function which adds a new member primitive to the conglomerate may accept additional parameters to indicate precisely at which position of the conglomeration the new member should appear, what kind of transformation to perform when scaling the primitive, what kind of transformation to perform when translating the primitive, etc.
Internally, the conglomeration would probably make use of a vector containing instances of some internal member structure, which would contain a reference to a Primitive3D and any other information that is necessary so as to know how to handle that primitive. Do not make the mistake of adding this information to the Primitive3D itself, it does not belong there, because a primitive does not know, and should not know, that it is a member of a conglomeration. I would even go as far as to say that the location of a primitive is not a feature of the primitive itself; it is a feature of the space that contains the primitive, whether this space is the universe, or a conglomeration.
Looking at your structure, composite is the pattern you should consider. Also identifying concrete shape with 'type' attribute is against object oriented design. It kills polymorphism, a great unique tool available in OO. Composite pattern will allow you to address elements as well as their aggregation in one hiearchy.
I'm writing code for mouse picking in 3D space. So far I've made Ray and AABB classes. What I need is a function for Ray-AABB intersecting. I know how to write it and make it work, my question is which class should define said funcion? Should it be a member of Ray or AABB, both, neither? What are the best practices for object oriented approach?
For me it makes the most sense to implement that function as a member of "engine" class, more like a procedure rather than a function. However I want my code to be truly object oriented.
I would say: neither.
Ray: Structure (Members: Start, End)
AABB: Structure (Members: Position, Size).
Ray-AABB intersection method could be in a Physics or Intersection class (Depends on your actual context) as static methods (Or functions in a namespace, depends on your coding convention).
Object oriented is fine, but it doesn't mean that everything you create should be a class.
Data oriented is a very good approach (CPU friendly with less cache misses).
EDIT: A good coding rule is to think your things independently, meaning that the AABB implementation shouldn't be dependent on the Ray implementation.
I've this situation:
class Shape{};
class Triangle : public Shape{};
class Rectangle : public Shape{};
class Square : public Rectangle{};
I want to implement a virtual collideWith method in the base class that works this way:
Shape *square = new Square();
Shape *triangle = new Triangle();
bool test = square.collideWith(triangle);
Is there a way to implement this method in order to work with base class without explitit casting do derived classes?
I've thinked to create a namespace that do it:
namespace Collision {
bool isCollisionBetween(const Triangle &triangle, const Square &square) {/* calculus */}
bool isCollisionBetween(const Rectangle &rect, const Square &square) {/* calculus */}
// and so on for all combination
}
But then I don't know how I can apply this when I have something like
std::set<Shape*> shapesSet;
and I want to calculate all collision for this set.
Is it possible or it's always necessary to explicity cast Shape class to the right inherited one?
In general you would solve a problem like this as follows:
Give Shape an abstract function describing the shape, that each subclass must implement.
Each subclass implements this function based on the specific type it is.
A collideWith() function in Shape uses the abstract function of two types to determine the property.
This way when a shape is added, none of the other shapes need to be changed and you don't have to add a function to compare it with each other shape. Only the new shape has to provide functions for all required properties.
Now the difficult part is to decide what property each shape can provide. And in your case it might be difficult to provide it with an efficient implementation.
Examples of properties you can use to provide a collision detection are:
polygonRepresentation()
asVectorGrahpic()
convertToBitMap()
As you can imagine, in your problem, it might be difficult to find a proper representation suitable for each item. Then you probably have to revert to comparisons that know all the elements. But this is really an anti Object Oriented pattern, and will lead to trouble when the amount of shapes or properties increases.
ok, my comment was a little bit missleading you. What I meant was:
To create the collisionInformation make something like a "BroadPhase" (This is optional but very helpful). Consider it as a precheck if potential shapes are colliding. Google therefore "Binary Space Partition". Forget about Quad- or Octtrees at this time. This phase will create a potential contact and their information. Ask your self: which shapes are colliding? which collision resolve function am i calling? Should I use polymorphism or function pointers? (i.e calling a function pointer to a static bool CollisionResolver::handle( Box b, Circle c ); which can be retrieved/called through collisoinInformation ). What callback am I calling if there is a collision? Save all this info to this collisoinInformation object
Create your different collision handle functions to treat collisions between different shapes. Iterate your i.e std::vector saving your collisionInformation and call ( a now imaginary function) colIter->resolve() which is calling the right implementation of your collision handle functions. If you found a collision ask yourself: Which functions will I call? What information could I provide? i.e stuff additional information into your collisoinInformation or maybe create a new object collisionPair etc... You could save collision point, normal, the shapes....
Collision detection was successfully an is calling "saved" callbacks to your final gameObjects. i.e you could call GameObject::OnCollision( const ContactPair& p );
Hope this helps you a little bit more than my previously added comment
ps: A good tip for starters: Look behind the scenes of current open source implementation. i.e ODE or PhysX
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!
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.
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.
This is for a small game project with SDL on MinGW/Windows.
I am working on a physics engine, and my idea was to have a Physics::Object, which all physical objects should derive from and it registers itself with a global Physics::System class (it's a monostate pattern) so that the user doesn't need to track which objects are included in physics calculations and just needs to call a function like Physics::System::PerformTimestepCalculation(double dt).
This works fine, and I even implemented it using a single derived class Physics::Circle, which is a 2d circle. I was pretty happy with the predictive collision detection, even though I still need to optimise it.
Anyway, I ran into trouble when I started adding other primitives to include in the calculation, e.g. line. The Physics::System::PerformTimestepCalculation(double dt) became littered with calls to Object::GetID() or similar functions (may way to avoid dynamic_cast<>), but I feel dirty.
I did a bit of reading and realised that my the elements of my hierarchy are not substitutable (i.e. the collision between two circles is very different between the collision of two lines).
I like the way my Physics::Objects "self register" with the System class so they automatically get included in the calculations, and I don't really want to lose this.
There must be some other sensible design paths. How can I better redesign things so non-substitutable objects do not get in the way?
Edit FYI:
In the end I have broken away entity and shape properties, similar to how it was described in the accepted answer, and similar to an entity-component-system model. It means I still have the yuk logic of "is this a circle or a line, and is that a line or a circle?", but I'm no longer pretending that polymorphism helps me here. It also means I use some sort of factory and can have multiple calculation worlds happening at once!
The most successful publically available physics engines are not very heavy on the 'patterns' or 'object oriented design'.
Here's a rundown to back up my, admittedly bold, assertion:
Chipmunk - written in C, enough said.
Box2d - Written in C++, and there is some polymorphism here. there's a hierarchy of shapes (base class b2Shape) with a few virtual function. That abstraction leaks like a sieve, though, and you'll find lots of casts to leaf classes throughout the source code. There's also a hierarchy of 'contacts', which proves more successful, although with a single virtual function it would be trivial to rewrite this without polymorphism (chipmunk uses a function pointer, I believe). b2Body is the class used to represent rigid bodies, and it is non-virtual.
Bullet - Written in C++, used in a ton of games. Tons of features, tons of code (relative to the other two). There's actually a base class that the rigid body and soft body representations extend, but only a small part of the code can make any use of it. Most of the base class's virtual function relate to serialization (save/load of the engine state), of the two remaining virtual functions soft body fails to implement one with a TODO informing us that some hack needs to be cleaned up. Not exactly a ringing endorsement of polymorphism in physics engines.
That's a lot of words, and I haven't even really started answering your question. All I want to hammer home is that polymorphism is not something that is applied effectively in existing physics engines. And that's probably not because the authors didn't "get" OO.
So anyway, my advice: ditch polymorphism for your entity class. You're not going to end up with 100 different types that you can't possibly refactor at a later date, your physics engine's shape data will be fairly homogeneous (convex polys, boxes, spheres, etc) and your entity data will likely be even more homogeneous (probably just rigid bodies to start with).
Another mistake that I feel you're making is only supporting one Physics::System. There is utility in being able to simulate bodies independently of eachother (for instance, for a two player game), and the easiest way to do this is to support multiple Physics::Systems.
With that in mind, the cleanest 'pattern' to follow would be a factory pattern. When users want to create a rigid body, they need to tell the Physics::System (acting as a factory) to do it for them, so in your Physics::System:
// returning a smart pointer would not be unreasonable, but I'm returning a raw pointer for simplicity:
rigid_body_t* AddBody( body_params_t const& body_params );
And in the client code:
circle_params_t circle(1.f /*radius*/);
body_params_t b( 1.f /*mass*/, &circle /*shape params*/, xform /*system transform*/ );
rigid_body_t* body = physics_system.AddBody( b );
Anyhoo, kind of a rant. Hope this is helpful. At the very least I want to point you towards box2d. It's written in a pretty simple dialect of C++ and the patterns applied therein will be relevant to your engine, whether it's 3D or 2D.
The problem of hierarchies is that they don't always make sense, and trying to cram everything into a hierarchy just results in awkward decisions and frustrating work down the line.
The other solution that can be used is the tagged union one, best embodied by boost::variant.
The idea is to create an object that can hold one instance of a given type (among a preselected list) at any given time:
typedef boost::variant<Ellipsis, Polygon, Blob> Shape;
And then you can provide the functionality by switching over the type list:
struct AreaComputer: boost::static_visitor<double> {
template <typename T>
double operator()(T const& e) { return area(a); }
};
void area(Shape const& s) {
AreaComputer ac;
return boost::apply_visitor(s, ac);
}
The performance is the same as a virtual dispatch (so not too much, normally), but you get greater flexibility:
void func(boost::variant<Ellipsis, Blob> const& eb);
void bar(boost::variant<Ellipsis, Polygon> const& ep);
// ...
You can provide functions only when relevant.
And on the subject of binary visitation:
struct CollisionComputer: boost::static_visitor<CollisionResult> {
CollisionResult operator()(Circle const& left, Circle const& right);
CollisionResult operator()(Line const& left, Line const& right);
CollisionResult operator()(Circle const& left, Line const& right);
CollisionResult operator()(Line const& left, Circle const& right);
};
CollisionResult collide(Shape const& left, Shape const& right) {
return boost::apply_visitor(CollisionComputer(), left, right);
}