🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Spaceolopy

Started by
86 comments, last by pbivens67 1 year, 5 months ago

Well, I am working on my game after a little break. I have drawn 5 rows and 8 columns of bugs to the screen. however, when I hit the initial bug with a bullet all the bugs vanish when I only want one bug to disappear.

void timer(int val)
{
	int TransitionLimit = 5000;
	AnimationCount++;
	if (AnimationCount >= TransitionLimit * 2)
		AnimationCount = 0;
	if (AnimationCount < TransitionLimit)
	for (float i = -90.0f; i <= 60.0f; i += 20.0f)
	{
	drawBug(i, 90.0f, 12, 0);
	drawBug(i, 70.0f, 12, 0);
	drawBug(i, 50.0f, 12, 0);
	drawBug(i, 30.0f, 12, 0);
	drawBug(i, 10.0f, 12, 0);
	}
	else
	for (float i = -90.0f; i <= 60.0f; i += 20.0f)
	{
	drawBug(i, 90.0f, 8, 0);
	drawBug(i, 70.0f, 8, 0);
	drawBug(i, 50.0f, 8, 0);
	drawBug(i, 30.0f, 8, 0);
	drawBug(i, 10.0f, 8, 0);
	}
	glutPostRedisplay();
	glutTimerFunc(10000, timer, 0);
}

void coll_ship_one()
{
	//draw bullet
	float x = -10.0f + move_x;
	float y = -90.0f + bullet;
	float oWidth = 5.0f;
	float oHeight = 5.0f;
	//draw bug
	float xTwo = -10.0f;
	float yTwo = 90.0f;
	float oTwoWidth = 10.0f;
	float oTwoHeight = 10.0f;

	if (checkCollide(x, y, oWidth, oHeight, xTwo, yTwo, oTwoWidth, oTwoHeight) == 1)
	{
		coll = 1;
		coll_count++;
//	cout << coll << " " << coll_count << endl;
		if (coll_count >= 1)
		{
			coll_count = 1;
		}
		cout << coll << " " << coll_count << endl;
		glutPostRedisplay();
	}
}

void renderScene()
{
	srand(time(NULL));
	glClear(GL_COLOR_BUFFER_BIT);
	drawBullet();
	coll_ship_one();
	go_space();
	if (coll == -1)
	{
		timer(0);
	}
	if (coll == 1 && coll_count == 1)
	{
		drawCollision(-10.0f, 90.0f, 9, 0);
		coll = 0;
	}
	if (coll == 0 && coll_count >= 1)
	{
		// no bug
	}
	ship();
	drawDice();
	glFinish();
}
 
Advertisement

I'd suggest grouping the bugs in an array, or a vector, or a list. Each bug should have its own variable to track whether it is alive or dead. Then, make sure you don't draw the bugs that are dead.

Maybe the bugs could be set up as follows. . .

class cBug // cBug = class Bug
{
public:
	cBug();
	~cBug();
	
	void SetLocation(float X, float Y); // this is how we move the bugs
	void OffsetPosition(float OffsetX, float OffsetY); // this bumps the bug in some direction
	void SetAnimationState(int AnimationState);
	
	void Draw();
	
	float GetXLoc();
	float GetYLoc();
	int GetAnimationState(); // tells us about this bug's current animation frame
	bool GetLifeState(); // tells us whether this bug is alive or dead
	void SetLifeState(bool LifeState); // makes this bug alive or dead
	
	bool TestCollusion(float TestX, float TestY); // tests if a point is in collusion with this bug
	
private:
	bool m_bAlive; // tracks whether this bug is still alive
	
	float m_fX_Loc; // tracks this bug's x location
	float m_fY_Loc; // tracks this bug's y location
	
	int m_iAnimationState; // tracks this bug's animation state
	
	// we could put the bug's height and width here (for collusion purposes)
	// or we could assume that all bugs have standard width/height
	// float m_fWidth;
	// float m_fHeight; 
};

Once the bugs have been defined (above), you can set up your bug array as follows. . .

cBug BugArray[8][5];

You'd then want to initialize your bugs, so that they all have the proper starting positions, etc.

