Lab6 - Moving our graphics

So far, we have been building static pictures using simple graphics routines. Can we make something move? Well of course, but exactly how is a bit of a puzzle. The details we will work through here are not something you need to remember for a test, but they will help you understand how the graphics works a bit better. The only thing you are really responsible for in this work is understanding the simple code I ask you to invent. Hang on - we are in for a ride!

Creating animations

When we want to make something move on the screen, we build up the scene the same way a movie works - one picture at a time. Once we complete a picture, we let it stay on the screen for a short period of time, then redraw the image with the objects we want to see move placed in a new location. If we do this over and over fast enough, we see the image move (assuming we do things right!). This is exactly the way cartoons and even movies were constructed in the old days before computers - a huge series of stationary images flashed on the screen fast enough that our eyes see only motion.

Start of with a simple picture

Using your previous lab projects as a start, set up a simple scene with a single red circle located near the lower left edge of the window. It should look something like this:

../../../_images/BasicAnimation.png

Now, add a few floating point variables to your program at the top of the code. These will be global variables available to any of your code in this file.

#define RADIUS  25
float xPos = RADIUS;
float yPos = RADIUS;
float speed = 0.5;
float deltaX = speed;
float deltaY = speed;

If we place declarations inside of a function, the variable names we define are only visible to code in that one function. These are called local variables. To make variables visible to all code in a file, we place then outside of a function at the top of the file - these are the global variables.

Now, in your drawScene function, change the command that draws the circle to this: `

void drawScene(void) {
    int x, y;
    x = (int) xPos;
    y = (int) yPos;
    ...
    drawFilledCircle(x,y,RADIUS);
    glEnd();
}

Why are we doing this? Well, the animation we are going to create might be too fast if we are not careful, so I am using floating point numbers to calculate the position of the circle using a small floating point adjustment value, and converting the number into an integer when we call the actual circle draw routine. That (int) notation tells C++ to convert the floating point value into an integer. Depending on the speed of your computer, you might need to adjust the value of the speed variable to keep the movement under control.

Making it move

The graphics library we are using is pretty smart. It does a lot of the work to do the animation for us. For instance, we do not really need to go through a lot of work to put the drawing code in a loop to make it move ourselves. Instead, we only need to create a new function that will adjust the location of the circle by adjusting the values of xPos and yPos. We can then ask the graphics library to call this function when it has nothing else to do and then redisplay the scene. Here is the code we need:

void animate() {
    xPos += deltaX;
    yPos += deltaY;
    glutPostRedisplay();
}

Place this code above your main function.

The real magic happens when we tell the library to use this routine. Here is the call we need to add to the main function:

int main(int argc, char ** argv) {
     GraphicsSetup(argc, argv);
     glutDisplayFunc(drawScene);
     glutIdleFunc(animate);
     glutMainLoop();
}

That single call to glutIdleFunc with the name of our animation function as a parameter is all we need to do. Do you see that glutMainLoop function next in the code above? That starts up a routine that sits in a loop looking for events of various sorts to process. Things like key presses, mouse movements, and other things we can control will get the codes attention. When it finds nothing else to do, it calls the idle function that we told it about in our code. This function adjusts the position of the circle, then calls a function to redisplay the circle at its new location.

This style of program is exactly how Windows itself works. The operating system is just sitting there watching all the hardware in your system waiting for anything that needs its attention. It quickly deals with the event, then continues looking for something to do. Neat!

Run the program and see what happens! PacMan anyone?

With any luck, the circle charged off the screen toward the upper right of the window. As it moved.

Your job!

Guess what? You get to see if you can make this ball bounce off of the walls! This is just like the problem we did in Scratch only now we are doing the same thing in a real programming language.

(This is not too hard, so do not get carried away with this!)

It you think about the code above, deltaX and deltaY are controlling the speed and direction that the ball moves. As an example, assume the ball is moving straight to the right. deltaX is positive and deltaY is zero. If the ball approaches the wall, the center of the ball is approaching a point radius away from the wall. We need to make the ball bounce which means, we need for it to move to the left. We can make this happen if we just change the sign of the number in deltaX.

deltaX = -1.0 *  deltaX;

This will do the job. (You can get rid of the minus 1.0 and just use -deltaX as well!)

The same idea applies to each of the other three walls. You flip the sign of the deltaX``or ``deltaY variable as you figure out that the ball has hit the appropriate wall. Think about how you would make the ball move toward each of the walls in turn, and set up an if test to change the ball direction in that case. When you put it all together, I bet you are happy with what you have accomplished!

Start your project by just making the ball move in one direction, then play with the initial values you use for deltaX and deltaY to see how the movement changes. Then focus on making it bounce off of one wall at a time. It will turn out that all you need to do is add four if statements to get this job done.

Look into the graphics.h file and see how the size of the screen is defined. You can use these names in your code to calculate the locations of the edge of the screen.

Turning in this lab

Submit the program file you create, (not the Graphics files) using BlackBoard.