# Is this as hard as I am making it....trying to bounce a ball off of four walls

This topic is 4438 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

I have a feeling I am making this much harder then it. My main goal hear is to simulate a ball bouncing in a square. I am doing this in a console window using a 2-D array as the grid and simply trying to display a O as the ball. I thought I had it but apparently I did not. Hear is the source...
#include <stdio.h>
#include <windows.h>
#include <iostream>
#include <cstdlib>

using namespace std;

const int SIZE_X = 80;
const int SIZE_Y = 24;
void getInput();
void gotoxy(int& x, int& y);
void moveBall();
void ballDirection(int& x, int& y, int& direction);
void bounceBall(int& x, int& y, int& direction);

int x = 0;
int y = 0;
int dx = 0;
int dy = 0;
int direction = 0;

int main()
{
getInput();
moveBall();
system("PAUSE");
return 0;
}

void getInput()
{
cout << "Please enter starting X: ";
cin >> x;
cout << "Please enter starting y: ";
cin >> y;
cout << "\n\nChoose number to set ball into motion. \n"
<< "1 = up/left\n"
<< "2 = left/down\n"
<< "3 = down/right\n"
<< "4 = right/up\n"
<< "Enter number now: " << flush;
cin >> direction;
}

void gotoxy(int& x, int& y) {
HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
COORD position = {x, y};
SetConsoleCursorPosition(hStdout, position);
}

void moveBall()
{
int i = 0;
for(int i = 0; 1 < 2000; i++)
{
x += dx;
y += dy;
ballDirection(x,y,direction);
bounceBall(x,y,direction);
gotoxy(x,y);
cout << "0";
i = i + 1;
Sleep(10);
}
}

void ballDirection(int& x, int& y, int& direction)
{
switch(direction){
case 1:	dx = -1; dy = -1; break;	//up/left
case 2:	dx = -1; dy =  1; break;	//left/down
case 3:	dx =  1; dy =  1; break;	//down/right
case 4:	dx =  1; dy = -1; break;	//right/up
default: dx = 0; dy = 0; break;
}
}

void bounceBall(int& x, int& y, int& direction)
{
if(y == 0  && direction == 1)				//hits top and is moving up/left
{
if (x == 0)								//hits top left corner
{
direction = 3;
}
else
{
direction = 2;
}
}
else if(y == 0 && direction == 4)			//hits top and is moving up/right
{
if (x == SIZE_X - 1)					//hits top right corner
{
direction = 2;
}
else
{
direction = 3;
}
}
else if(x == SIZE_X - 1 && direction == 3)	//hits right and is moving down/right
{
direction = 2;
}
else if(x == SIZE_X - 1 && direction == 4)	//hits right and is moving up/right
{
direction = 1;
}
else if(y == SIZE_Y - 1 && direction == 3)	//hits down and is moving down/right
{
if (x == SIZE_X - 1)					//hits down right corner
{
direction = 1;
}
else
{
direction = 4;
}
}
else if(y == SIZE_Y - 1 && direction == 2)	//hits bottom and is moving left/down
{
if (x == 0)								//hit down left corner
{
direction = 4;
}
else
{
direction = 1;
}
}
else if(x == 0 && direction == 2)			//hits right and is moving left/down
{
direction = 3;
}
else if(x == 0 && direction == 1)			//hits right and is moving up/right
{
direction = 4;
}
}

As of now the ball or O for that matter does not move at all. I can't catch were my logic went wrong. Also any suggestion on how to clean things up would be greatly appreciated. Thanks Chad [Edited by - chadsxe on September 20, 2006 10:41:22 AM]

##### Share on other sites
If I were to do this starting from scratch, I would make my ball have four variables: X, Y, dX, and dY. The dX and dY stand for delta-X and delta-Y, where "delta" is a term frequently used in math to indicate change. In this case, it indicates how much the ball moves in each direction per unit of time (whatever you choose that unit to be, such as 1 second, 1 millisecond, 1 frame, and so forth).

