Adding vector from another moving object...

Started by
38 comments, last by mwkenna 12 years, 6 months ago
Alright, now I'm understanding it. SAT is pretty cool stuff!

Here's another quick question...



Check out the image. You can see that in the top pic, block C (cyan block) is on top of blocks A and B and it's being pulled down by a gravity vector. You can also see that the C block is sitting where blocks A and B come together. The problem I'm having in this example is this: If I do an 'A-C' collision check then a 'B-C' collision check in that sequence, block C falls between the two blocks when it moves across B and begins to move across the top of block A (illustrated in the bottom pic). This seems to happen because of the sequence the collision detection schemes are happening (A-C collision check happens before the B-C collision check). Any ideas on how to keep the cyan block from falling between blocks A and B?

Thanks!
"The crows seemed to be calling his name, thought Caw"
Advertisement
that should not be a problem. In your example, if you do A-C, C should be pushed up (MTD going up). If you do B-C, same. So in the end, C will always be pushed up.

if this doesn't happen, it's something wrong with your code (ie, MTD reported being wrong, or pushing the wrong way round).

Everything is better with Metal.

If it's supposed to work, then I'm not sure what I'm doing wrong. Here's my experiment...

bool HitThisBlock(MyQuad *AtkQuad, MyQuad *TgtQuad, bool MakeChg){	bool Hitblok;	float aPos[2], aExt[2], aDir0[2], aDir1[2]; 	float bPos[2], bExt[2], bDir0[2], bDir1[2];	float Disp[2], tFirst, Nfirst[2], MTD[2];	// Preliminary stuff for Box A...	VecCopy(AtkQuad->newLoc, bPos);	bExt[0] = (AtkQuad->entSize*AtkQuad->xScale) * 0.5f;	bExt[1] = (AtkQuad->entSize*AtkQuad->yScale) * 0.5f;	bDir0[0] = AtkQuad->entVerts[1].x - AtkQuad->entVerts[0].x;	bDir0[1] = AtkQuad->entVerts[1].y - AtkQuad->entVerts[0].y;	VecNormalize(bDir0);	bDir1[0] = AtkQuad->entVerts[0].x - AtkQuad->entVerts[3].x;	bDir1[1] = AtkQuad->entVerts[0].y - AtkQuad->entVerts[3].y;	VecNormalize(bDir1);	Disp[0] = AtkQuad->oldLoc[0] - AtkQuad->newLoc[0];	Disp[1] = AtkQuad->oldLoc[1] - AtkQuad->newLoc[1];	// Preliminary stuff for Box B...	VecCopy(TgtQuad->newLoc, aPos);	aExt[0] = (TgtQuad->entSize*TgtQuad->xScale) * 0.5f;	aExt[1] = (TgtQuad->entSize*TgtQuad->yScale) * 0.5f;	aDir0[0] = TgtQuad->entVerts[1].x - TgtQuad->entVerts[0].x;	aDir0[1] = TgtQuad->entVerts[1].y - TgtQuad->entVerts[0].y;	VecNormalize(aDir0);	aDir1[0] = TgtQuad->entVerts[0].x - TgtQuad->entVerts[3].x;	aDir1[1] = TgtQuad->entVerts[0].y - TgtQuad->entVerts[3].y;	VecNormalize(aDir1);	// Do collision detection...	Hitblok = CoolBoxBoxCollision(aPos, aExt, aDir0, aDir1, 				bPos, bExt, bDir0, bDir1, 				Disp, tFirst, Nfirst, MTD);	if (Hitblok && MakeChg)	{		AtkQuad->onGround = true;		if (fabs(MTD[1]) > fabs(MTD[0]))			AtkQuad->entDelta[1] = 0;		if (tFirst == 0)		{			AtkQuad->newLoc[0] = AtkQuad->newLoc[0] + MTD[0];			AtkQuad->newLoc[1] = AtkQuad->newLoc[1] + MTD[1];		}		else		{			AtkQuad->newLoc[0] = AtkQuad->newLoc[0] + Nfirst[0];			AtkQuad->newLoc[1] = AtkQuad->newLoc[1] + Nfirst[1];		//	AtkQuad->newLoc[0] = AtkQuad->newLoc[0] + Disp[0] * tFirst + MTD[0];		//	AtkQuad->newLoc[1] = AtkQuad->newLoc[1] + Disp[1] * tFirst + MTD[1];		}	}	return Hitblok;}


In that 'else' statement, I've been going back and forth between using 'Nfirst' and 'MTD' to see how each behave. If I use the MTD and tFirst, then AtkQuad colliding with TgtQuad on its edge at a high rate of speed will 'slide' it off the edge whereas using 'Nfirst' will stop it dead in its tracks. Here's a crude diagram of what's going on when I use 'MTD and tFirst' vs 'Nfirst'.

Using MTD and tFirst:                      |                    |          ------                      |            Slides  |         | Atk  | ------               |    ------   / up   |         | ---> || Atk  |              |   | Atk  | /  here |         |      || ---> |              |   | ---> |         |          ------|      |    ------    |   |      |------   |          ------ ------    | Tgt  |   |    ------| Tgt  |  |         | Tgt  |           |      |   |          |      |  |         |      |           |      |   |          |      |  |         |      |            ------    |           ------   |          ------Using Nfirst:                      |                    |                      |            SMACK!  | ------               |    ------   /      |   ------| Atk  |              |   | Atk  | /       |  | Atk  || ---> |              |   | ---> |         |  |      ||      |    ------    |   |      |------   |  |      |------ ------    | Tgt  |   |    ------| Tgt  |  |   ------| Tgt  |           |      |   |          |      |  |         |      |           |      |   |          |      |  |         |      |            ------    |           ------   |          ------


Do you see where I might be doing something wrong in the above function? Or do you need to see more of my sloppy code? Thanks for the help, I really appreciate it!
"The crows seemed to be calling his name, thought Caw"

AtkQuad->newLoc[0] = AtkQuad->newLoc[0] + Nfirst[0];
AtkQuad->newLoc[1] = AtkQuad->newLoc[1] + Nfirst[1];

that is in fact wrong.

I think you mis-understand the concept of Nfirst. Nfirst is the normal of the plane of collision, and you use Nfirst to 'bounce' your objects against each other.

MTD, as you know, is just the amount of penetration, and is used correctly.

tfirst is the fraction of displacement required so that the two objects touch.

so, what you want to do is

AtkQuad->newLoc[0] = AtkQuad->newLoc[0] + Disp[0] * tfirst;
AtkQuad->newLoc[1] = AtkQuad->newLoc[1] + Disp[1] * tfirst;

in fact, to be complete, you can just do

	if (Hitblok && MakeChg)	{		AtkQuad->onGround = true;		if (fabs(MTD[1]) > fabs(MTD[0]))			AtkQuad->entDelta[1] = 0;		AtkQuad->newLoc[0] = AtkQuad->newLoc[0] + Disp[0] * tFirst + MTD[0];		AtkQuad->newLoc[1] = AtkQuad->newLoc[1] + Disp[1] * tFirst + MTD[1];	}	return Hitblok;}

