Jump to content
  • entries
    943
  • comments
    5,899
  • views
    924,401

Collision Decisions


Josh

6,785 views

 Share

As I was implementing the collision commands for Leadwerks3D, I noticed a few things that illustrate the differences between the design philosophies of Leadwerks Engine and Leadwerks3D.

 

You'll easily recognize the new collision commands, although they have been named in a more verbose but logical manner:

void ClearCollisionResponses();
void SetCollisionResponse(const int& collisiontype0, const int& collisiontype1, const int& response);
int GetCollisionResponse(const int& collisiontype0, const int& collisiontype1);

In Leadwerks Engine, the collisions were left to the end user to define however they wished. In Leadwerks3D, we have some built-in collision types that are declared in the engine source code:

const int COLLISION_SCENE = 1;
const int COLLISION_CHARACTER = 2;
const int COLLISION_PROP = 3;
const int COLLISION_DEBRIS = 4;

By default, the following collision responses are created automatically:

SetCollisionResponse(COLLISION_SCENE,COLLISION_CHARACTER,COLLISION_COLLIDE);
SetCollisionResponse(COLLISION_CHARACTER,COLLISION_CHARACTER,COLLISION_COLLIDE);
SetCollisionResponse(COLLISION_PROP,COLLISION_CHARACTER,COLLISION_COLLIDE);
SetCollisionResponse(COLLISION_DEBRIS,COLLISION_SCENE,COLLISION_COLLIDE);
SetCollisionResponse(COLLISION_PROP,COLLISION_PROP,COLLISION_COLLIDE);
SetCollisionResponse(COLLISION_DEBRIS,COLLISION_PROP,COLLISION_COLLIDE);

Each entity's default collision type is COLLISION_PROP, which means that without specifying any collision responses, all entities collide with one another.

 

Of course if you want to scrap all my suggested collision responses and define your own, you can just call ClearCollisionResponses() at the start of your program. The main difference in design is that Leadwerks3D assumes the user wants some default behavior already specified, instead of being a completely blank slate. That's a design decision that is being implemented across all aspects of the engine to make it easier to get started with.

 Share

24 Comments


Recommended Comments

what would you consider the difference between a PROP and DEBRIS?

 

in LE2.X, a PROP is anything basically other than a scene or character and would collide with a scene or character or another prop... here you are showing the default for a PROP only collides with a character but not a scene or prop? Has DEBRIS replaced PROP? but yet DEBRIS will not collide with other DEBRIS?

 

ugh... you changed the constant values... thats going to get me a couple of times in the future i just know it! :)

Link to comment

I don't know what the previous constants were, off the top of my head.

 

Debris means small fragments of things you don't want the player to affect, but will still collide with the scene and most dynamic objects. If you play around with Half-Life 2: Deathmatch you'll get a good feel for this. For example, if a piece of wood breaks in half, you don't want the player kicking small fragments around, but they would still need to collide with the scene and large physical objects.

Link to comment

LE2:

COLLISION_NONE=0
COLLISION_PROP=1
COLLISION_SCENE=2
COLLISION_CHARACTER=3
COLLISION_TRIGGER=4
COLLISION_AILINEOFSIGHT=5

 

ok understand about debris... but does that mean we can expect breakable objects inherent within LE? As for the PROP, isn't it missing a collision response for scene and other props?

Link to comment

CSG brushes will eventually have real breakage. No pre-made pieces have to be made. Scripted breakage of models can still be done the same in LE2, but typically no one bothers with something that requires more work in the art pipeline. I know there are some third-party tools coming out that will create pre-broken pieces for any triangle mesh, and that's something I'll look at down the road, but it's low-priority at the moment.

 

Yes, I was missing one collision response I wanted.

Link to comment

By the way, the collision type pair is stored in an iVec2, a new class in Leadwerks3D, which is just a Vec2 with integers instead of floats. An operation overloader allows this class to be used as the key in an std::map, and the collision response is stored as a value for that key:

void PhysicsDriver::SetCollisionResponse(const int& collisiontype0, const int& collisiontype1, const int& response)
{
if (collisiontype0>collisiontype1)
{
	collisionresponse[iVec2(collisiontype1,collisiontype0)] = response;
}
else
{
	collisionresponse[iVec2(collisiontype0,collisiontype1)] = response;
}
}

The C++ programmers here might find that interesting.

Link to comment

I appreciate the engine taking care of collision declarations and other assumable information.

 

I'm hoping that with this version of the engine, you can make it so we can do something like this, any time we want:

AreEntitiesColliding(entity1,entity2);

Link to comment

gamecreator brings up a good point. I like collision callbacks personally but it can require more thought & design (a different way of thinking) than inline checking for collisions. One would probably want LE3D to let the developer design which collision method they wish to use.

Link to comment
Guest Red Ocktober

Posted

I appreciate the engine taking care of collision declarations and other assumable information.

 

agreed... this is good...

 

 

I'm hoping that with this version of the engine, you can make it so we can do something like this, any time we want:

AreEntitiesColliding(entity1,entity2)

 

uuuhhh... forgive me here, but i'm not too keen on that sorta approach...

 

i'd prefer something that would allow collision response to be more intimate to the object itself... something that would facilitate a method in the class declaration which would allow each instance to react as needed... that way an overridable collision response could be available to all objects of a class, be available as required, be "automatic"... and only called in response to that object colliding with another...

 

continually checking for collisions when the chance of none actually occurring i see as a wasted cycle...

 

also... this goes along with my design goals of making all objects in a game self contained components that only have to be dropped into (declared) an existing framework...

 

--Mike

Link to comment

I appreciate the engine taking care of collision declarations and other assumable information.I'm hoping that with this version of the engine, you can make it so we can do something like this, any time we want:AreEntitiesColliding(entity1,entity2);

The problem is that at any given time, you may have thousands of collisions occurring, but you are only interested in a few. If we want to save all that information, we're going to have a ton of dynamically allocated collision objects each physics update, which makes things slow. Okay, we can get around that by writing the collision data to big memory buffers that only get expanded, never made smaller. Now we have to consider that while the user is accessing this memory, the physics are already processing the next step and writing to these buffers, so now we need to use two copies of the data, and alternate the read and write buffers each frame.

 

If each collision is 20 bytes (position, normal, speed, entity0, entity1), 1000 collisions will use 19 kb of memory, times two. Okay, that isn't bad.

 

So that gives us a system where you call CountCollisions() and GetCollision(int index) and iterate through all collisions that occurred, but it still doesn't do what you want. To find a particular collision, you would have to iterate through n*n collisions, which is always the wrong way to do things in programming. If you had 1000 collisions, you would have to iterate through 1000*1000=1,000,000 combinations to find the one you are interested in.

 

Another way to do this would be to add a list of collisions to each entity, again with a read and write buffer that alternate each frame. That would allow faster focusing on the entities you are interested in, but again we are storing tons of data when we're really only interested in a small percentage of that data.

 

When we use collision callbacks, all collisions are dealt with immediately, and no collision data has to be written and saved. (Not entirely true...the engine stores an array of collisions for each thread, but only entities with a collision callback get written into the array, so that cuts out 95% of your entities.)

 

For C++, I think the ideal situation would be if you could write a class with a virtual function that overrides an Entity class function called OnCollision(). However, since C++ can't turn an object of one class into one with a derived class, that makes it very difficult. If you loaded a model, retrieved one of its limbs, and tried to recreate it with a derived class... :) Not pretty. This is a major shortcoming of C++, but to my knowledge, no other language deals with it either.

 

So we have this situation where there's one approach that's simpler if you only have a few objects on the screen, but can potentially cause a lot of slowdown if you have a heavy simulation running.

 

The best approach is probably to use a Lua script with a Collision() function declared. The support for Lua in Leadwerks3D is a lot better, with debugging and code stepping.

Link to comment