When you update the ball's position, you don't need any if statements. Just add dX to X, and add dY to Y (dX and dY can be negative, thus moving in the opposite direction). When you detect that the ball is moving beyond a wall, then if it is a horizontal wall, just negate the dY, and if it is a vertical wall, just negate the dX. There is a little more work to do if you want to get perfect accuracy, which basically involves figuring out how far into the time step the ball collided with the wall, and adjusting it's position appropriately, but to get the basics working, you can completely ignore this.

Note that this will also allow you to use essentially any direction for your ball, rather than being limited to just a few. A dX/dY pair of (1, 0) means it is moving right one unit per time unit. A pair of (-3, 2) means it is moving to the left and up (or down, depending on which way you have positive-Y pointing) at around 56 degrees from the horizontal. If you use floating point values for your X, Y, dX, and dY then you can get even more accuracy. (Doing it this way, keep everything as floating point until you need to draw it to the screen; then just round it at that step. This will significantly reduce cummulative rounding errors over long periods of time.)

##### Share on other sites
I totally agree with Agony.

Though for the particular code written:
In your bounceBall function you modify direction, but since direction is not passed by reference this has no effect. The same problem also affects moveBall.

##### Share on other sites
Oh my god.

1) Don't #define constants. Use real constants.
2) Don't use (void). Use ().
3) Use +=, -= etc. for simple modifications to variables.
4) Don't represent a direction as a code, recalculate a code upon reflection and then try to decode the value into actual direction components for update. Just represent the direction as the components of the direction.
5) Don't write comments like "prototype for ...". Of course it's a prototype. The reader of your source code is assumed to know the language you're using. Comments are for explaining why you're doing what you're doing. You want the "what" at a conceptual level, not a code level.
6) But don't prototype anyway unless you have to.
7) Don't use names like "StartX" for something that's going to change over time.
8) Don't define a structure just for a character.
9) Use character literals. Don't try to remember ASCII values. You make things harder for yourself and the person reading your code, for exactly zero benefit.
10) Don't use a do-while loop for a *counted* number of iterations. Use a for loop.
11) You don't need to make separate functions for really simple things (unless they're going to be reused). And as you apply the above principles, you will find that things are generally much simpler than you made them out to be before.
12) Don't use globals if you don't need them. When you pass parameters around (you did this correctly), you don't need the globals, because you already have access to the value via the parameter (and in fact, the parameter will "shadow" a global of the same name, so that you need scope resolution to access the global). By the way, displayGrid didn't need or use the x/y parameters you fed it.
13) Don't abuse endl, and do understand that nothing stops you from outputting more than one line of text in the same statement.
14) Don't use function-calling for flow control: if you call X from Y, it should be because Y is logically a part of the X task. If your main loop has to do X, then Y, then Z, then DO NOT call Y at the end of X, and Z at the end of Y. Instead - I hope this doesn't sound too obvious - call X, then call Y, then call Z, all in the main loop.
15) Don't declare your for-loop variables "outside". The proper idiom is to declare and initialize them within the for statement itself.

#include <iostream>#include <windows.h>#include <cstdlib>using namespace std;void getInput(int& x, int& y, int& dx, int& dy)// Please note the &'s! They are important!// We are passing by reference, so that we can get "out parameters".{	cout << "Please enter starting X: ";	cin >> x;	cout << "Please enter starting Y: ";	cin >> y;	cout << "\n\nChoose number to set ball into motion.\n"	     << "1 = up/left\n"	     << "2 = left/down\n"	     << "3 = down/right\n"	     << "4 = right/up\n"	     << "Enter number now: " << flush;	// Now we'll convert the "direction" into dx and dy:	int direction;	cin >> direction;	switch (direction) {		// This is why you need semicolons in C++ - because it's useful:		case 1: dx = -1; dy = 1; break;		case 2: dx = -1; dy = -1; break;		case 3: dx = 1; dy = -1; break;		case 4: dx = 1; dy = 1; break;		default: dx = 0; dy = 0; break;	}}int main(){	const int X_SIZE = 20;	const int Y_SIZE = 20;	int x, y, dx, dy; // position and direction	// We want to "pass back" multiple values. We can do it by using	// reference parameters:	getInput(x, y, dx, dy);	for (int i = 0; i < 50; ++i)	{		char arrayGrid[X_SIZE][Y_SIZE] = {0};		// Here's all of 'moveBall':		x += dx;		y += dy;		arrayGrid[x][y] = 'O';		// Here's all of 'bounceBall':		if (x == 0 || x == X_SIZE - 1)		// Notice the -1!!! That fixes an out of bounds problem you had.		// Of course, simplifying the logic also fixes other problems		// that you had with cases that weren't fully considered.		{			// Then we hit an edge, and need to reflect in X			dx *= -1;		}		if (y == 0 || y == Y_SIZE - 1) // similarly.		{			dy *= -1;		}		// Here's all of 'displayGrid':		for (int y = 0; y < Y_SIZE; ++y)		{			for (int x = 0; x < X_SIZE; ++x)			{				cout << arrayGrid[x][y];			}			cout << "\n";		}		cout << flush; // remember, you need to flush the buffer for		// the text to actually appear.		::Sleep(100);	}}