And you could draw them as follows. . .

// draw the rows and columns of bugs (if they are alive)
for(int column = 0; column < 8; column++)
{
	for(int row = 0; row < 5; row++)
	{
		if( BugArray[column][row].GetLifeState() ) // if the current bug is alive
		{
			BugArray[column][row].SetAnimationState(AnimationState);
			BugArray[column][row].Draw(); // draws the current bug
		} // end if bug is alive
	} // end for row
} // end for column

I am going to put the bugs in a vector

The same principles should work in a vector.

well, I have drawn 5 rows and 8 columns of bugs, what I want to do is use AABB collision detection to shoot every bug with a bullet. In other words, I want to do collision detection with multiple objects.

void coll_ship_one()
{
	//draw bullet
	float x = -10.0f + move_x;
	float y = -90.0f + bullet;
	float oWidth = 5.0f;
	float oHeight = 5.0f;
	//draw bug
	float xTwo = -10.0f;
	float yTwo = 90.0f;
	float oTwoWidth = 10.0f;
	float oTwoHeight = 10.0f;

	if (checkCollide(x, y, oWidth, oHeight, xTwo, yTwo, oTwoWidth, oTwoHeight) == 1)
	{
		coll = 1;
		coll_count++;
		if (coll_count >= 1)
		{
			coll_count = 1;
		}
		cout << coll << " " << coll_count << endl;
		glutPostRedisplay();
	}
}
   

pbivens67 said:
what I want to do is use AABB collision detection to shoot every bug with a bullet. In other words, I want to do collision detection with multiple objects.

Now we know what you want, but we don't know what is your problem to get there. So again this is no question.

However, i did some little collision detection demo recently here: https://www.gamedev.net/forums/topic/713512-what-kind-of-coding-standard-do-you-prefer/?page=6

It's about circles, no boxes. And for 40 objects vs. some bullets you can do a simple brute force approach.
But there is also a 2D AABB class inside:

				struct AABox 
				{
					vec2 minmax[2];
	
					void Init ()
					{
						minmax[0] = vec2 (FLT_MAX, FLT_MAX);
						minmax[1] = vec2 (-FLT_MAX, -FLT_MAX);
					}

					void Extend (const AABox &b)
					{
						minmax[0][0] = min (minmax[0][0], b.minmax[0][0]);
						minmax[0][1] = min (minmax[0][1], b.minmax[0][1]);

						minmax[1][0] = max (minmax[1][0], b.minmax[1][0]);
						minmax[1][1] = max (minmax[1][1], b.minmax[1][1]);
					}

					vec2 Center () const
					{
						return (minmax[0] + minmax[1]) * 0.5f;
					}

					bool BoundsPoint (const vec2 point) const
					{
						for (int i=0; i<2; i++)
						{
							if (point[i] < minmax[0][i] || point[i] > minmax[1][i])
								return false;
						}
						return true;
					}

					bool IntersectsBox (const AABox &box) const
					{
						for (int i=0; i<2; i++)
						{
							if (box.minmax[1][i] < minmax[0][i] || box.minmax[0][i] > minmax[1][i])
								return false; // disjoint
						}
						return true; // intersecting
					}

					static AABox& Empty()
					{
						static AABox empty = {vec2 (FLT_MAX, FLT_MAX), vec2 (-FLT_MAX, -FLT_MAX)};
						return empty;
					}
				};

Which might be useful in case your problem is the intersection test.

vec2 minmax[2] could be rewritten as:

float minimumCorner[2]; // x and y of the (usually) top left corner of the box
float maximumCorner[2]; // x and y of the (usually) bottom right corner of the rectangle

or, more like i did here actually: float minAndMaxCorners[2][2];

With that you should be able to port the IntersectsBox() function to your code.

Notice your actual definition is corner and width / height, which differs from my definition of two corners.
But otherwise the test is easy.
The basic idea in 1D is:

If the right (max) of line segment A is on the left side (negative direction) from the left (min) of line segment B, there can't be any overlap and the lines are separated.
If not, we need to check the other side as well:
If the left (min) of line segment A is on the right side (positive direction) from the right (max) of line segment B, there can't be any overlap and the lines are separated.
If not, we know the line segments overlap and so intersect.

In 2D it's the same. We do this test just 2 times, once for x direction and once for y:

We project the boxes to x and y axis to get 1D line segments.

Then we find they overlap on y axis, so there may be an intersection.
But after doing the same on x axis, we see they do not overlap, so no collision.

Now i hope the intersection test actually is your problem. Otherwise i've wasted quite some time, because again you failed on asking a proper question, Phil!

@JoeJ here is my collision detection function

bool checkCollide(float x, float y, float oWidth, float oHeight, float xTwo, float yTwo, float oTwoWidth, float oTwoHeight)
{
	// AABB 1
	float x1Min = x;
	float x1Max = x + oWidth;
	float y1Max = y + oHeight;
	float y1Min = y;

	// AABB 2
	float x2Min = xTwo;
	float x2Max = xTwo + oTwoWidth;
	float y2Max = yTwo + oTwoHeight;
	float y2Min = yTwo;

	// Collision tests
	if (x1Max < x2Min || x1Min > x2Max) return false;
	if (y1Max < y2Min || y1Min > y2Max) return false;

	return true;
}

Looks good. I guess it works. ; )

However, there are issues if we think further, meaning the big goal of having a general and reusable code base, so you never need to code a similar collision check for other / future projects.
I'm convinced that's the topic where you need to progress, as you know.

So let's to some trail of observations and conclusions from what you currently have:

bool checkCollide(float x, float y, float oWidth, float oHeight, float xTwo, float yTwo, float oTwoWidth, float oTwoHeight) // observation: this is rather specific, but not general
{
	// AABB 1
	float x1Min = x;
	float x1Max = x + oWidth; // observation: we constantly have to do this addition, which feels inefficient. 

We could fix those issues this way:

bool CheckAABoxOverlap (	float box1minX, float box1minY, float box1maxX, float box1maxY, 
							float box2minX, float box2minY, float box2maxX, float box2maxY ) {...};
// usecase:

bool collides = CheckAABoxOverlap (bugA.x, bugA.y, bugA.x + bugA.width, bugA.y + bugA.height, bugB...);
  

The amount of instructions remains the same, but still we get advantages on the long run:
The function name is more general. Now we can use it e.g. to test if one GUI window overlaps another, which is the same problem, but has nothing to do with physics or ‘collisions’.
The function parameters names make clear what they mean, so we don't need to read the code to figure out if ‘x’ means a corner or a center, for example.
Plus: The potentially redundant additions are gone from the function. The caller is responsible to do this, in case it's needed. That's important, because we have multiple conventions to define a box:
* Min and max corners (ideal for overlap tests)
* center with width and height (ideal to calculate separation to resolve a collision)
* corner with width and height (not ideal for anything, but maybe the first convention coming to peoples minds)
Because we want a general and reusable function, we can not make assumptions on which convention is currently used by the caller. So we use the ideal convention for the function, and leave the conversation to the caller.
Eventually we additionally provide tooling functions to help with conversations.

So, while writing code, you always should think about such questions. It's important to minimize complexity on ever growing projects. You have to optimize for both the CPU and for yourself.

The next step then is the realization that there are many function params.
Using a vec2 class would help a lot already, and after that some Axis Aligned Box class is the next step. I mean, we use boxes often, so it's likely worth it to make one.

After that you could use this box class in multiple ways, such as:

class Sprite
{
	AABox box;
};

// or

class Sprite : AABox
{
	Texture tex;
};

// or

std::vector<Sprite> sprites;
std::vector<AABox> boxes;
int spriteIndex = 5;
RenderSprite (boxes[spriteIndex], sprites[spriteIndex]);

You know, anything that gives you some organisation and avoids large numbers of global variables such as playerX and playerY.
Structure instead Spaghetti code… ; )

@pbivens67 It's good to see that you're still working on this!

Hopefully I'll get to see what a Spaceolopy looks like soon.

This topic is closed to new replies.

Advertisement