Thank you for taking the time to consider it and explain the difficulties. Some of this is over my head (I don't know what virtual functions are, for example) but I get most of it. Too bad. Maybe the character controller could be more robust and controllable instead.

Link to comment

A virtual function is just a class function that can be overridden by a derived class' function. I make all my class functions virtual, always. The fact you even have to specify it probably indicates this was tacked on at some point after class functions were originally implemented in C++.

 

So like if you have Entity::SetPosition and Model::SetPosition, the Model class' SetPosition function will override the entity's, if they are both virtual.

Link to comment

Just curious as to why you couldn't do something like:

 

// engine class
class Entity
{
public:
virtual void OnCollision(Entity* e)
{
}
};

// my class
class MyEntity : public Entity
{
public:
        virtual void OnCollision(Entity* e)
        {
             // now I can handle collision with this object
        }
};

Link to comment

Yes, that is what I was describing.

 

Now you have a model that you want to add your own collision function for. You load the model from a file and then... :) Oh wait, it's a Model, not a MyEntity.

 

Okay, so I completely rewrite the Model class so that you can create a blank model, then load a file with it. You have your own MyEntity class derived from the Model class, and you load a file:

MyEntity* entity = new MyEntity;
entity->Load("model.mdl");

 

That works, except the engine actually has the GraphicsDriver class create a model, containing special functions for rendering::

Model* OpenGL3GraphicsDriver::CreateModel()
{
return new OpenGL3GraphicsModel;
}

 

Let's just ignore that problem for now. :)

 

What if you want a limb in a model you load to have a special collision function? Now we have to do something like this:

ModelGenerator* mymodelgenerator = new MyModelGenerator;
world->SetModelGenerator(mymodelgenerator );
MyEntity* entity = new MyEntity;
entity->Load("model.mdl");

 

That wouldn't be confusing, at all. :o

 

Ideally, you would want to just replace an object's class function with your own, or turn a base object into a derived class, but you can't do those things with C++.

 

We could have a "collisionhandler" object as a member of the Entity class, but that is even more abstract and confusing, and you would still be using the "entity" prefix inside the function to access the entity's members, so it's just like a callback and defeats the purpose.

Link to comment

Another thing I should point out about collision callbacks is you avoid the issue of accessing deleted entities. If collisions are stored, you could delete an entity and then when you are iterating through collisions, come across a collision that occurred with an entity that no longer exists. I experienced this problem a long time ago with Blitz3D.

 

I suppose you could delete an entity inside the callback, if you are a crazy person, but the problem is much less likely to come up.

Link to comment

I'm not saying I have the answers. I've been out of C++ for a couple months now and using only Lua for my game so kind of rusty, but.

 

Instead of what you have you would do:

 

Model* entity = new EvilBadGuy();    // note that I use Model for the variable type
entity->Load("evil.bad.guy.mdl");

 

Where MyEntity is derived from Model. Now the entity variable is of type Model AND MyEntity. But more importantly you can now pass the entity variable to any function that takes a Model pointer like a collision function and like all the functions in your engine that are just of type Model.

 

The entity variable can then be cast to MyEntity by the user inside their function if they so need, but that would be up to the user.

 

I guess I don't know about the ModelGenerator thing you have above. Not sure what that's all about and how it relates to limbs.

 

There shouldn't be too many reasons to ever store your models as a type that you derived from (ie. the left side of the variable declaration). You can't store them in a list which will almost always be what you want to do. The whole point in assigning it as the base class you derived from is so you can store it in 1 list, pass it around to common methods. Then the reason for deriving is to override the virtual methods and give custom behavior to them. If you ever need to query the data or call the methods in the child class then you cast it.

 

 

"static_cast can perform conversions between pointers to related classes, not only from the derived class to its base, but also from a base class to its derived. " This is what we would use inside the Collision method if we needed more information about the model we collided with that we stored inside our derived model and isn't part of the base engine model.

Link to comment
You can't store them in a list which will almost always be what you want to do.

You actually can if you use pointers, which I always do for complex objects like entities. You can store Models or whatever in a list of type Entity*, and cast them back to Model* later.

 

The problem is the user can't be responsible for the creation of every single entity. When you load a model, it has children from the file that are automatically created. When you load a map, entities will be automatically created.

