box2d contact listening and userdata

hi all
so i am trying to get certain particles to change on collision. I understand that I should be able to use a box2d body’s userdata to store a pointer to my objects. so i create my particles with something like:

  
  
BubbleParticle p;//extends ofxBox2dCircle  
p.setup(box2d.getWorld(), x, y, 10);  
p.body->SetUserData(&p);  
bubbleParticles.push_back(p);  
  

then in my contact listener i have:

  
  
void Add(const b2ContactPoint* point) {  
		b2Vec2 p = point->position;  
		p *= OFX_BOX2D_SCALE;  
		contactAdd(ofPoint(p.x, p.y));  
		b2Body * b1 = point->shape1->GetBody();	  
		if (b1->GetUserData() != NULL) { //must be bubble  
			BubbleParticle * bp = (BubbleParticle*)b1->GetUserData();  
			bp->collide();  
			contact_points.push_back(p);  
		}  
  

this seems to detect the collisions correctly as the contact_points vector gets updated correctly. However, bp->collide(); does not work. I seem to be unable to call anything inside the object. am I doing something wrong when I add the bubble particle to the vector not as a pointer? any help on how to do this greatly appreciated.

so i had slightly better luck adding the pointer after adding to the vector:

  
bubbleParticles.push_back(p);  
bubbleParticles[bubbleParticles.size()-1].body->SetUserData(&bubbleParticles[bubbleParticles.size()-1]);  

but it still didn’t perform as expected. it registered collisions only when the collision happened on initialisation not afterwards when they bump into each other. in the meantime as a workaround i just treat the pointer like an ID and loop through the vector to make changes:

  
  
b2Body * b1 = point->shape1->GetBody();  
		b2Body * b2 = point->shape2->GetBody();  
		  
		if (b1->GetUserData() != NULL) { //must be bubble  
			for(int i=0; i<bubbleParticles.size(); i++) {  
				if (bubbleParticles[i].body->GetUserData() == b1->GetUserData()) {  
					bubbleParticles[i].collided = true;  
				}  
			}  
		}  

would love to know how to do this more eloquently but at least this works for now.

This is likely because you are not allocating your user data BubbleParticle object dynamically, and so it is getting automatically destroyed when it goes out of scope at the end of your creation method.

Try declaring it as a pointer, and initializing it dynamically…

  
  
BubbleParticle* p = new BubbleParticle();//extends ofxBox2dCircle  
p->setup(box2d.getWorld(), x, y, 10);  
p->body->SetUserData(p);  
bubbleParticles.push_back(p);  
  

This is still likely not the best approach though, because you are putting a reference to the object inside itself… Which I think technically should work, but is not necessary.

Consider having your user data BubbleParticle class only define the new data, rather than extending the ofxBox2dCircle. Then your initialization would be something like this:

  
  
BubbleParticle* p = new BubbleParticle();// should NOT extend ofxBox2dCircle  
ofxBox2dCircle* myObj = new ofxBox2dCircle();  
myObj->setup(box2d.getWorld(), x, y, 10);  
myObj->body->SetUserData(p);  
bubbleParticles.push_back(myObj); // remember to update your bubbleParticles vector to take type <ofxBox2dCircle*> rather than <BubbleParticle>  
  

Taking this approach allocates your objects dynamically, and so they will not be destroyed when they go out of scope at the end of your initializer function.

Because you are now allocating memory dynamically, you must be careful of memory leaks, and make sure that you use the delete command to free up the memory when you no longer need the objects. Take note that the body object does not delete the UserData for you… so before you delete the ofxBox2dCircle, you will want to call its GetUserData() method and if it is not NULL, delete it, then delete the ofxBox2dCircle.

Ex (assuming vector<ofxBox2dCircle*> bubbleParticles; defined in testApp … otherwise put it in the destructor of whichever class contains the vector):

  
  
void testApp::exit(){  
	for(int i=0; i < bubbleParticles.size(); i++){  
		void* userData = bubbleParticles[i]->body->GetUserData();  
		if(userData != NULL){  
			delete userData;  
			bubbleParticles[i]->body->SetUserData(NULL);  
		}  
		delete bubbleParticles[i];  
	}  
	bubbleParticles.clear();  
}  
  

HTH!
-plong0

thanks plong0 that post is so helpful and does so much more for me than solve this one problem! will try it all out today. thanks!

cool. glad it was helpful. C++ pointers are typically one of the more challenging aspects for people to understand at first; but once you grok 'em, they’re your bestest friend ever!
Definitely worth doing a tutorial or two on them… practice em till you fully understand the how/why/when, and you will be greatly rewarded with one of the most powerful tools in the C++ toolbox.

A good rule to remember is that if you need to declare/initialize the object in one function and have it persist beyond the end of that function - you will need a pointer (unless you’re using a pass-by-reference function parameter - which is a whole other topic).

The main rule when dealing with pointers is to always always always remember to free up anything you allocate with the new keyword by using the delete (or delete[] for arrays) keyword. If you don’t, then your RAM will slowly, but surely fill up, and eventually crash your program - if not your entire system.

If you’re using XCode, I have found that MallocDebug set to detect Leaks has been a most invaluable tool in tracking down and eliminating potentially fatal memory leaks.

Good luck in pointer-land (aka RAM)!