I am making a collision system in C++ with SFML. I have a base object class with the following methods :
class Object : public sf::CircleShape {
protected:
bool collision;
void onCollision();
void afterCollision();
public:
bool checkCollision(Object& object);
bool reactCollision(bool _collision);
};
bool Object::checkCollision(Object& object) {
//Calcul distance with object and radius
bool _collision = (distance <= radius)? true : false;
this->reactCollision(_collision)
return _collision;
}
bool Object::reactCollision(bool _collision) {
//If collision state change compare to previous
if(_collision != collision) {
if(_collision) {
this->onCollision();
}
else {
this->afterCollision();
}
collision = _collision;
}
return _collision;
}
void Object::onCollision() {
this->setFillColor(sf::Color::Red);
}
void Object::afterCollision() {
this->setFillColor(sf::Color::White);
}
And then a player class who implement object and overide two methods:
class Player : public Object {
protected:
void onCollision();
void afterCollision();
};
void Player::onCollision() {
this->setFillColor(sf::Color::Magenta);
}
void Player::afterCollision() {
this->setFillColor(sf::Color::Green);
}
The problem : player use the parent base methods instead of his owns specialized methods. If I implement reactCollision() and checkCollision() in the player class, it's working but that not the goal.
I would like to modify onCollision() in the children's classes of object without implementing the same code from reactCollision() and checkCollision() in the children classes.
Related
There is an abstract class Entity, and other classes like Player and Enemy are inherit from it. When game detects a collision between the entities, the following method is called:
void handleCollision(Entity* ent1, Entity* ent2) {
if (dynamic_cast<Player*>(ent1) || dynamic_cast<Player*>(ent2) &&
dynamic_cast<Enemy*>(ent1) || dynamic_cast<Enemy*>(ent2)) {
//player <-> enemy collision
}
else if (dynamic_cast<Player*>(ent1) || dynamic_cast<Player*>(ent2) &&
dynamic_cast<Projectile*>(ent1) || dynamic_cast<Projectile*>(ent2)) {
//player <-> projectile collision
}
else if () {
//...
}
else if() {
//...
}
}
Each entity has unique behavior when colliding with another, which depends on the type of entity (Player, Enemy, etc), that's why I need to check every possible combination between entities as shown above. But I don't like the fact it creates a huge else if chain, where each entity is checked multiple times. Is there another way of doing it?
Trying to expand Ben Voigt's comment about multiple virtual dispatch, something along the lines of:
void handleCollision(Entity* ent1, Entity* ent2)
{
ent1->collide_with(ent2);
}
Where:
class Entity
{
public:
virtual void collide_with(Entity*) = 0; // Dispatcher
virtual void handle_collision_with(Entity*) {}
virtual void handle_collision_with(class Player*) {}
virtual void handle_collision_with(class Enemy*) {}
virtual void handle_collision_with(class Projectile*) {}
};
class Player : public Entity
{
public:
virtual void collide_with(Entity* other) override
{
other->handle_collision_with(this);
}
virtual void handle_collision_with(Entity* other) override
{
// Unhandled entity
}
virtual void handle_collision_with(Player* other) override
{
// Handle collision player-player
}
virtual void handle_collision_with(Projectile* projectile) override
{
// Handle collision player-projectile
}
};
class Enemy : public Entity
{
public:
virtual void collide_with(Entity* other) override
{
other->handle_collision_with(this);
}
virtual void handle_collision_with(Enemy* other) override
{
// Handle collision enemy-enemy
}
virtual void handle_collision_with(Player* player) override
{
// Handle collision enemy-player
}
virtual void handle_collision_with(Projectile* projectile) override
{
// Handle collision enemy-projectile
}
};
class Projectile : public Entity
{...}
source: a-polyglots-guide-to-multiple-dispatch
Use a virtual function defined in Entity class to uniquely identify the derived class whether Player or Enemy. This will be a good practice to avoid any runtime errors as well.
enum EntityType { Entity, Player, Enemy}
In the Entity class define a virtual function like this,
virtual EntityType getType (return Entity;)
and override the function in two classes accordingly.
class Entity {
public:
virtual void applyCollisionBehaviorTo(Entity &entity) { }
virtual void onCollision(Entity &entity) { }
};
class Ball : public Entity {
public:
void applyCollisionBehaviorTo(Entity entity) override {
}
void onCollision(Entity entity) override {
entity.applyCollisionBehaviorTo(this); // error: no matching function for call to 'Entity::applyCollisionBehaviorTo(Ball*)'
}
};
void setup() {
// put your setup code here, to run once:
}
void loop() {
// put your main code here, to run repeatedly:
}
I come from a C# background so I'm getting my head around C++ inheritance and polymorphism.
Your class Entity should be like this:
class Entity
{
public:
virtual void applyCollisionBehaviorTo(Entity &entity) = 0;
virtual void onCollision(Entity &collidingEntity) = 0;
};
You can't refere to object of Ball class inside Entity, in fact entities don't even know about the existence of balls.
OTOH balls "know" that they are entities
I'm trying to create a simple events system, which will have many different events. So, I've tried to create an event class which allows you to register functions, taking the correct type of event, and returning a boolean.
What I want is that the method post in any subclass of Event will take that subclass rather than Event, and the functions in the list listeners in each subclass should take the correct subclass type. Here's the code I already have, which forces the function to cast to the correct event type:
events.h:
namespace events {
class Event {
public:
static const std::List<bool (*)(Event)> listeners;
void post(Event event);
}
class ExampleEvent : Event {
int eventData;
}
}
events.cpp:
namespace events {
void Event::post(Event event) {
for(int i = 0; i < listeners.size(); i++) {
if(listeners[i](event)) return;
}
}
}
Is there some way I can get this to work with subclassed events without having to do the following?
bool handleExample(Event event) {
ExampleEvent exampleEvent = (ExampleEvent)event;
std::cout << exampleEvent.eventData << std::endl;
return false;
}
// Somewhere else in the code
ExampleEvent::listeners.push_back(&handleExample);
I apologise for any incorrect code, I don't quite have the rules of the language perfect yet.
The common way is to use CRTP:
namespace events {
template<typename Derived>
class Event {
public:
static const std::list<bool (*)(Derived)> listeners;
void post(Derived event)
{
static_cast<Derived&>(*this).post(event);
}
};
class ExampleEvent : Event<ExampleEvent> {
int eventData;
void post(ExampleEvent event)
{
//implement post
}
};
}
Just use virtual functions:
namespace events {
class EventHandler {
public:
static const std::list<Event*> listeners;
void post() {
for (Event * listener : listeners) {
if (listener->post()) break;
}
}
};
class BaseEvent {
public:
virtual bool post() = 0;
virtual ~BaseEvent() {}
};
class ExampleEvent : public BaseEvent { // use public inheritance
int eventData;
public:
virtual bool post() override {
if (eventData == 0) return true;
return false;
}
};
}
I'm not happy with the question title, but I couldn't describe it well. I'm putting implementation in the class declarations for sake of brevity.
I have a class like this:
class VisibleObject {
public:
void draw(sf::RenderWindow& rw) {
rw.draw(*shape.get());
}
virtual void setSize(sf::Vector2f) = 0;
protected:
std::shared_ptr<sf::Shape> shape;
}
sf::Shape is an abstract class. Then I have a derived class like so:
class Brick : VisibleObject {
Brick() {
shape.reset(new sf::RectangleShape());
}
void setSize(sf::Vector2f newSize) {
std::dynamic_pointer_cast<sf::RectangleShape>(shapes).get()->setSize(newSize);
}
}
sf::RectangleShape() is a concrete class that inherits from sf::Shape and setSize() is defined for it, not sf::Shape, which is why I need to cast.
Of course, I need to do some error handling, in the case that the dynamic cast fails and returns an empty shared_ptr.
I'm doing this because I wanted to be able to define the draw method just once, since in this simple game, every object will draw their member this way. Originally I left the shape out of the base class, and e.g. Brick would just have its own private sf::RectangleShape that could get instantiated on the stack; which was clean, but then the draw method had to be re-written for each object type.
This works, but is uglier to work with and introduces heap allocation. I also have shared_ptr overhead (I would have used unique_ptr, but I needed dynamic casting).
Is this the most appropriate way of doing what I'm trying to do?
It might be preferable to keep the interface an interface, and not start mandating implementation details. So just have an empty base class like so:
class VisibleObject
{
public:
~VisibleObject() {}
virtual void draw(sf::RenderWindow & window) = 0;
virtual void setSize(sf::Vector2f const & size) = 0;
};
You can stick the shape storage into the concrete class that implements this interface.
Moreover, Shape should provide a virtual resize method:
class Shape
{
public:
virtual ~Shape() {}
virtual void resize(sf::Vector2f const & size) = 0;
};
Now you can make, say, a VisibleShapeObject as an intermediate base class:
class VisibleShapeObject : public VisibleObject
{
public:
virtual void draw(sf::RenderWindow & window) override final
{
window.draw(*shape_);
}
virtual void setSize(sf::Vector2f const & size) override final
{
shape_->resize(size);
}
protected:
std::shared_ptr<Shape> shape_; // or unique_ptr<Shape>
};
Instead of mandating storage in std::shared_ptr<sf::Shape>, why not simply introduce a means of retrieving an sf::Shape& from the concrete class?
class VisibleObject {
virtual sf::Shape& getShape() = 0;
public:
void draw(sf::RenderWindow& rw) {
rw.draw(getShape());
}
virtual void setSize(sf::Vector2f) = 0;
};
class Brick : VisibleObject {
sf::RectangleShape shape;
sf::Shape& getShape() override { return shape; }
public:
void setSize(sf::Vector2f newSize) override {
shape.setSize(newSize);
}
};
It seems ridiculous to store via a pointer to base, introducing indirections and downcasts and reference count overhead, when you could just store a plain old member. In fact, if I'm understanding the problem correctly, you could probably use a template to generate concrete classes and avoid a lot of boilerplate:
class VisibleObject {
public:
virtual ~VisibleObject() {}
virtual void draw(sf::RenderWindow&) = 0;
virtual void setSize(sf::Vector2f) = 0;
};
template <typename Shape>
class VisibleConcreteObject : public VisibleObject {
Shape shape;
public:
void draw(sf::RenderWindow& rw) override /* final? */ {
rw.draw(shape);
}
void setSize(sf::Vector2f newSize) override /* final? */ {
shape.setSize(newSize);
}
};
typedef VisibleConcreteObject<sf::RectangleShape> Brick;
You haven't shared everything you are trying to do, but this it one way:
template<ShapeT>
class VisibleObject {
public:
void draw(sf::RenderWindow& rw) {
rw.draw(*shape.get());
}
virtual void setSize(sf::Vector2f) = 0;
protected:
std::shared_ptr<ShapeT> shape;
void reset(ShapeT* shape) {
this->shape = shape;
}
}
class Brick : VisibleObject<sf::RectangleShape> {
Brick() {
shape.reset(new sf::RectangleShape());
}
void setSize(sf::Vector2f newSize) {
shape->setSize(newSize);
}
}
There may be reasons why this doesn't work for you, but without more insight, I couldn't guess at what.
I have been playing around with various methods of making the Visitor pattern in C++ more dynamic, such that sibling classes don't have to know about each other, and that allows later extension of the visitor hierarchy. I came up with this example based on "More Effective C++" by Scott Meyers:
class Dummy
{
public:
void collide(int& gameobject) { }
};
class DynVisitor
{
public:
template<class Visitor=Dummy, class Arg=int>
void visit(Arg& target)
{
Visitor* vis = dynamic_cast<Visitor*>(this);
if(vis != nullptr)
{
vis->collide(target);
}
else
{
cerr<<"No implementation!"<<endl;
}
}
virtual ~DynVisitor() { }
};
class GameObject
{
public:
virtual ~GameObject() { }
virtual void collide(GameObject& obj)
{
cout<<"Default collide implementation"<<endl;
}
virtual void accept(DynVisitor* vis) = 0;
};
class AsteroidVisitor
{
public:
virtual void collide(Asteroid& target) = 0;
virtual ~AsteroidVisitor() = 0;
};
class Collider : public DynVisitor, public AsteroidVisitor
{
public:
virtual void collide(Satellite& target) { cout<<"Satellite collision."<<endl; }
virtual void collide(Spaceship& target) { cout<<"Spaceship collision."<<endl; }
virtual void collide(Asteroid& target) { cout<<"Asteroid collision."<<endl; }
virtual ~Collider() { }
};
class Asteroid : public GameObject
{
public:
virtual void accept(DynVisitor* visitor)
{
visitor->visit<AsteroidVisitor, Asteroid>(*this);
}
};
int main(int argc, char** argv)
{
DynVisitor* coll = new Collider();
GameObject* ast = new Asteroid();
ast->accept(coll);
delete ast;
delete coll;
return 0;
};
This appears to work as I would expect, printing out "Asteroid collision" when the GameObject passed is an Asteroid, and I can add classes to the hierarchy just by defining a new ABC with a collide() method and extending DynVisitor.
My question is, when I add a new class to the hierarchy, does DynVisitor need to be recompiled?
EDIT: Added the asteroid class... sorry about that.
All objects can collide with each other, so they still need to be visitors of each other and hence there is no added "dynamism". DynVisitor is a template and thus needs to be in the translation unit and will be recompiled everytime. In fact, in this example, DynVisitor does't give any benefit because the accept() function can call the collide() function instead of the template visit() function.