Link to comment

You actually can if you use pointers, which I always do for complex objects like entities. You can store Models or whatever in a list of type Entity*, and cast them back to Model* later.

 

That quote out of context. The sentence before flows into the sentence you quoted.

 

You are correct but that's not the way you have it above. You had MyEntity* entity;. You wouldn't do that because you'd have 20 different classes like MyEntity*. Instead you would do Entity* entity (using the engine's base class), which is what you are saying, but not what you typed above. That aside I see where you are coming from with things being automatically loaded.

 

When you load a map, entities will be automatically created.

 

This is generally why virtual methods are limiting and I like using the slot/signal design. Ideally your model class would expose a public event (variable) called OnCollision that my class that I put the model instance in can subscribe to. When the engine calls the model class OnCollision it's really firing all the methods I've chained to it.

 

This is a little different and I'm sure you won't use it as I'm sure you don't feel comfortable with it, but it works like a charm and can fit into C# & Lua. I've posed that code to do it in C++ a bunch of times on the forums. I didn't create it, but someone showed me the code a long time ago and it has so many uses and is very flexible and is standard C++. http://www.leadwerks.com/werkspace/topic/4032-raknet-in-leadwerks-tutorial-files/page__p__35748__hl__events__fromsearch__1#entry35748

 

To describe it in Lua terms it would be like your Model class has a table that stores functions. Any other object can add it's own function to that table. Then your Model class loops through every entry in that table and calls each function. In Lua it's even more flexible because the function signatures can have variable parameters. In C++ the function signature has to be static (the same, not static keyword) but that's fine because it would be anyway in the OnCollision situation.

 

To me this solves the "problem" you'll have by being heavy OO in the engine design and then having a standard C callback method for collisions. Guessing people will think that a little odd. Also if you stick with a C callback are you still having user data functionality because below is generally what I do, but I have to screw around with storing the pointer to the class (this) as user data to the entity so that in the C collision function I can extract it so I can call it's Collision function. Honestly if you leave the user data stuff in I'm cool because I can still do that. If you take it out it'll become harder to do what I need to do in that C callback and that'll suck.

 

Usage:

class BigBossMan
{
private:
  Model* _mdl;
  void mdl_OnCollision(Model* mdl)
  {
  }
public:
  BigBossMan(Model mdl)
  {
     _mdl = mdl;

     // hook the models collision event to this classes function
     _mdl.OnCollision.Bind(this, &BigBossMan::mdl_OnCollision);
  }
};

 

Now you handle collisions normally in the engine. When this _mdl variable has a collision with it you call it's OnCollision event behind the scenes and that will loop through all the methods hooked to it and call them.

 

From the users perspective this is nice and clean. The collision logic for BigBossMan happen in the BigBossMan class and not some global collision function that you might have to go searching through 100 files to find.

 

The "cool" thing is that inside this event class you can accept both class methods or standard C methods. You would just overload the Bind() method to accept both styles and then store 2 separate lists. When calling loop through both lists. This is nice because it allows the user to pick how they want to bind to events of the model.

 

Honestly this would be sweet if every callback that is entity specific would be setup this way. Would be sweet if this was how you handled GUI events too :)

Link to comment

I see. What is I added an "extra" parameter to be passed back to the callback, like this?:

class Thing
{
Model* model;
virtual void OnCollision(Entity* entity, Vec3 position, Vec3 normal, float speed);
};

//Extra paramter gets passed back to callback:
void CollisionCallback(Entity* entity, Vec3 position, Vec3 normal, float speed, char* extra)
{
Thing* thing = (Thing*)extra;

//Call the class function:
thing->OnCollsiion(entity,position,normal,speed)
}

Thing* thing = new Thing;
thing->model = Model::CreateBox();
thing->model->SetCallback(CALLBACK_COLLISION,CollisionCallback,thing);

This would do the same thing you are describing, just one global callback function has to be declared, and it will work with all languages.

Link to comment

Just of curiosity. Is there any special reason why you doesn't use enumerations like this