If I didn't somehow fix the problem accidentally, all I can suggest is to use a debugger. But seriously, stop whatever it is you're doing and learn some actual programming practice.

##### Share on other sites
Quote:
 If I didn't somehow fix the problem accidentally, all I can suggest is to use a debugger. But seriously, stop whatever it is you're doing and learn some actual programming practice.

I'm getting tired of this response. Beginning programmers don't know how to program. If they did, they wouldn't be beginners. This is all great advice, but you have to learn to crawl before you can walk.

It's obvious by his code (not to mention the simple "ball bouncing" exercise) that he is brand new to all of this. Have you forgotten the shitty code you wrote when you first started? Or did you start out as the programming Deity that you are now? Or did you start so long ago that the languages were much lower level and therefore there was only one way to do things?

To the OP, Agony's response was the best way to handle this situation. Give it a shot, the code will be much smaller and more flexible!

##### Share on other sites
Quote:
Original post by JBourrie
Quote:
 If I didn't somehow fix the problem accidentally, all I can suggest is to use a debugger. But seriously, stop whatever it is you're doing and learn some actual programming practice.

I'm getting tired of this response. Beginning programmers don't know how to program. If they did, they wouldn't be beginners. This is all great advice, but you have to learn to crawl before you can walk.

Quote:

Wherein I am considerably nicer. If you want to be coddled, you should post where such is expected. It's at the top of the forum list for a reason: so that beginners will notice it.

In any event, lots of beginners (I would conjecture the vast majority of them) are "learning" from horrible sources, and do in fact need to stop whatever it is they're doing and learn some actual programming practice. Sometimes when people are headed up a blind alley, they need to hear this kind of thing so that they will consider backing out.

And FWIW, I consider myself very lucky as regards my early programming education. From age 7 until 9 or so, I wrote all kinds of shitty code (it was in various BASIC dialects - and quite basic ones at that - and I had no real ideas or direction, so it was only to be expected). After that, I got instruction from an excellent tutor who I think was also a university TA at the time. From ages 9 till 12 or so, I still wrote shitty code (with gradually diminishing frequency), but it was normally only permitted to exist on screen for a matter of seconds; my instructor would point at the screen and give me this look that said I should know better, and probably 80 or 90 percent of the time I *did*, and fixed it; otherwise I learned something. In this kind of environment you can learn very quickly. Compassion is a nice quality (maybe less in-demand for those with greater natural aptitude; insert autism-spectrum-disorder theories here), but when it comes to teaching *programming*, I sincerely believe that you need to be very strict, or things won't improve.

Peter (I strain my memory for the name, and might be wrong), if you're out there - thank you.

##### Share on other sites
Quote:
 If you want to be coddled, you should post where such is expected. It's at the top of the forum list for a reason: so that beginners will notice it.

...and I'm getting tired of this response.

Zahlman, you are clearly an asset to many on this and other forums. You seem to have boundless time, energy and enthusiasm to help people out. It's just sometimes your approach can be intimidating and unconstructive - though I have no doubt you intend neither.

Clearly, the fact that the beginners forum is at the top of the list is not working as an obvious signpost for beginners to post in. There's little point in stating it again and again - it doesn't matter how unobservant you think people are. It just seems excessive that your punishment for the "crime" of mis-posting is the full extent of your wrath.

