I have been following lazyfoos tutorials on SDL, and I have heavily modified his code to make sort of a ship game, that moves around. I'm trying to make the ship shoot, but i have absolutely no idea how to go about doing this. I have the ship and it's movements and the actual application of the image in a class, and I as wondering if anyone had any techniques or certain ways that are efficient in making the ship shoot, making the shot move independently and then disappearing when it goes off screen. I know that I am giving a vague explanation sort of, but I don't want to be given all of the answers, just a little sample code and a point in the right direction.
Create a class to hold a projectile, with all the information you need in it, such as this:
struct Projectile
{
Vector2 position;
Vector2 velocity;
shared_ptr<Image> graphic;
Time time_until_my_destruction;
bool dead;
void update(Time time_delta) {
if(!dead) {
position += velocity * time_delta;
time_until_my_destruction -= time_delta;
if(time_until_my_destruction < 0.0) dead = true;
}
}
void draw(DrawDest & dest) const {
graphic->draw(dest, position);
}
bool checkCollision(const GameObject & object) const {
return object.area().contains(position);
}
};
This class is not complete obviously, you'll probably want to make adjustments to access levels, and write some constructors and other things, but it should give you the basic idea.
Make a container of those. When the ship fires, put one into the container. Each frame, call update, draw, check if the projectile is dead and check for collisions against the game objects. If a collision occurs, apply damage or whatever. If the object is dead, remove it from the container.
I can only absolutely recommend Aaron's Game Programming Tutorials, it uses C++ and SDL.
Related
So I'm using SFML for a Computer Science project - making a chess game. I have a class Square which is a single square of the chessboard - currently, it contains four vertices (four sf::Vertex objects in a member variable sf::VertexArray) and is colored either white or black. A class ChessBoard encapsulates a std::vector of Squares.
Using the tutorial given by SFML, I'm able to draw a single square. However, the draw() function works based on vertices, and since the ChessBoard class doesn't not actually contain vertices, but rather objects that themselves contain vertices, I'm not able to draw the chess board (i.e. its internal draw() function does not work).
Does anyone know how to work around this?
(I can provide more info/clarification/code if necessary/helpful.)
That's not really how "higher level drawing" is supposed to work.
Your parent class(es) shouldn't have to bother how to draw children. You're mixing responsibilities.
Instead, subclass sf::Drawable (and sf::Transformable, if required).
All this does is forcing you to implement a draw() member, which does all the drawing.
Here's a simple example for your ChessBoard class:
class ChessBoard : public sf::Drawable {
void draw (RenderTarget &target, RenderStates states) const {
for (auto &tile : mTiles) // Iterate over all board pieces
target.draw(tile, states); // Draw them
}
}
As you can see, this is trivial to setup. In a similar way, you can overload your Square class. (Isn't that name too generic? Why not simply reusing sf::RectangleShape?)
class ChessBoard : public sf::Drawable {
void draw (RenderTarget &target, RenderStates states) const {
target.draw(mVertices, states);
}
}
So, back to your main game loop. How to draw the ChessBoard? Again, trivial:
while (window.isOpen()) {
// All the other things happening
window.draw(mChessBoard);
}
While the advantages of this approach might not be as obvious at first, it's pretty easy to see that you're capable of passing responsibilities down the line. For example, the ChessBoard doesn't have to know how to properly draw a Square. In a trivial example – using unicolored polygons only – it's not that easy to notice, but your code will be a lot cleaner once you start adding shaders, textures, etc. Suddenly you'd no longer just have to return a sf::VertexArray, but you'll also need pointers or references to the other ressources. So the ChessBoard would have to know, which components to request from Square to draw it properly (Does it have a shader? Do I need a texture?).
Nevermind. Silly me. Implemented a getter inside class Square that returned the vertex array, & inside Chessboard looped through the vector of squares, calling the getter on each iteration.
Backstory:
Currently I have a series of three classes related to a game. I've previously made games using Unity where you access components such as the camera using functions accessible throughout all code. My current setup, however, relies on instances of each class being sent across the other classes. See the following outtake:
class World{
};
class Game{
Camera* camera;
World* world;
};
class Camera{
Game* game;
World* world;
};
Questions:
Is this a result of bad design or are there times when a setup like
this is justified?
Should I rather use static classes over my current setup since it
would clarify the code and no more than one instance of each class is
ever used?
What are the possible downsides of using static classes over
instances?
Edit: Why they need to access each other.
Why Camera needs World:
The camera needs access to the world since the world need to be rendered from the perspective of the camera. The camera triggers a render of the world depending on what it sees. Rendering is triggered from various methods in the camera such as when it moves.
When the camera is drawn it draws what it sees, such as the world. To draw the world the camera needs access to the world.
Why Camera needs Game:
Game has values such as FPS which Camera use to display an overlay of debugging information.
Three classes which are that tightly coupled does suggest some questionable design choices. Try to change your code so that, for example, the Camera gets a pointer or reference to World passed in only the methods where it actually needs to deal with World. Also consider whether Camera and World actually need a pointer to Game. Conceptually it would make more sense if Game has a World and has a Camera, instead of all three objects being owned by someone else (who)?
The relationship between Game and Camera still only suggest that you should pass Game, or even better, relevant data FROM Game as method arguments to the Camera draw method.
If you have static (global) instance(s) of any class(es) it can be accessed from anywhere and might end up with a "big ball of mud" making it hard to track what uses or needs what.
One idea from the so-called "SOLID" principles is that
"Details should depend upon abstractions"
Now introducing extra abstract base classes (or interfaces in languages that support them) might seem extra complicated, but it might help you find which parts of each object you rally need from where, and allow you say to introduce another Game in the future.
One approach might go as follows:
#include <iostream>
class World {
public:
int Info() { return 0; }
};
//An abstract base class
class IGame {
public:
virtual ~IGame() = 0 {}
virtual int FPS() = 0;
};
//One specific type of game
class Game : public IGame {
public:
int FPS() { return 0; }
};
class Camera {
public:
Camera(Game * game, World * world) : game(game), world(world) {
}
void Move() {
//actually move first then ...
Draw();
}
void Draw() {
//This will show us what needs to be public in the other classes
std::cout << world->Info() << '\n';
if (game)
std::cout << game->FPS() << '\n';
}
private:
IGame* game;
World* world;
};
int main() {
World world;
Game game;
Camera camera(&game, &world);
camera.Move();
}
(to the Dependency Inversion Principle) as discussed in this question
This might seem like overkill for what you are doing, but takes you nearer "everything relies on something abstract" rather than "everything conrete relies on everything else".
I'm trying to write a class (some sort of graphics engine) basically it's purpose is to render ANYTHING that I pass into it. In most tutorials I've seen, objects draw themselves. I'm not sure if that's how things are supposed to work. I've been searching the internet trying to come up with different ways to handle this problem, I've been reviewing function templates and class templates over and over again (which sounds like the solution I could be looking for) but when I try using templates, it just seems messy to me (possibly because I don't fully understand how to use them) and then I'll feel like taking the template class down, then I'll give it a second try but then I just take it down again, I'm not sure if that's the way to go but it might be. Originally it was tiled-based only (including a movable player on screen along with a camera system), but now I've trying to code up a tile map editor which has things such as tool bars, lists, text, possibly even primitives on screen in the future, etc. and I'm wondering how I will draw all those elements onto the screen with a certain procedure (the procedure isn't important right now, I'll find that out later). If any of you were going to write a graphics engine class, how would you have it distinguish different types of graphic objects from one another, such as a primitive not being drawn as a sprite or a sphere primitive not being drawn as a triangle primitive, etc.? Any help would be appreciated. :)
This is the header for it, it's not functional right now because I've been doing some editing on it, Just ignore the part where I'm using the "new" keyword, I'm still learning that, but I hope this gives an idea for what I'm trying to accomplish:
//graphicsEngine.h
#pragma once
#include<allegro5\allegro.h>
#include<allegro5\allegro_image.h>
#include<allegro5\allegro_primitives.h>
template <class graphicObjectData>
class graphicsEngine
{
public:
static graphicObjectData graphicObject[];
static int numObjects;
static void setup()
{
al_init_image_addon();
al_init_primitives_addon();
graphicObject = new graphicObjectData [1]; //ignore this line
}
template <class graphicObjectData> static void registerObject(graphicObjectData &newGraphicObject) //I'm trying to use a template function to take any type of graphic object
{
graphicObject[numObjects] = &newObject;
numObjects++;
}
static void process() //This is the main process where EVERYTHING is supposed be drawn
{
int i;
al_clear_to_color(al_map_rgb(0,0,0));
for (i=0;i<numObjects;i++) drawObject(graphicObject[i]);
al_flip_display();
}
};
I am a huge fan of templates, but you may find in this case that they are cumbersome (though not necessarily the wrong answer). Since it appears you may be wanting diverse object types in your drawing container, inheritance may actually be a stronger solution.
You will want a base type which provides an abstract interface for drawing. All this class needs is some function which provides a mechanism for the actual draw process. It does not actually care how drawing occurs, what's important is that the deriving class knows how to draw itself (if you want to separate your drawing and your objects, keep reading and I will try to explain a way to accomplish this):
class Drawable {
public:
// This is our interface for drawing. Simply, we just need
// something to instruct our base class to draw something.
// Note: this method is pure virtual so that is must be
// overriden by a deriving class.
virtual void draw() = 0;
// In addition, we need to also give this class a default virtual
// destructor in case the deriving class needs to clean itself up.
virtual ~Drawable() { /* The deriving class might want to fill this in */ }
};
From here, you would simply write new classes which inherit from the Drawable class and provide the necessary draw() override.
class Circle : public Drawable {
public:
void draw() {
// Do whatever you need to make this render a circle.
}
~Circle() { /* Do cleanup code */ }
};
class Tetrahedron : public Drawable {
public:
void draw() {
// Do whatever you need to make this render a tetrahedron.
}
~Tetrahedron() { /* Do cleanup code */ }
};
class DrawableText : public Drawable {
public:
std::string _text;
// Just to illustrate that the state of the deriving class
// could be variable and even dependent on other classes:
DrawableText(std::string text) : _text(text) {}
void draw() {
// Yet another override of the Drawable::draw function.
}
~DrawableText() {
// Cleanup here again - in this case, _text will clean itself
// up so nothing to do here. You could even omit this since
// Drawable provides a default destructor.
}
};
Now, to link all these objects together, you could simply place them in a container of your choosing which accepts references or pointers (or in C++11 and greater, unique_ptr, shared_ptr and friends). Setup whatever draw context you need and loop through all the contents of the container calling draw().
void do_drawing() {
// This works, but consider checking out unique_ptr and shared_ptr for safer
// memory management
std::vector<Drawable*> drawable_objects;
drawable_objects.push_back(new Circle);
drawable_objects.push_back(new Tetrahedron);
drawable_objects.push_back(new DrawableText("Hello, Drawing Program!"));
// Loop through and draw our circle, tetrahedron and text.
for (auto drawable_object : drawable_objects) {
drawable_object->draw();
}
// Remember to clean up the allocations in drawable_objects!
}
If you would like to provide state information to your drawing mechanism, you can require that as a parameter in the draw() routine of the Drawable base class:
class Drawable {
public:
// Now takes parameters which hold program state
virtual void draw(DrawContext& draw_context, WorldData& world_data) = 0;
virtual ~Drawable() { /* The deriving class might want to fill this in */ }
};
The deriving classes Circle, Tetrahedron and DrawableText would, of course, need their draw() signatures updated to take the new program state, but this will allow you to do all of your low-level drawing through an object which is designed for graphics drawing instead of burdening the main class with this functionality. What state you provide is solely up to you and your design. It's pretty flexible.
BIG UPDATE - Another Way to Do It Using Composition
I've been giving it careful thought, and decided to share what I've been up to. What I wrote above has worked for me in the past, but this time around I've decided to go a different route with my engine and forego a scene graph entirely. I'm not sure I can recommend this way of doing things as it can make things complicated, but it also opens the doors to a tremendous amount of flexibility. Effectively, I have written lower-level objects such as VertexBuffer, Effect, Texture etc. which allow me to compose objects in any way I want. I am using templates this time around more than inheritance (though intheritance is still necessary for providing implementations for the VertexBuffers, Textures, etc.).
The reason I bring this up is because you were talking about getting a larger degree of seperation. Using a system such as I described, I could build a world object like this:
class World {
public:
WorldGeometry geometry; // Would hold triangle data.
WorldOccluder occluder; // Runs occlusion tests against
// the geometry and flags what's visible and
// what is not.
WorldCollider collider; // Handles all routines for collision detections.
WorldDrawer drawer; // Draws the world geometry.
void process_and_draw();// Optionally calls everything in necessary
// order.
};
Here, i would have multiple objects which focus on a single aspect of my engine's processing. WorldGeometry would store all polygon details about this particular world object. WorldOccluder would do checks against the camera and geometry to see which patches of the world are actually visible. WorldCollider would process collission detection against any world objects (omitted for brevity). Finally, WorldDrawer would actually be responsible for the drawing of the world and maintain the VertexBuffer and other lower-level drawing objects as needed.
As you can see, this works a little more closely to what you originally asked as the geometry is actually not used only for rendering. It's more data on the polygons of the world but can be fed to WorldGeometry and WorldOccluder which don't do any drawing whatsoever. In fact, the World class only exists to group these similar classes together, but the WorldDrawer may not be dependent on a World object. Instead, it may need a WorldGeometry object or even a list of Triangles. Basically, your program structure becomes highly flexible and dependencies begin to disappear since objects do not inherit often or at all and only request what they absolutely require to function. Case in point:
class WorldOccluder {
public:
// I do not need anything more than a WorldGeometry reference here //
WorldOccluder(WorldGeometry& geometry) : _geometry(geometry)
// At this point, all I need to function is the position of the camera //
WorldOccluderResult check_occlusion(const Float3& camera) {
// Do all of the world occlusion checks based on the passed
// geometry and then return a WorldOccluderResult
// Which hypothetically could contain lists for visible and occluded
// geometry
}
private:
WorldGeometry& _geometry;
};
I chose the WorldOccluder as an example because I've spent the better part of the day working on something like this for my engine and have used a class hierarchy much like above. I've got boxes in 3D space changing colors based on if they should be seen or not. My classes are very succinct and easy to follow, and my entire project hierarchy is easy to follow (I think it is anyway). So this seems to work just fine! I love being on vacation!
Final note: I mentioned templates but didn't explain them. If I have an object that does processing around drawing, a template works really well for this. It avoids dependencies (such as through inheritence) while still giving a great degree of flexibility. Additionally, templates can be optimized by the compiler by inlining code and avoiding virtual-style calls (if the compiler can deduce such optimizations):
template <typename TEffect, TDrawable>
void draw(TEffect& effect, TDrawable& drawable, const Matrix& world, const Matrix& view, const Matrix& projection) {
// Setup effect matrices - our effect template
// must provide these function signatures
effect.world(world);
effect.view(view);
effect.projection(projection);
// Do some drawing!
// (NOTE: could use some RAII stuff here in case drawable throws).
effect.begin();
for (int pass = 0; pass < effect.pass_count(); pass++) {
effect.begin_pass(pass);
drawable.draw(); // Once again, TDrawable objects must provide this signature
effect.end_pass(pass);
}
effect.end();
}
My technique might really suck, but I do it like this.
class entity {
public:
virtual void render() {}
};
vector<entity> entities;
void render() {
for(auto c : entities) {
c->render();
}
}
Then I can do stuff like this:
class cubeEntity : public entity {
public:
virtual void render() override {
drawCube();
}
};
class triangleEntity : public entity {
public:
virtual void render() override {
drawTriangle();
}
};
And to use it:
entities.push_back(new cubeEntity());
entities.push_back(new triangleEntity());
People say that it's bad to use dynamic inheritance. They're a lot smarter than me, but this approach has been working fine for a while. Make sure to make all your destructors virtual!
The way the SFML graphics library draws objects (and the way I think is most manageable) is to have all drawable objects inherit from a 'Drawable' class (like the one in David Peterson's answer), which can then be passed to the graphics engine in order to be drawn.
To draw objects, I'd have:
A Base class:
class Drawable
{
int XPosition;
int YPosition;
int PixelData[100][100]; //Or whatever storage system you're using
}
This can be used to contain information common to all drawable classes (like position, and some form of data storage).
Derived Subclasses:
class Triangle : public Drawable
{
Triangle() {} //overloaded constructors, additional variables etc
int indigenous_to_triangle;
}
Because each subclass is largely unique, you can use this method to create anything from sprites to graphical-primitives.
Each of these derived classes can then be passed to the engine by reference with
A 'Draw' function referencing the Base class:
void GraphicsEngine::draw(const Drawable& _object);
Using this method, a template is no longer necessary. Unfortunately your current graphicObjectData array wouldn't work, because derived classes would be 'sliced' in order to fit in it. However, creating a list or vector of 'const Drawable*' pointers (or preferably, smart pointers) would work just as well for keeping tabs on all your objects, though the actual objects would have to be stored elsewhere.
You could use something like this to draw everything using a vector of pointers (I tried to preserve your function and variable names):
std::vector<const Drawable*> graphicObject; //Smart pointers would be better here
static void process()
{
for (int i = 0; i < graphicObject.size(); ++i)
draw(graphicObject[i]);
}
You'd just have to make sure you added each object to the list as it was created.
If you were clever about it, you could even do this in the construction and destruction:
class Drawable; //So the compiler doesn't throw an error
std::vector<const Drawable*> graphicObject;
class Drawable
{
Triangle() {} //overloaded constructors, additional variables etc
int indigenous_to_triangle;
std::vector<const Drawable*>::iterator itPos;
Drawable() {
graphicObject.push_back(this);
itPos = graphicObject.end() - 1;
}
~Drawable() {
graphicObject.erase(itPos);
}
}
Now you can just create objects and they'll be drawn automatically when process() is called! And they'll even be removed from the list once they're destroyed!
All the above ideas have served me well in the past, so I hope I've helped you out, or at least given you something to think about.
Yet another question about classes as Im new to OOP. I am creating a game just for fun. Its a top down shooter, space shooter.
I have a few different classes:
Bullet (a list of bullet coordinates),
Player (player sprite, position etc),
Enemy (enemy sprite, position etc),
Collision (taking coordinates Ax, Ay, and Bx, By to see if they have collided)
How can I send the coordinates from Bullet, Enemy to the Collision class to see if they have collided?
Collision col
col.collision(ax, ay, bx, by) //how can I get the Player and Bullet pos?
Probably what you want to do is to have a common class "SceneObject" that has a position. Then Player, Enemy and Bullet all inherit from that class.
Your Collision then does not need knowledge about Players, Enemies etc. but only SceneObjects which have a position. You can write a Getter method in your base class that returns the position.
I would create a base class for Bullet, Enemy (and maybe Player). Let's call it Object and the Object would own the Coordinates and would have a function determining if a Collision happened.
In code it would look like to following:
class Object
{
private:
Coord position;
public:
bool Collide(const Object& otherObject) const;
};
You typically have to go through every enemy and check the coordinates of that enemy against your player coordinates.
I'm sure you stored those enemies somewhere, preferably in a container like std::vector. Now let me just assume you did exactly that:
// somewhere in a 'Game' class
std::vector<Enemy> myEnemies;
Player myPlayer;
Collision col;
for(int i=0; i < myEnemies.size(); ++i){
col.collision(myPlayer.getX(), myPlayer.getY(), myEnemies[i].getX(), myEnemies[i].getY())
}
And that's it. :) You do the same for bullets and similar objects. Save them all in a container, iterate over that container, and check the positions of the objects.
Now, for another tip, if a class only contains methods and no variables, you may aswell make that free functions. Just because you use OOP in some parts of your project doesn't require you to use it everywhere. Make that collision a free function and be done with it. :)
I'm in a bit of a pickle: say I'm making a simple, 2D, Zelda-like game.
When two Objects collide, each should have a resulting action. However, when the main character collides with something, his reaction depends solely on the type of the object with which he collided. If it's a monster, he should bounce back, if it's a wall, nothing should happen, if it's a magical blue box with ribbons, he should heal, etc. (these are just examples).
I should also note that BOTH things are part of the collision, that is, collision events should happen for both the character AND the monster, not just one or the other.
How would you write code like this? I can think of a number of incredibly inelegant ways, for instance, having virtual functions in the global WorldObject class, to identify attributes - for instance, a GetObjectType() function (returns ints, char*s, anything that identifies the object as Monster, Box, or Wall), then in classes with more attributes, say Monster, there could be more virtual functions, say GetSpecies().
However, this becomes annoying to maintain, and leads to a large cascading switch (or If) statement in the collision handler
MainCharacter::Handler(Object& obj)
{
switch(obj.GetType())
{
case MONSTER:
switch((*(Monster*)&obj)->GetSpecies())
{
case EVILSCARYDOG:
...
...
}
...
}
}
There's also the option of using files, and the files would have things like:
Object=Monster
Species=EvilScaryDog
Subspecies=Boss
And then the code can retrieve the attributes without the need for virtual functions cluttering everything up. This doesn't solve the cascading If problem, however.
And THEN there's the option of having a function for each case, say CollideWall(), CollideMonster(), CollideHealingThingy(). This is personally my least favourite (although they're all far from likeable), because it seems the most cumbersome to maintain.
Could somebody please give some insight into more elegant solutions to this problem?
Thanks for any and all help!
I would do it vice versa - because if the character collides with an object, an object collides with the character as well. Thus you can have a base class Object, like this:
class Object {
virtual void collideWithCharacter(MainCharacter&) = 0;
};
class Monster : public Object {
virtual void collideWithCharacter(MainCharacter&) { /* Monster collision handler */ }
};
// etc. for each object
Generally in OOP design virtual functions are the only "correct" solution for cases like this:
switch (obj.getType()) {
case A: /* ... */ break;
case B: /* ... */ break;
}
EDIT:
After your clarification, you will need to adjust the above a bit. The MainCharacter should have overloaded methods for each of the objects it can collide with:
class MainCharacter {
void collideWith(Monster&) { /* ... */ }
void collideWith(EvilScaryDog&) { /* ... */ }
void collideWith(Boss&) { /* ... */ }
/* etc. for each object */
};
class Object {
virtual void collideWithCharacter(MainCharacter&) = 0;
};
class Monster : public Object {
virtual void collideWithCharacter(MainCharacter& c)
{
c.collideWith(*this); // Tell the main character it collided with us
/* ... */
}
};
/* So on for each object */
This way you notify the main character about the collision and it can take appropriate actions. Also if you need an object that should not notify the main character about the collision, you can just remove the notification call in that particular class.
This approach is called a double dispatch.
I would also consider making the MainCharacter itself an Object, move the overloads to Object and use collideWith instead of collideWithCharacter.
How about deriving all collidable objects from one common abstract class (let's call it Collidable). That class could contain all properties that can be changed by a collission and one HandleCollision function. When two objects collide, you just call HandleCollision on each object with the other object as the argument. Each object manipulates the other to handle the collision. Neither object needs to know what other object type it just bounced into and you have no big switch statements.
Make all colidable entities implement an interface (lets say "Collidable") with a collideWith(Collidable) method.
Then, on you collision detection algorithm, if you detect that A collides with B, you would call:
A->collideWith((Collidable)B);
B->collideWith((Collidable)A);
Assume that A is the MainCharacter and B a monster and both implement the Collidable interface.
A->collideWith(B);
Would call the following:
MainCharacter::collideWith(Collidable& obj)
{
//switch(obj.GetType()){
// case MONSTER:
// ...
//instead of this switch you were doing, dispatch it to another function
obj->collideWith(this); //Note that "this", in this context is evaluated to the
//something of type MainCharacter.
}
This would in turn call the Monster::collideWith(MainCharacter) method and you can implement all monster-character behaviour there:
Monster::CollideWith(MainCharacter mc){
//take the life of character and make it bounce back
mc->takeDamage(this.attackPower);
mc->bounceBack(20/*e.g.*/);
}
More info: Single Dispatch
Hope it helps.
What you call "an annoying switch statement" i would call "a great game" so you are on the right track.
Having a function for every interaction/game rule is exactly what I would suggest. It makes it easy to find, debug, change and add new functionality:
void PlayerCollidesWithWall(player, wall) {
player.velocity = 0;
}
void PlayerCollidesWithHPPotion(player, hpPoition) {
player.hp = player.maxHp;
Destroy(hpPoition);
}
...
So the question is really how to detect each of these cases. Assuming you have some sort of collision detection that results in X and Y collide (as simple as N^2 overlap tests (hey, it works for plants vs zombies, and that's got a lot going on!) or as complicated as sweep and prune + gjk)
void DoCollision(x, y) {
if (x.IsPlayer() && y.IsWall()) { // need reverse too, y.IsPlayer, x.IsWall
PlayerCollidesWithWall(x, y); // unless you have somehow sorted them...
return;
}
if (x.IsPlayer() && y.IsPotion() { ... }
...
This style, while verbose is
easy to debug
easy to add cases
shows you when you have
logical/design inconsistencies or
omissions "oh what if a X is both a
player and a wall due to the
"PosessWall" ability, what then!?!"
(and then lets you simply add cases
to handle those)
Spore's cell stage uses exactly this style and has approximately 100 checks resulting in about 70 different outcomes (not counting the param reversals). It's only a ten minute game, that's 1 new interaction every 6 seconds for the whole stage - now that's gameplay value!
If I am getting your problem correctly, I would sth like
Class EventManager {
// some members/methods
handleCollisionEvent(ObjectType1 o1, ObjectType2 o2);
// and do overloading for every type of unique behavior with different type of objects.
// can have default behavior as well for unhandled object types
}