Box-- Absorbing impulse after collide

Started by
15 comments, last by JohnRaptis 8 years, 11 months ago

Hi all,

I'm trying to get an effect from Box2D. I have a game that is overhead, basically with all physics turned off, and I'm only using Box2D for collision response.

I have a player character-- represented as a sphere-- and anywhere from 5 to 50 other spheres constantly are converging on him, Robotron style. The idea of the game is to not get swarmed too badly.

So here's the problem: I want my player character to get pushed around-- but not too much. So if I make him very dense, no problem... he doesn't get shoved around by the horde of chasing objects much.

Except: I want the player's pushing power to be very limited! That is to say, he doesn't have the "strength" to push his way out of the horde. But with Box2D, when I make him dense enough to not be pushed all over the screen by the pursuing bad guys, he's strong enough to shove them all out of his way without a problem.

What I want to do is sort of break my character's attributes apart... I want everyone to have "density" and "strength." Density is how heavy you are, strength is how well you can push away someone else's density. This is also important because my horde of monsters is able to shove their friends out of the way too, so when I try to do some effects like having one pause and "think" it just gets shoved along, because they all have the same density.

I got the idea that I could grab the PostSolve collision impulse and modify it based on the strength, so that the pushing behaves differently... except I can't seem to access the impulse data, and I'm not sure it hasn't already been applied at that point anyway.

Can anyone help? I'm willing to modify the Box2D code-- in fact, I've been trying to, and I've had very limited success mostly because the collisions are simultaneous and I'm trying to adjust the object's mass on the fly to reflect their strength relative to eachother. That ain't working out so well.

Thanks, I appreciate any help you can give.

TLDR: I want to modify Box2D somehow so objects have a "push strength" seperated from their density-- so a heavy object could have very little ability to displace other objects. Essentially, I want to make objects able to absorb some of their impacts so they don't move as far when pushed.

Advertisement

I would be very careful when editing physics engine. Before that try playing around with restitution (determines elasticity of collision, 0 for completely inelastic and 1 for elastic) and velocityDamping parameters. Try setting restitution to small values and/or velocityDamping for player and/or enemies to high values (in this case you might have to increase forces that control characters).

Otherwise I have a suggestion, but I havent tested it. You can edit b2ContactSolver SolveVelocityConstraints procedure. As all your characters are represented as spheres you only need to modify normal constraint solving. Something like this:


vA -= mA * P * (1 - 0.5f * (vcp->strengthA - vcp->strengthB));
wA -= iA * b2Cross(vcp->rA, P); // fix rotation or modify accordingly!

vB += mB * P * (1 + 0.5f * (vcp->strengthA - vcp->strengthB));
wB += iB * b2Cross(vcp->rB, P); // same here

Plus you need to modify Box2D structures (b2Fixture, VelocityConstraintPoint) so that they contain strength property.

Hi there, thanks for replying!

Just for a quick test, I set up a simple two-circle system to prototype... my goal was to just get everything to push at half strength initially, just to make sure I wad modifying the right code.

I did this:


b2Vec2 vaMod=mA*P;
b2Vec2 vbMod=mB*P;
vaMod*=.5f; // Just a pretend weight/strength modifier
vbMod*=.5f; // Pretending!
vA-=vaMod;
vB+=vbMod;