Quote:
 Compassion is a nice quality (maybe less in-demand for those with greater natural aptitude; insert autism-spectrum-disorder theories here), but when it comes to teaching *programming*, I sincerely believe that you need to be very strict, or things won't improve.

Is this based on an intimate and professional understanding of how people learn best? I do know something about this... and people learn and respond to "teaching" in different ways. Assuming you don't know the OP personally, you have no idea whether your method of critique of his code was constructive or not.

These forums are a real asset to new and experienced programmers alike. They are less of an asset if people feel intimidated by the few like yourself that stand astride them with such presence.

I would hate for you to see the above as anything other than constructive! I am amongst your many fans for much of what you post. It's just a pity if an occasional rush of blood to the head - or whatever it is and however intentioned - can spoil it all!

##### Share on other sites
I just want to say that I greatly appreciate everyones advice. From the the advice about including a delta all the way to the complete beat down by Zahlman, i can't thank you all enough. Zahlman, you hit it on the head. I have always suspected my learning sources to be sub-par. I have never once really been face to face with another programmer (let alone a good) and have had the ability to pick there brain. I have so much to learn and I do realize that you were only trying to help. blairhartley & JBourrie
, thanks for the kind words and advice. Anyways, I tried some new things and have come up with this....
#include <stdio.h>#include <windows.h>#include <iostream>#include <cstdlib>using namespace std;const int SIZE_X = 80;								const int SIZE_Y = 24;								void getInput();	void gotoxy(int& x, int& y);void moveBall();void ballDirection(int& x, int& y, int& direction);void bounceBall(int& x, int& y, int& direction);int x = 0;										int y = 0;											int dx = 0;int dy = 0;int direction = 0;								int main() {	getInput();	moveBall();        system("PAUSE");        return 0;} void getInput(){	cout << "Please enter starting X: ";	cin >> x;	cout << "Please enter starting y: ";	cin >> y;	cout << "\n\nChoose number to set ball into motion. \n" 	     << "1 = up/left\n" 		 << "2 = left/down\n" 		 << "3 = down/right\n" 		 << "4 = right/up\n" 		 << "Enter number now: " << flush;	cin >> direction;}void gotoxy(int& x, int& y) {        HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);        COORD position = {x, y};        SetConsoleCursorPosition(hStdout, position);}void moveBall(){	int i = 0;	for(int i = 0; 1 < 2000; i++)	{		x += dx;		y += dy;		ballDirection(x,y,direction);		bounceBall(x,y,direction);		gotoxy(x,y);		cout << "0";		i = i + 1;		Sleep(1000);	}}void ballDirection(int& x, int& y, int& direction){	switch(direction){		case 1:	dx = -1; dy = -1; break;	//up/left		case 2:	dx = -1; dy =  1; break;	//left/down		case 3:	dx =  1; dy =  1; break;	//down/right		case 4:	dx =  1; dy = -1; break;	//right/up		default: dx = 0; dy = 0; break;	}}void bounceBall(int& x, int& y, int& direction){	if(y == 0  && direction == 1)				//hits top and is moving up/left	{			if (x == 0)								//hits top left corner		{			direction = 3;		}		else										{			direction = 2;		}	}	else if(y == 0 && direction == 4)			//hits top and is moving up/right	{		if (x == SIZE_X - 1)					//hits top right corner		{			direction = 2;		}		else 		{			direction = 3;		}	}	else if(x == SIZE_X - 1 && direction == 3)	//hits right and is moving down/right	{		direction = 2;	}	else if(x == SIZE_X - 1 && direction == 4)	//hits right and is moving up/right	{		direction = 1;	}	else if(y == SIZE_Y - 1 && direction == 3)	//hits down and is moving down/right	{		if (x == SIZE_X - 1)					//hits down right corner		{			direction = 1;								}		else 		{			direction = 4;		}	}	else if(y == SIZE_Y - 1 && direction == 2)	//hits bottom and is moving left/down	{		if (x == 0)								//hit down left corner		{			direction = 4;		}		else		{			direction = 1;		}	}	else if(x == 0 && direction == 2)			//hits right and is moving left/down	{		direction = 3;	}	else if(x == 0 && direction == 1)			//hits right and is moving up/right	{		direction = 4;	}}
There still a lot of improvments to made but I think I am headed in the correct direction. For one I ditched the whole array idea and built a function to move my cursor. This makes things a lot smoother in my eyes. Few questions though....