Everything is better with Metal.

OK, I think the SAT code is working but the MTD code isn't. Or something else in my demo is messing up the MTD calculation... I can't find where I'm going wrong! [sad] Here's some pics to reference in my explanation of the problem:

Pic 1
Pic 2
Pic 3
Pic 4

In Pic 1, you can see how I'm holding the RIGHT arrow key until the CYAN block collides with the ORANGE one. Since there's no collision yet, there's no MTD value in the upper left corner of the window.

In Pic 2, you can see that while I'm holding the RIGHT arrow key the CYAN block is successfully stopped by the ORANGE block. You'll also see the correct MTD value in the upper left of the window.

In Pic 3, see how I back off the CYAN block one click using the LEFT arrow key (one click = 16 pixels, or, one rapid touch of the arrow key). The MTD display value is saved from the last collision so it still says (-16, 0), but the actual MTD value is (0, 0) since there's no collision happening right now. Then, I move the CYAN block one click to the right again...

Look at Pic 4 and see what happens when I try to move the CYAN block to the right after backing up by one click. The CYAN block jumps down, and the MTD indicating how far down it jumped is shown in the upper left corner of the window.

NOTE: In Pic 3, if you move the CYAN block back by more than one click, the rest of the problem may not happen! The problem can reliably be reproduced by backing up by only one click, but may or may not happen by simply moving the block around randomly.

Since I don't know where in my code I'm getting it wrong, here's all of my code in a VisualCPP project:

mtdProblem (2.14mb)

Heh, the code will look a lot like Microsoft's 'Vertices' demo because most of it is based off it. It should compile if you have at least DirectX9.0c Summer Edition installed. There's a description of the problem in a remark at the beginning of the code, as well as text you can search for to jump to areas in the code where I think the problem may reside. There's also instructions on how to use the program after you compile and get it running (use the arrow keys).

Would any of you guys show me what I'm doing wrong? On paper, it looks like it should work but in this demo it doesn't... If you correct my code, would you remark the old code and label your edits so I know how you got it to work and so I can learn why my code doesn't work? Thanks again for the help!
"The crows seemed to be calling his name, thought Caw"
cool, a bug :)

the condiiton (tfirst > 0.0f) is not sufficient to determine if we have a ntersection or collision. instead, I added a bCollided boolean. I've updated the code.

Everything is better with Metal.

Glad I could help you debug your code! [grin] Anyhoo, I've added the updates and I still get the same result... Move my block against a block, then back up one click, then push against the block again, and my block zings around the other block rather than stops against it.

Is it something in my own code that's causing this problem?

Thanks!
"The crows seemed to be calling his name, thought Caw"
It's messy code. you have entDelta[], oldLoc[], newLoc[],... it's all mixed up, and the control and update code is all over the place.

Effectively, when you do a collision, you move the block twice. If the block is touching, this effectively push it right 16 pixels into the other block, then test, and the MTD return a Y axis with 16 pixels depth as well, so the Y axis gets chosen, and push the quad down.

Use controls to change entDelta[]. Then displacement will be quad's entDelta[].

in case of a collision, move block by

AtkQuad->newLoc[] = AtkQuad->newLoc[] + AtkQuad->entDelta[] * tFirst + MTD[];

Then use Bounce() to make the entDelta[] reflect on Nfirst (or set entDelta[] to 0 in your example).

That's it. newLoc[] can be replaced by a more appropriate Loc[] variable, and oldLoc[] can be removed.

check it out.

I've amended the code a little, but I have yet to update the code I posted previosuly, nor time to update the sample code. There are FP problems sometimes, and it needs careful tweaking, but it should be stable now.

[Edited by - oliii on October 28, 2005 7:36:02 PM]

Everything is better with Metal.

VERY cool! Plus, you've showed me some programming techniques that I wouldn't have thought to use (sending the collision routine a pointer to an array of all my quads). Pretty awesome! I'm still having troubles understanding how the time-based collision detection works (I understand the concept, but not how to make it work in a game), but I'm getting the hang of the rest of it. Thanks for correcting my code, now I can read up and figure out this time-based stuff!

Thanks again, you're my hee-row! [wink]
"The crows seemed to be calling his name, thought Caw"
This truly is an amazing post.

I have been trying to get my head around swept SAT for ages, thanks to the worked examples in this post I finally managed to understand it.

Many thanks,
Mark.

This topic is closed to new replies.

Advertisement