code questions (explain what this code does)..

Started by
7 comments, last by Zahlman 17 years, 8 months ago
Hey guys, reading Sam's TY Game Programming in 24 hours. It's not a bad book, but as I delve into the higher chapters (on ch. 11), I find that some of the code is not explained; and even if it's explained, sometimes I'm confused. I realize he has his own site where people can ask questions... I tried that last summer and got my questions answered after waiting for too long. This summer, I thought I'd try asking here, since most people are friendly and willing to explain things without calling you stupid. Btw, I AM a comp. sci. major but when it comes to game programming, I sometimes miss the simplest things. >:( I'll start with question 1. Guess I'll edit this post to keep adding more questions, unless it starts looking messy. Here we go.

//CalcCollisionRect() method calculates the collision rectangle of a sprite by //subtracting one-sixth of the sprite's size off the position rectangle.
inline void Sprite::CalcCollisionRect()
{
  int iXShrink = (m_rcPosition.left - m_rcPosition.right) / 12;
  int iYShrink = (m_rcPosition.top - m_rcPosition.bottom) / 12;
  CopyRect(&m_rcCollision, &m_rcPosition);
  InflateRect(&m_rcCollision, iXShrink, iYShrink);
}



Ok, so I read the 'explanation' but I don't know why we're doing what we're doing. If anyone wants to explain this in the utmost basic terms, that'll be excellent. (Also, why are we subtracting left from right, and then top from bottom?) TIA. Answered :) -----------------------EDIT-------------------------- Ok, thanks for answering the top questions guys. I'm onto another question here, I'm hoping you guys can explain in simple terms like before. I read this chapter a week ago, and not sure if he didn't explain this piece of code in the book or if I can't find it at this moment. Anyway, here it is:

inline BOOL Sprite::TestCollision(Sprite* pTestSprite)
{
  RECT& rcTest = pTestSprite->GetCollision();
  return m_rcCollision.left <= rcTest.right &&
         rcTest.left <= m_rcCollision.right &&
         m_rcCollision.top <= rcTest.bottom &&
         rcTest.top <= m_rcCollision.bottom;
}


//GetCollision() is an accessor method:
RECT&   GetCollision()            
{ 
  return m_rcCollision; 
};


TIA. [Edited by - sspeedy on July 24, 2006 10:36:39 PM]
Advertisement
The idea is to make the collision rectangle smaller than the rectangle for the sprite itself because the actual object usually doesn't fill the rectangle of the sprite (take Link from Legend of Zelda, for example: the sprite is a square/rectangle, but Link himself isn't, so there's areas of the sprite that are transparent). This helps to avoid situations where there's a near miss that counts as a hit. It also creates situations where a near hit (or graze hit) is counted as a miss, but that's the price you pay for using a quick collision detection system like this.

You subtract left from right and top from bottom because you want to send negative numbers to the InflateRect funtion so it SHRINKS instead. Bit of a hack, really, and something that should have been spelled out in the book, but oh well.

Not sure how 12 ends up getting you 1/6th of the sprite size, but that's the problem with "magic numbers" in general.

CopyRect is pretty simple, it's copying the sprite's rectangle to the collision rectangle (altough the to/from order seems odd).

InflateRect would normall make a rectangle larger, but since we're passing negative numbers to it, it shinks the rectangle.

Hope that helps!
The code takes the bounding rectangle for the sprite and shrinks it to form a collision rectangle. This is done to make for more realistic collision detection.

In the "diagram" below, we have a (crappy) ship in it's bounding rectangle. Notice that there is a lot of empty space in the rectangle. If we used the bounding rectangle for collision detection, a hit would be detected, even if an enemy shell passed through the empty space.

By shrinking the bounding rectangle, we reduce the empty space and get better looking collision detection. We risk missing some collisions (notice the wing tips are no longer in the rectangle), but it cuts down on complaints of "Hey! That guy never touched me!"

Original bounding rectangle:+-------+|   A   ||  AAA  ||  AAA  ||A AAA A|| AAAAA ||  A A  |+-------+Shrunken collision rectangle:+---+| A ||AAA||AAA||AAA|+---+


edit - tooooooo slllooooooowwww.
Quote:Original post by sspeedy
Hey guys,
reading Sam's TY Game Programming in 24 hours. It's not a bad book


Yes it is. I can tell by the title.

Quote:
Ok, so I read the 'explanation' but I don't know why we're doing what we're doing. If anyone wants to explain this in the utmost basic terms, that'll be excellent. (Also, why are we subtracting left from right, and then top from bottom?)


The 'right' > the 'left', so left - right is the negative of the width. Similarly, top - bottom is the negative of the height.