1. I was trying to clear the screen after the user enters the inital direction. I was shown to include conio and call clrscr(); but for some reason this does not work. The call clrscr() is not recgonized and throws and error.

2. I would like to terminate the program when it again reaches the orignal starting point. This was the reason for the orginal do/while loop. Only thing I can't figure out is how to make the loop start if the opinion perameter is equal to the while condition.

3. My whole bounce ball function seems the least bit elegent. I have it based around 8 specific conditions. One for top, bottom, left, and right bounds, and one for each four corners. I am also trying to check which way the ball is moving as it hits the edges/corners. It actually seems to be working but with a few minor things going wrong. You will notice that as it hits some of the edges it is not deflecting in a perfect smoothly it gets hung up a little then deflects. Also when it hits the bottom right corner it is escaping the bounds I have setup. I have it placing an O in every spot it visits to demonstrate this.

4. Anything else you can tell me on cleaning things up will help a lot. As stated earlier my programming practices suck because I plainly don't know better.

##### Share on other sites
In his defence, I think that Zahlman's attitude was just fine, for the GP forum.

Whenever I've been on the receiving end of such an informed, if blunt, response, I've considered it a godsend. You don't need to lurk these forums for long to realise that Zahlman knows exactly what he is talking about, so anybody who takes his pearls of wisdom as a personal attack don't deserve any help.
chadsxe took it well, so clearly nothing was way out of line.

Pleasantness is a nice seasoning for a short reply, but when presenting that much solid, concise, platinum help, there's really no need. The tone of dismay only serves to drill the importance of the message home.

Regards

##### Share on other sites
Quote:
 Original post by chadsxeThere still a lot of improvments to made but I think I am headed in the correct direction. For one I ditched the whole array idea and built a function to move my cursor. This makes things a lot smoother in my eyes. Few questions though....1. I was trying to clear the screen after the user enters the inital direction. I was shown to include conio and call clrscr(); but for some reason this does not work. The call clrscr() is not recgonized and throws and error.

Is it a compiler error, a link error, or a runtime error? For future reference, this can be very important information when asking a question about an error.

My guess is that this is a compiler error (the first step in building/running an executable, ignoring the preprocessor), because your particular platform and/or IDE doesn't support the clrscr() function. (A brief search on Google suggested as much.) Two alternatives are to use the function system("cls") from <cstdlib>, or use a platform-specific function. Since I see you're using a Win32 API function already (SetConsoleCursorPosition()), I believe you could also easily use ScrollConsoleScreenBuffer() to clear the whole screen by "scrolling" it all off the screen in one single move.

Quote:
 2. I would like to terminate the program when it again reaches the orignal starting point. This was the reason for the orginal do/while loop. Only thing I can't figure out is how to make the loop start if the opinion perameter is equal to the while condition.

Two ways come to mind. You could use a do/while loop:
int start_x = x;int start_y = y;do{	x += dx;	y += dy;	ballDirection(x,y,direction);	bounceBall(x,y,direction);	gotoxy(x,y);	cout << "0";	i = i + 1;	Sleep(1000);} while (start_x != x || start_y != y);

Or you could just remember if this is the first time through or not:
int start_x = x;int start_y = y;bool started = false;while (!started || start_x != x || start_y != y){	x += dx;	y += dy;	ballDirection(x,y,direction);	bounceBall(x,y,direction);	gotoxy(x,y);	cout << "0";	i = i + 1;	Sleep(1000);}

Quote:
 3. My whole bounce ball function seems the least bit elegent. I have it based around 8 specific conditions. One for top, bottom, left, and right bounds, and one for each four corners. I am also trying to check which way the ball is moving as it hits the edges/corners. It actually seems to be working but with a few minor things going wrong. You will notice that as it hits some of the edges it is not deflecting in a perfect smoothly it gets hung up a little then deflects. Also when it hits the bottom right corner it is escaping the bounds I have setup. I have it placing an O in every spot it visits to demonstrate this.