However, I get what seems to be no effect at all-- or it's so minimal that it's below notice (I wouldn't expect that for a 50% modification). Furthermore, even if I multiply it by zero instead of .5 I get nothing. Am I going in the wrong direction here? I notice the direction of the application of the mod-- this is seperation, right? So I tried changing it to multiply by a large number (1000) instead, and I still see no effect whatsoever.

When I do this instead:


vA*=.5f;
vB*=.5f;

I get the desired effect, but it propagates into unwanted areas-- for instance, I can't "slide" against a heavier object without being slowed down as well, it slows down everything.

I would have expected the first piece of code up there to have *some* effect, yet I can't see any whatsoever.

Do you see anything obviously wrong with what I'm doing there? There's not much in this loop to get wrong, it must be the full concept or my location. I'm doing this within the


        for (int32 j = 0; j < pointCount; ++j)

loop.

Thanks again!

Edit:

Further note, if I put it within the "if (vc->pointCount == 1)" condition, which I assume is where it's specifically spheres colliding, what happens is my objects are allowed to slide into eachother (albeit slowed and jelly-like). This happens if I multiply to make vaMod and vbMod larger or smaller both.

Have you tried setting velocityDamping parameter?

if this


vA*=.5f;
vB*=.5f;

gives desired effect velocityDamping with some adjastment to game code might work.

As for the other snippet, I will test it myself first and comment later.

I'd *love* to do it without going into the engine and tearing stuff up... the issue is, each collision needs to be relative to its other body-- i.e. when BodyA hits BodyB, BodyA's velocity damping is value n... but when BodyA hits BodyC, the damping value would be m.

I have no problem going in an giving each fixture a "strength" attribute to have an effect against the mass in the collisions... where I'm running into trouble is figuring out what to do with it.

It seems like this would be a very good addition to Box2D... isn't the need for this everywhere in games? To me, the need is so universal it almost seems there must be a way to do it, and I'm just not typing in the right google keywords to turn it up!

I've checked it.

You need to change the code in two places. First is SolveVelocityConstraint, but you have to change the code after "// Solve normal constraints" line. Second place is b2ContactSolver::WarmStart.


float strA = 0.1f, strB = 0.9f;
vA -= strA * mA * P;
...
vB += strB * mB * P;

Plus you can experiment with restitution ranging from 0 to 1.

p.s.: it is not a separation phase, it is just a calculation of velocities after collision

p.p.s: and dont forget to remove your previous changes (the one after "// Solve tangent constraints") as it may mess up with the sliding

Alex, thanks for all your help, I really appreciate it.

I must be doing something wrong because I still see no effect at all.

In WarmStart, I modified the point loop like so:


		for (int32 j = 0; j < pointCount; ++j)
		{
			b2VelocityConstraintPoint* vcp = vc->points + j;
			b2Vec2 P = vcp->normalImpulse * normal + vcp->tangentImpulse * tangent;

			//
			// MODIFIEDTAG
			//
			b2Vec2 PModA=P;
			b2Vec2 PModB=P;

			PModA*=.1f; // Fake something just to see effect....

			wA -= iA * b2Cross(vcp->rA, P);
			vA -= mA * PModA;
			wB += iB * b2Cross(vcp->rB, P);
			vB += mB * PModB;
		}

And in SolveVelocity I did this:


		if (vc->pointCount == 1)
		{
			b2VelocityConstraintPoint* vcp = vc->points + 0;

			// Relative velocity at contact
			b2Vec2 dv = vB + b2Cross(wB, vcp->rB) - vA - b2Cross(wA, vcp->rA);

			// Compute normal impulse
			float32 vn = b2Dot(dv, normal);
			float32 lambda = -vcp->normalMass * (vn - vcp->velocityBias);

			// b2Clamp the accumulated impulse
			float32 newImpulse = b2Max(vcp->normalImpulse + lambda, 0.0f);
			lambda = newImpulse - vcp->normalImpulse;
			vcp->normalImpulse = newImpulse;

			// Apply contact impulse
			b2Vec2 P = lambda * normal;

			//
			// MODIFIEDTAG
			//
			b2Vec2 PModA=P;
			b2Vec2 PModB=P;

			PModA*=.1f;


			vA -= mA * PModA;
			wA -= iA * b2Cross(vcp->rA, P);

			vB += mB * PModB;
			wB += iB * b2Cross(vcp->rB, P);
		}

I've confirmed that my A and B objects are what I expect them to be (I'm got an incredibly simple setup with two objects and no gravity) and I'm just moving A toward B with the arrow keys.

Whether I modify those PModA/PModB values seems to make no difference at all-- I can even multiply them by zero. Have I changed it in the complete wrong place?

Thanks again, I do appreciate the time you've put into this. Solving this is the difference between my using Box2D or trying to roll my own, inferior collision system.

Edit to note: I was wrong, I don't see "no" effect... if I make the numbers larger than zero, my circles are allowed to interpenetrate eachother, slowly.

Could you do the inverse, and instead of absorbing impulses, instead add an impulse to the player when the zombie collides with him?

What about directly modifying the velocity of the objects, such that, since you know what the player (and NPC's) velocity is when it's being told to move (ie, pressing arrow up), after the collision, you can see what the velocity is, and manually compensate. So, if you're moving up, and an NPC is pushing you from the left, you know the player's X velocity is supposed to be 0, but it's now much larger, you can compensate and make it smaller. Same with the NPC's since you should know their X and Y velocity would be without external forces.

It's ugly, but it was just something I thought of; something to play around with maybe.

Good luck.

My Gamedev Journal: 2D Game Making, the Easy Way

---(Old Blog, still has good info): 2dGameMaking
-----
"No one ever posts on that message board; it's too crowded." - Yoga Berra (sorta)

Well, sadly, it's starting to look like this is either impossible, or well beyond my capabilities. Any progress I am able to make is immediately negated by a side effect elsewhere. It's strange to me that Box2D doesn't have some way to do or simulate this, because it seems almost a requirement to me, in game dev, to be able to seperate out an object's push energy from its density!

This topic is closed to new replies.

Advertisement