We copy the position rectangle into the collision rectangle, and then "inflate" the rectangle by -1/12 the width on each side, i.e. deflate it by 1/12 the width on each side, i.e. reduce the width by 2 * 1/12 = 1/6. Similarly top and bottom. Net result, the collision rect has the same centre but is scaled down slightly.
Quote:CopyRect(&m_rcCollision, &m_rcPosition);
InflateRect(&m_rcCollision, iXShrink, iYShrink);


WTF? What sort of C++ programmer would write this instead of operator= and a member function? Oh, that's right, a bad one. Burn that book now!
Quote:Original post by sspeedy
It's not a bad book


You're right, it's downright /horrible/. I would not ever recommend a Sam's book.

So, play by play breakdown:

int iXShrink = (m_rcPosition.left - m_rcPosition.right) / 12;
left - right = width, assuming left > right, or -width, assuming right > left (more likely).

int iXShrink = -width / 12;
Pretty self explanitory now, iXShrink = 1/12th the width of m_rcPosition.
int iYShrink = (m_rcPosition.top - m_rcPosition.bottom) / 12;
Same thing here, only top - bottom = height, assuming top > bottom, or -height, assuming bottom > top ("screen coordinates", again, more likely).
int iYShrink = -height / 12;

As this code would look using industry::retangle< int > (personal library):

int iXShrink = - m_rcPosition.width() / 12;
int iYShrink = - m_rcPosition.height() / 12;

Really weird to use a rectangle to directly store a position, though.

Next:
CopyRect(&m_rcCollision, &m_rcPosition);
Copies m_rcPosition to m_rcCollision. As the sane would write it:
m_rcCollision = m_rcPosition;
Betchya $20 that this compiles as-is with no other modifications to your code and does the exact same thing.

InflateRect(&m_rcCollision, iXShrink, iYShrink );
"Inflates"/grows the rectangle by 2*iXShrink and 2*iYShrink. Since these are negative, it really shrinks the rectangle. Equivilant to:

m_rcCollision.left -= iXShrink;
m_rcCollision.right += iXShrink;
m_rcCollision.top -= iYShrink;
m_rcCollision.bottom += iYShrink;

(assuming right > left && bottom > top).

Since i[X/Y]Shrink are negative, this means we're adding to left and subtracting from right (shrinking width), as well as adding to top and subtracting from bottom (shrinking height).
thanks everyone for the help. it seems much clearer now.. I was wondering how it shrinks if InflateRect is suppose to INFLATE the rectangle, as the name implies, haha.

aw, it's probably not the best book, but all game programming books seem to have a few flaws. I just want to follow through and finish all of the lessons and get a demo setup soon 'nuff.

anyway, I will edit the original post for another question, or two, depends ;)
bumping this... I know you guys can explain the code; I posted a new question after the Edit.

I just don't want to make new posts per question and look like an ill-mannered punk. Anyway, read the original post, after the edit. TIA.
Quote:Original post by Deyja
WTF? What sort of C++ programmer would write this instead of operator= and a member function? Oh, that's right, a bad one.


Or, you know, the fine people at Microsoft who did all of this stuff for C originally. But C++ programmers ought to be able to obtain a good wrapper for this instead (or failing that, write their own). (Actually, from a quick glance at MSDN, it looks like there is already such a thing done in-house: CRect.)

Quote:Burn that book now!


I'll agree on that one. :)

For the second question, starting a new thread actually probably would have been preferred :/ But: basically, each Sprite object contains a collision rect m_rcCollision like in the first snippet; the author is using the member directly for the this-object Sprite, but calling an accessor function to get the collision rect of the passed-in Sprite. (This is unnecessary; he could equally have written "RECT& rcTest = pTestSprite->m_rcCollision;".) The code creates a reference to the other sprite's collision rect: that avoids copying the rect data across (that's *sort of* a premature optimization because we don't need to change that rect; but then, *passing* a rect by const reference instead of by value would be seen as perfectly OK, even preferred, and this is basically the same thing) but also doesn't require using pointer syntax to get at the data. Again, the same as passing a parameter by reference. (By the way, there is absolutely no reason for passing the target Sprite in by pointer; it should be passed by reference instead. Clearly the author knows about references, so this is a minor WTF in itself.)

The function will test to see if the two collision rects intersect. Writing this code is bad because the library that provides RECT, CopyRect and InflateRect - i.e. the Windows API - also provides IntersectRect which does basically the same thing. (Actually, it will additionally generate the intersection region if there is any, but that is a nearly-free side effect of looking for the intersection.)

But to explain the workings of it: for two rectangles to be intersecting, they have to have some overlap in both horizontal and vertical directions. To test overlap in the horizontal direction, we test that the left of rect A is left of (i.e. less than) the right of rect B (if it were to the right, then A would be entirely to the right of B, and there would therefore be no intersection) AND, similarly, that the left of rect B is left of (less than) the right of rect A.

The code therefore makes four such checks, two for each dimension. Since loose inequality is used (<=), rectangles which touch on the edges or corners will also be considered to intersect.

This topic is closed to new replies.

Advertisement