With the dx and dy variables, you no longer need the direction variable at all, and should also be able to automatically handle corner cases when dealing with the ordinary edge cases. Just check for every single case, independent of the others. (By independent, I mean check for them all in their own if statements, rather than have an if-else-else statement where at most one block of code will execute.)
void bounceBall(){	if (x < 0) //The ball has collided and is going through the left wall	{		dx = -dx; //Reverse the horizontal direction		x = 0 + (0 - x); //Handle the extra distance that the ball was going to travel into the wall		//Format is BOUNDARY_VALUE + (BOUNDARY_VALUE - CURRENT_VALUE)		//  or also BOUNDARY_VALUE * 2 - CURRENT_VALUE	}	if (x > (SIZE_X - 1)) //The ball has collided and is going through the right wall	{		dx = -dx; //Reverse the horizontal direction		x = (SIZE_X - 1) + ((SIZE_X- 1) - x); //Handle the extra distance that the ball was going to travel into the wall	}	if (y < 0) //The ball has collided and is going through the top wall	{		dy = -dy; //Reverse the vertical direction		y = 0 + (0 - y); //Handle the extra distance that the ball was going to travel into the wall	}	if (y > (SIZE_Y - 1)) //The ball has collided and is going through the bottom wall	{		dy = -dy; //Reverse the vertical direction		y = (SIZE_Y - 1) + ((SIZE_Y - 1) - y); //Handle the extra distance that the ball was going to travel into the wall	}}

Note that I didn't test this code or anything. If it somehow breaks and manages to kill a kitten, don't blame me. [smile]

Quote:
 4. Anything else you can tell me on cleaning things up will help a lot. As stated earlier my programming practices suck because I plainly don't know better.

A) While with a small program like this it is hardly an issue, it would be preferable to get rid of as many global variables as is possible. In particular, variables like x, y, dx and dy are particular to one thing, rather than being part of the whole program. What if you wanted more than one ball, for example? Or what if some part of your code needed to use some temporary variables that were most appropriately named x and y, but you still needed access to the global x and y?

Being an object-oriented advocate (though hopefully not an obsessive one, they scare me), I would suggest that in this particular case it would be most natural to consider the ball to be an object. Not necessarily because it mimics a physical object, but because it is a very obvious abstraction that can in a sense be separated from the rest of the program, has certain properties, can perform certain actions, and can have certain actions performed on it. Since you're using a few basic C++ features, you might as well consider using classes, one of the most significant C++ features compared to C. A ball would know it's own x, y, dx and dy values, you can tell it to draw, and you can tell it to move, updating its position. This way, you could have as many balls as you want, and could simply loop through each one, telling it to Balls.Move() and Balls.Draw().

B) You don't need to pass global variables to functions by reference, or at all. The functions already can "see" them. Notice that you never pass dx or dy to any function, and yet the functions can manipulate them quite easily. Of course, this was probably a carry-over from your previous version.

Referring back to part A on OOP, I would stick with passing things as parameters to functions (by reference where appropriate) instead of having global variables. But instead of having to pass four variables by reference (x, y, dx, and dy), you could pass just one ball object by reference, making your code look a lot cleaner, as well as a wee bit more efficient (not that it's very important at this stage).

C) Eventually you'll run into the problem where a program runs too fast or two slow on different computers, or gets jerky movement by not consistently running at the same speed while the program runs on a single computer. Once you get to that point, and wish to fix it, then you can look up details on "frame based" versus "time based" movement. Right now, you'll never notice a problem, because your program is very simple, and your sleep time is so large. But once you get things that are running at under 50 or so milliseconds, you might start to notice problems, especially if your motion is of a type that really needs smooth execution to look good.

[Edited by - Agony on September 21, 2006 3:57:20 PM]

1. 1
Rutin
22
2. 2
3. 3
4. 4
5. 5

• 9
• 9
• 9
• 14
• 12
• ### Forum Statistics

• Total Topics
633306
• Total Posts
3011287
• ### Who's Online (See full list)

There are no registered users currently online

×

## Important Information

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!