enum CollisionType
{
     COLLISON_NONE,
     COLLISION_SCENE,
     COLLISION_CHARACTER,
     COLLISION_PROP,
     COLLISION_DEBRIS
};

void PhysicsDriver::SetCollisionResponse(const CollisionType& collisiontype0, const CollisionType& collisiontype1, const int& response)
{
     if ( collisiontype0 == COLLISION_SCENE ) 
     ....
     ....
     ....
}

Link to comment

@Josh, I'd say that works and is similar to what I have to do in LE2 today to get the same functionality. I'd venture to say it's not as pretty as the event way but I understand why you wouldn't go that route just yet :)

 

I promise you if you would take the time to read and understand that event code and think about all the uses you'd never go back and you'd use it in so many places :)

 

Chaining events together is such a cool and powerful ability that really lets you separate your code but still have them linked. It also removes a good number of polling mechanics which generally makes your game faster because you aren't wasting time polling for things that aren't happening.

 

class Actor
{
private:
void _TakeDamage(int value)
{
}
public:
Player()
{
	// we link the actor method to this event so it will be notified when it's called
	TakeDamage.Bind(this, &Actor::_TakeDamage);
}
Event1<int> TakeDamage;
};



class HUD
{
public:
void UpdateDamage(int dmg)
{
}
};


class Game
{
Actor* player = new Player();
HUD* hud = new HUD();

Game()
{
	// you would setup your interactions between objects when your game initializes, which means you don't have
	// to go hunting for object interactions. they are all done right up front. with dynamic objects you can 
	// still set those up when the object is created (bullets for example)
	player->TakeDamage.Bind(hud, hud->UpdateDamage);
}
};


// when your enemy hits the player you would call
player.TakeDamage(15);

 

this uses operator overloading to make TakeDamage look like a function when it's really a variable. same as: TakeDamage.Raise(15)

When the above is called it'll actually fire 2 class methods. the one in Actor and the one in HUD.

 

now the player can take damage in many different ways but you don't have to remember to deal with the HUD in each of those situations because you've created this one time link right at the start

 

Again, I don't expect you to adapt this but just giving you something to think about if you wish. Another way of looking at a problem.

Link to comment

Bind adds the class function pointer to a list so when you raise the event by calling Raise() (or just () with operator overloading) it can loop through this list and call all the class functions that were added. These can be from all different classes as long as the method signatures match.

 

This allows you to chain together events. You call Raise on an event once and it can end up calling 10+ different class methods. It's how you get different classes to talk to each other without having to know about each other (you don't have to store a pointer of class A inside class B so it can query the object every cycle to find what changed).

 

This is one of those things where I tell someone to understand it is hard but to use it is easy. Ask a mechanic to describe every piece inside an alternator and they probably can't do it. Ask them to use it and they can.

 

Note that below is the outfacing class the user would use (the engine really the user wouldn't need to define one just call Bind) like Event2<x, y>, but it has supporter functions that I linked to in my previous post. You can see this class stores a list 'mCaller', and Bind simply adds to this list, and Raise simply loops through the list calling the functions.

 

template<class T1, class T2>
class Event2
{
public:
  list<TFunctor2<T1, T2>* >  mCaller;

  template<class Target>
  Event2(Target* t, void (Target::*fnPtr)(T1, T2))
  { mCaller.push_back(new TSpecificFunctor2<Target, T1, T2>(t,fnPtr)); }

  Event2(){}

  template<class Target>
  void Bind(Target* t, void (Target::*fnPtr)(T1, T2))
  { mCaller.push_back(new TSpecificFunctor2<Target, T1, T2>(t,fnPtr)); }

  void Raise(T1 V1, T2 V2)
  {
     list<TFunctor2<T1, T2>*>::reverse_iterator iter;


     for (iter = mCaller.rbegin(); iter!= mCaller.rend(); iter++)
     {
        (*iter)->Call(V1, V2);
     }
  }   

  void Clear()
  {
     mCaller.clear();
  }
};

Link to comment
Guest
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...