Transformations

[ LiB ]

Transformations are very important in game programming. They are used everywhere that you have movement; they change an image's position or direction. There are three types of transformations: translating, scaling, and rotating. Let's begin with translating.

Translating

When you hear the word translation, you probably think of languages. Well, translating images is completely different! When using translations in game programming, you take an image at its current position and then "translate" it to another position, so translate is just a fancy way of saying move!

Translation is moving an image from one coordinate to another. When complete, translation looks something like Figure 7.1.

Figure 7.1. Translations.


Translation is really simple. You are basically drawing an image at a different position. For example, say you have an enemy ship that moves from the top-left corner of the screen to the bottom-right corner of the screen, and you want the ship to move at five pixels a second. You might have an initialization section that looks like this:

 shipx = 0 shipy = 0 

This just puts the position of the ship at the top-left corner of the screen. We now begin the game loop. Because we are moving the ship diagonally down at 5 pixels a second, we will have to update the ship with some code like this:

 While Not KeyDown(1) Cls DrawImage shipimage, shipx, shipy shipx = shipx + 7 shipy = shipy + 5 Flip Wend 

Let's go through this. We start off with a loop, just like any other game. We first clear the screen, so that we can use page flipping. Then, we draw the ship image. Note that you have a choice where to put your DrawImage command. I chose to put it at the beginning of the loop so that you can see the ship image at (0,0), but you can put it at the end of the loop. The DrawImage command draws the ship image at the set x and y coordinates. When the loop is run through the first time, ship x and ship y are both set equal to 0, but this changes with the next line of code. This line adds 7 to shipx and 5 to shipy . Because the additions occur each frame, the ship moves 7 pixels to the right and 5 pixels down each frame. Figure 7.2 might help clear up the coordinate positions for youit is a table of the values and the frame number. The equation, written next to the table in the figure, allows you to determine the position of x and y by plugging in a frame number. Of course, once the x and y values are off the screen, the image can't be seen anymore, but the image's coordinates are still updated.

Figure 7.2. A table of coordinate values.


The rest of the main loop is a Flip command that works with page flipping. Please see Chapter 6 for a review if you don't understand what Flip does. By the way, the full program is available on the CD as demo07-01.bb.

NOTE

The "d" in dx and dy is there for a reason; it isn't just a random letter. In Greek, the letter delta, which is symbolized as a triangle ( ? ) means "change in." If you "read" the vari able then, you can see that dx and dy mean, "the change in x" and "the change in y."

Let's quickly come up with an equation for translation. This formula is very simple, but hey, so is translation!

 x  1  = x + dx y  1  = y + dy 

What does this mean? Well, if you input the proper coordinates (in this case, the x and/or y values) and add a number you would like to translate by (this number, dx or dy , and be either negative or positive), you will get the new coordinate position for the variable. For example, in the lines of code:

 shipx = shipx + 7 shipy = shipy + 5 

shipx and shipy are x 1 and y 1 , respectively. Shipx and shipy are x and y , and 7 is dx and 5 is dy . All of these variables are taking the place of the constants in the previous equation. Note that the x 1 and y 1 variables are the same as the x and y variables. This doesn't matter because you are updating them in order to move the image.

Are you ready to write a Translate function? Translate() translates any point you send it. Let's begin with a function declaration.

We are going to need an input coordinate and a transformation ("d") variable. So, the function might look something like this.

 Function Translate(x,dx) 

Easy, eh? And of course, the body of the function will be just as simple

 Return x + dx 

with x being the coordinate you want to translate and dx being the factor by which the x variable is translated. Cool, huh?

Let's rewrite the main loop with our new function.

 While Not KeyDown(1) Cls DrawImage shipimage,shipx,shipy shipx = Translate(shipx,7) shipy = Translate(shipy,5) Flip Wend   Function Translate(x,dx) Return x+dx End Function 

And there we are! We now have a working translation function. Although it may seem trivial, it is probably a lot easier to understand the line

 shipx = Translate(shipx,5) 

rather than

 shipx = shipx + 5 

Don't you agree? Notice that the Translate() function does not use global variables, which makes this function extremely portable, because it can now be used in any other program. Copy the code over, and you can use Translate() as much as you want. By the way, the program using the Translate() function, is available on the CD as demo07-02.bb. If you need help understanding the main loop, see Figure 7.3.

Figure 7.3. The main loop.


Okay, now that we've covered translation, let's pump it up: next, we do scaling.

Scaling

When you scale an object, you make the object bigger or smaller (or the same size, if you really want to). Scaling means making something a smaller or larger size but usually keeping the same proportions . Proportionality, though, is not required. Unlike translation, you cannot scale a point. This is because a point is a point is a pointyou cannot make a point a different size.

Let's start off by learning what a proportion is and how it is used.

Proportion? What the Heck Is That?

A proportion is a ratio or a fraction. For example, the ratio of an object to another object that is two times bigger is 1:2 or 1/2. If the other object was three times as big, the ratio would be 1:3 (and the fraction would be 1/3). If you take a look at the fraction, you will notice that 1/3 is the same as 1 divided by 3. This is sort of interesting: the smaller object (the "1" in 1:3) is exactly 1/3 the size of the bigger object. If you flip the top and bottom on the fraction, you get 3/1, which is the size of the larger object compared to the smaller object (the bigger object is three times the size of the smaller one). Take a look at Figure 7.4. In this figure, you can see the picture of a regular sized man on the left. The picture on the right is the same man, but he is scaled. He is 1/5 the size of the original man (the big man to small man ratio is 1:5).

Figure 7.4. A man and his 1/5 scaled counterpart .


Ratios and fractions can also be used when an object becomes smaller. Say you have object A and object B. Object B is 5 times smaller than object A. The ratio in this example is 5:1, and the fraction is 5/1 (or just 5: any number divided by 1 is that number). As you can see, object A, which is the "5" in 5:1, is 5 times bigger than object B. If you flip the fraction 5/1, you get 1/5, which is the size of object B in comparison to object A.

A proportion can be thought of as a ratio or a fraction. You can also use percentages. When using Blitz Basic, you usually use percentages. In other words, when you want to scale the size of an object, you multiply by a percentage number. For example, say you want to make something four times as large as it is. Just multiply each coordinate by 4. Referring to Figure 7.5, you can see a box with coordinates (0,0), (0,5), (5,0), and (5,5). By multiplying each coordinate by 4 such that (0,0) remains (0,0); (0,5) becomes (0,20); (5,0) becomes (20,0); and (5,5) becomes (20,20), the box becomes 4 times as large. However, what if you want to make the object something like 5/8th as large? All you have to do is bust out a calculator and divide 5 by 8. Since 5/8 = .625, the multiplication factor will be .625.

Figure 7.5. A scaled rectangle.


NOTE

TIP

When scaling an object, you must use decimal percentages. If you want to make one object scaled to 50% of the previous one, don't multiply by 50.Your new object will be 50 times as large as the older one! Instead, think of it as a fraction.You want to make the new object 1/2 as large as the previous one. If you divide 1 by 2, you get .5. Multiply the object by .5 and your object is scaled to 50%.

Now we have a basic outline for our scaling equation. The scaled equation looks a lot like the translation equation:

 x  1  = x * sx y  1  = y * sy 

Notice the differences between translations and scaling. When translating, you add the "d" variable to the current x; however, when scaling, you multiply the "s" variable by the current x to scale it.

Scaling Shapes

Shape scaling is relatively simple. Just multiply the ending coordinate by the scaling factor, and you're done! We are going to go over scaling different kinds of shapes, such as rectangles and triangles . Ready to move?

Scaling Rectangles

Let's write a few programs that utilize scaling within shapes. The first draws a rectangle, waits for the user to choose a ratio, and draws a new rectangle with a new size. On the CD, this file is named demo07-03.bb.

We begin with a graphics call. After that, we initialize the variables:

 ;demo 07-03.bb - Demonstrates Scaling Graphics 1024,768   ;VARIABLES ;Create the variables that define the rectangle rectbeginx = 25 ;The x coordinate of the top left corner rectbeginy = 25 ;The y coordinate of the top left corner rectwidth  = 256 ;The x coordinate of the bottom right coordinate rectheight = 256 ;The y coordinate of the bottom right coordinate 

All that has happened so far is the creation of a few variables. I offset the box from (0,0) so that you can see the scaling more clearly. If you feel like it, change any of these variables.

Next, we move to the main section of the code. The first part deals with the first rectangle.

 ;MAIN SECTION ;Make sure the text goes near the bottom of the screen Locate 0,700 Print "This is our first rectangle." ;Draw the first rectangle, and make it not filled Rect rectbeginx,rectbeginy,rectwidth ,rectheight,0 

We first start off with a call to Locate . This forces the text, " This is our first rectangle" to appear near the bottom of the screen, so it doesn't interfere with the rectangles. Next, we call the Rect function. This function, which is compiler defined, draws a rectangle from the starting coordinates ( rectbeginx and rectbeginy ) to the ending coordinates ( rectendx and rectendy ). (See Table 7.1 to see Rect 's parameters.) The 0 at the end leaves the rectangle unfilled (you can set it equal to 1 if you want the rectangle filled, but it looks kind of ugly if you do so).

Table 7.1. Rect's Parameters

Parameter

Description

x

The x coordinate of the top left of the rectangle

y

The y coordinate of the top left of the rectangle

width

The width in pixels of the rectangle

height

The height in pixels of the rectangle

solid

If set to 0, the rectangle is not filled; if set to 1, the rectangle is filled


Next up: finding the scaling factor.

 ;Ask the user what the scaling factor is sxy# = Input ("What would you like the scaling factor to be? (Ex: 50% = .5)? ==> ") 

This statement asks the user what he would like to scale the x and y coordinates by. For this program, both x and y are scaled by the same amount. If you feel like it, rewrite this program so the user can scale both x and y by different amounts.

NOTE

We haven't touched on variable types for a long time. In case you have forgotten, when ' # ' is appended to the end of a variable name , the variable is a floating-point variable. If a variable is floating point, it can hold decimal places. In other words, xyx can be 314, whereas xyx# can be 314.13. If you try to make a non-floating- point variable (an integer) include a decimal, the decimal portion will be truncat- ed (removed). For example, the number 1.9 will become 1 because the .9 has been truncated or deleted. Be careful when doing this because if you truncate decimal numbers , you will lose information. And unless you intended to do this, it could be really bad. For example, if you were computing tax with the number 0.08 and got rid of the decimal part, you would end up with a tax rate of 0! In this program, the user can multiply the variable by any number, such as 1.5, .3, and so on. It would not be very good if the decimal was truncated, because the new vari able would (most likely) end up being either 1 or 0. What a boring program it would be if the new rectangle was either deleted or kept the same size!

The scaling factor is stored in the variable sxy# . This variable is used in the next section of code.

 ;Multiply the width and height by the scaling factor rectwidth = rectwidth  * sxy# rectheight = rectheight * sxy# 

To scale the new object, you must multiply each coordinate by the scaling factor. The scal-ing factor was determined, via user input, in the previous section of code. Here, both the x and y values are multiplied by the scaling factor to make it as big as the user wants it to be.

The final section of the code draws the second rectangle and exits the program.

 ;Draw the new rectangle Rect rectbeginx,rectbeginy,rectwidth,rectheight,0 Print "Press any key to exit." ;Wait for the user to press a key before exiting. WaitKey 

The first line here draws a new rectangle with the scaled coordinates. Because the beginning x and y values remain the same, the rectangle is drawn over the old one.

The final two lines ask the user to press any key. Once a key is pressed, the program is over. Figure 7.6 shows a screenshot from the program.

Figure 7.6. The demo07-03.bb program.


This program teaches a lot of important ideas. Try this out: change the code to work with ellipses (using the Oval function).

You might have noticed something strange when using a scaling factor. When scaled by 2, as in Figure 7.7, the original rectangle is only 1/4 of the size of the new rectangle, even though you might expect it to be 1/2. The reason is that each coordinate is scaled by 2, not the rectangle as a whole. Therefore, the new rectangle is actually four times the size of the original rectangle.

Figure 7.7. Demo07-03.bb with a scaling factor of 2.


Scaling Triangles

We can now move on to something a bit more challenging: scaling a triangle. Unlike the rectangle, triangles don't have a function. We have to draw each line manually.

Before we can begin discussing how to scale a triangle, we need to understand the difference between local and global coordinates.

There is a huge difference between global and local coordinates. A local coordinate, much like a local variable, is only visible from the object which is being drawn. Global coordinates, on the other hand, are the same for all objects.

Maybe an analogy will help to understand the difference. Take a human; for instance, let's take you. You are a person. There are many people. But you are the center of everything that you can see. To you, everything revolves around you. Therefore, your local coordinates stem from the top of you to the bottom of you. However, remember that this holds true for everyone else as well. Each person has their own local coordinates.

Now, imagine a spaceship watching Earth from the sky. To the aliens, people are everywhere. Each person is not central to the spaceship; instead, the Earth as a whole is. So, to the aliens , the Earth is a coordinate plane (it isn't actually a plane, but never mind that). Where you are now is located at some coordinate position (maybe (13,14)), but that will change when you take a step to another area. Latitude and longitude perform the same actions as global coordinatesyou can pinpoint a certain position anywhere in the world by telling what the latitude and longitude coordinates are.

Look at Figure 7.8, which shows a map of the world with two people, Person A and Person B. Person A and Person B each believe they are the center of the world; that is, they think of themselves as located at (0,0). However, the spaceship that is watching them (you are the spaceship for now) sees them in two very different coordinate positions, shown by their latitude and longitude values.

Figure 7.8. The spaceship and the world.


When you move around, your global coordinates change. However, your local coordinates remain the same. Your viewpoint does not change, and therefore, your local coordinates stay with you no matter where you go.

With objects in Blitz Basic, this analogy works extremely well. To the triangle we are using in the following program, the center begins at coordinates (0,0). The object's global coordinates begin wherever it is displayed on the screen. Referring to Figure 7.9, you can see that the local coordinates of an object begin at the top-left corner and end at the bottom-right corner.

Figure 7.9. Global and local coordinates.


Now that we understand local and global coordinates, let's get into this program. We first set up the graphics mode.

 Graphics 1024,768 

Now we are going to create a type called point . point will contain two fields: its x and y coordinates.

 Type point         Field x,y End Type 

We need to have three points for this triangle: one for each vertex. A vertex (plural vertices ) is a point where a line changes directionin the case of a triangle, there are three vertices, one at each corner.

 point1.point = New point point2.point = New point point3.point = New point 

point1 , point2 , and point3 are the three different vertices on the triangle. point1 begins at the apex, or top, of the triangle, and point2 and point3 follow in a clockwise manner. The line that begins at point1 extends to point2 , the line from point2 extends to point3 , and the line from point3 extends to point1 .

Next, we have to define the local coordinates for our first triangle. In demo07-04.bb, the vertices are defined like this:

 ;These variables define each vertex and are in local coordinates point1\x= 0 point1\y= -100 point2\x= 100 point2\y= 100 point3\x= -100 point3\y = 100 

These points are centered around (0,0). Note that all of these coordinates are local: obviously, you can never have negative values for global coordinates. Figure 7.10 shows the coordinates of each point on the triangle. As you can see, the origin point (0,0) is in the exact center of the triangle.

Figure 7.10. Local coordinates.


As stated earlier, to obtain global coordinates, we will add a constant value to each local coordinate of the triangle. The constant section of this program has two variables.

 ;CONSTANTS ;The global indicators that is added to each local coordinate to place it on screen Const xs = 512 Const ys = 384 

I chose these two numbers because they center the triangle onscreen. Note that the program is 1024 pixels by 768 pixels (these numbers are defined in the Graphics call), and 1024 / 2 = 512 and 768 / 2 = 384. To achieve the correct global coordinates, the xs variable is added to each x coordinate and ys is added to each y coordinate.

Now that we have all of our initialization values defined and variables created, let's move on to the actual program. The program begins with these two lines:

 Locate 0,700 Print "This is our first triangle." 

As you probably know, the Locate command places all of the Print commands near the bottom of the screen. The Print statement then writes , "This is our first triangle" on the screen.

Next, we draw the first triangle. This is accomplished with the Line function, which draws a line from one coordinate position to another. Line is declared like this

 Line x,y,x1,y1 

In essence, the Line function draws a straight line from coordinates x , y to coordinates

 x1,y1. For this program, there are three Line calls for each triangle. ;Draw out first triangle Line point1\x + xs, point1\y + ys, point2\x + xs, point2\y + ys Line point2\x + xs, point2\y + ys, point3\x + xs, point3\y + ys Line point3\x + xs, point3\y + ys, point1\x + xs, point1\y + ys 

As you can see, each Line call draws a line from one of the vertices to another of the vertices. If you look closely, you can see that xs is added to each x coordinate and ys is added to each y coordinate. These numbers are added to the triangle's local coordinates in order to move the triangle onscreen so they can be seen in the program.

Okay, now that we have drawn the original triangle, let's find out what the user wants the scaling factor to be. This is accomplished with a call to Input .

 ;Find scaling factor from user sxy# = Input ("What would you like the scaling factor to be? (Ex: 50% = .5)? ==> ") 

The user now has a chance to input his scaling factor, which is stored in sxy# . Note that this variable is a float, as signified by the # symbol.

After the user has chosen his scaling factor, we scale each point. The following lines perform the scaling actions.

 ;Multiply all the coordinates by the scaling factor point1\x = point1\x * sxy# point1\y = point1\y * sxy# point2\x = point2\x * sxy# point2\y = point2\y * sxy# point3\x = point3\x * sxy# point3\y = point3\y * sxy# 

Pretty easy, eh? All this block of code did was multiply each vertex's x and y position by sxy# .

Okay, now we must get ready to draw out the new triangle. Since we want the new object to be easily seen, let's change the color of the lines. This is easily accomplished using the color function.

 ;Change the default color to green Color 0,255,0 

This makes all following Line commands green.

Now, all we have to do is draw out the new triangle. This is accomplished by calling Line for each point, as we did for the original triangle.

 ;Draw final triangle (with scaled coordinates) in green Line point1\x + xs, point1\y + ys, point2\x + xs, point2\y + ys Line point2\x + xs, point2\y + ys, point3\x + xs, point3\y + ys Line point3\x + xs, point3\y + ys, point1\x + xs, point1\y + ys 

Excellent! The program has now printed lines connecting each vertex, and therefore, drawn a new triangle.

Now all we do is finish the program.

 Print "Press any key to exit." ;Wait for user to press a key before exiting WaitKey 

These lines of code tell the user to press any key, and wait for him to press a key before exiting. That's the complete program. Figures 7.11 and 7.12 demonstrate the program with scaling factors of 2 and .5. You won't be able to see that the new lines are drawn in green in the figures, but you can look at the program on the CD to see the new triangles drawn in green. If you notice, the new triangle is centered in respect to the original triangle. But, what if you don't want to keep it centered? All you have to do is change the local coordinates.

Figure 7.11. Demo07- 04.bb with a scaling factor of 2.


Figure 7.12. Demo07- 04.bb with a scaling factor of .5.


NOTE

TIP

Wanna see something cool? When you are asked to input sxy# , enter a negative number. The new triangle is flipped . Check out Figure 7.13 ; it shows the program with an sxy# value of 1.

Demo07-04.bb with a scaling factor of -1.


Figure 7.14 shows demo07-05.bb. As you can see, the triangle grows downward, but it does not remain centered. Demo07-05.bb is almost exactly the same as demo07-04.bb, except the beginning variables have been changed. The variables are now initialized with different values.

Figure 7.14. The demo07-05.bb program.


 ;VARIABLES ;These variables define each vertex and are in local coordinates point1\x= 0 point1\y= 0 point2\x= 100 point2\y= 100 point3\x= -100 point3\y = 100 

The big difference here is that point1\y has been changed to 0 from its value in demo07-04.bb. Because any number multiplied by 0 equals 0, when sxy# is multiplied in the line:

 point1\x = point1\x * sxy# 

point1\x will always equal 0. Because the variable does not change, point1 will remain in the same position throughout the program. Thus, the triangle will grow from the top downward.

It comes out pretty nicely , don't you think? By the way, if you want it to grow upward, change the bottom points' y values to 0. Demo07-06.bb shows the triangle growing upward.

The variables have been changed slightly, now they are

 ;VARIABLES ;These variables define each vertex and are in local coordinates point1\x= 0 point1\y= -100 point2\x= 100 point2\y= 0 point3\x= -100 point3\y = 0 

As you can see, the two lower points are equivalent to 0. Now, when it is scaled by 2, it grows upward, as in Figure 7.15.

Figure 7.15. The demo07-06.bb program.


One thing you should know about the previous program: because changing the y values for the bottom two points moves the figure up a little, I changed the ys variable a little. The constants section now reads:

 ;CONSTANTS ;The global indicators that are added to each local coordinate to place it on screen Const xs = 512 Const ys = 484 

The ys variable in demo07-06.bb was changed from 384 to 484 to offset the 100 pixel difference between the original triangle in demo07-04.bb and the new triangle in demo07-06.bb.

Now let's create a scale function.

 Function Scale(x,sx) Return x*sx End Function 

x is the value you want to scale and sx is the scaling factor. If you want to scale the x coordinate of point1 , just call it like this:

 Scale(point1\x,sxy#) 

assuming that sxy# is the scaling factor.

Scaling Images

Blitz Basic makes scaling images extremely easy by providing the function ScaleImage . ScaleImage is defined like this:

 ScaleImage image,xscale#,yscale# 

Basically, just include the image handle and the x and y scaling values (don't forget, 1.0 = 100%) in the call to ScaleImage .

As an example, we will scale an image of a spaceship. The original spaceship is shown in Figure 7.16, and the resulting scaled image is shown in Figure 7.17.

Figure 7.16. The original spaceship image.


Figure 7.17. The scaled spaceship in the demo07-07.bb program.


Demo07-07.bb is pretty short, so I am just going to copy the entire program and explain it at the end.

 ;demo07-07.bb - Demonstrates the use of ScaleImage Graphics 1024,768 ;Set automidhandle to true AutoMidHandle True ;IMAGES ;Load the spaceship that will be drawn on screen spaceshipimage = LoadImage("spaceship.bmp") ;Draw the spaceship directly in the center of the screen DrawImage spaceshipimage, 512,384 ;Find out what the player wants the x and y scaling factors to be xs# = Input("What would you like the x scaling value to be? ") ys# = Input("What would you like the y scaling value to be? ") ;Prepare the screen for the scaled spaceship by clearing it Cls ;Scale the image ScaleImage spaceshipimage, xs#,ys# ;Draw the new scaled spaceship DrawImage spaceshipimage, 512,384 Print "This is your updated image" Print "Press any key to exit" ;Wait for user to press a key before exiting WaitKey 

The first thing the program does is initialize the graphics and set AutoMidHandle to true, so that the images are centered. It then loads the spaceship and draws it onscreen.

Using the Input functions, the program finds out what the scaling factors are. It then clears the screen in preparation for the new image.

 ;Scale the image ScaleImage spaceshipimage, xs#,ys#  ;Draw the new scaled spaceship DrawImage spaceshipimage, 512,384 

The new scaled spaceship is drawn directly in the center of the screen, after being scaled by the ScaleImage function, which uses the scaling factors provided by the user earlier in the program.

These two lines scale and draw the new image. The program finishes off its tour by asking the user to press a key. Once he does, the program is exited.

Note that if you size the image to greater than 100 percent, the image looks a little blurry. The reason is that the scaling function stretches the image and makes each of its pixels a little bit larger.

We can use ScaleImage with polygons such as triangles, also. We just need to make a few calls to CreateImage() and ImageBuffer() . Let's rewrite demo07-04.bb using ScaleImage .

Obviously, we first initialize the graphics. We then set AutoMidHandle to true so that the images will be centered in the program. We can then initialize the program's variables, such as the starting coordinates and the point type. Now is when we change the program a little bit.

We must make a call to CreateImage() to get a handle for an image that we can scale. This call should do the trick:

 image = CreateImage( (point2\x  point3\x),(point2\y-point1\y) ) 

Because we know that point3 is the farthest vertex to the right, point2 is the farthest down and left, and point1 is the highest, we subtract the high and low values to get the width and height of our image. Now we need to call SetBuffer to set the active buffer to the image handle so we can draw straight to it.

 SetBuffer ImageBuffer(image) 

Now we continue with the Line commands. We then must revert back to the FrontBuffer() by calling SetBuffer again.

 SetBuffer FrontBuffer() 

We then use the DrawImage() function to display the original triangle.

 DrawImage image, 512,384 

Using Input , we get the scaling factor, and we call ScaleImage like this:

 ScaleImage image,sxy#,sxy# 

We then call

 DrawImage image, 512,384 

And the program is done!

Following is the entire source for this program. We will review at the end.

 ;demo07-08.bb - Demonstrates Scaling with ScaleImage Graphics 1024,768 ;Make sure AutoMidHandle is true AutoMidHandle True ;STRUCTURES ;The point structure defines one coordinate point Type point Field x,y End Type ;Create the three vertices point1.point = New point point2.point = New point point3.point = New point ;VARIABLES ;These variables are in local coordinates and define the positions of the vertices point1\x= 100 point1\y= 0 point2\x= 200 point2\y= 200 point3\x= 0 point3\y = 200 ;Create a buffer with the proper height and width image = CreateImage( (point2\x - point3\x) + 1, (point2\y - point1\y) + 1 ) ;MAIN SECTION ;Place text at bottom left of screen Locate 0,700 Print "This is our first triangle." ;Set default buffer to the image we created so that we can draw the triangle directly to it SetBuffer ImageBuffer(image) ;Draw the triangle on the new buffer Line point1\x, point1\y, point2\x, point2\y Line point2\x, point2\y, point3\x, point3\y Line point3\x, point3\y, point1\x, point1\y    ;Go back to front buffer SetBuffer FrontBuffer() ;Draw the image centered on screen DrawImage image,512,384 ;Find the scaling factor sxy# = Input ("What would you like the scaling factor to be? (Ex: 50% = .5)? ==> ") ;What is the scaling factor ;Scale the image by its scaling factos ScaleImage image,sxy#,sxy# ;Draw the new image DrawImage image,512,384 Print "Press any key to exit." ;Wait for a key before exiting WaitKey 

I want you to notice a few things in this program. First, I removed the xs and ys constants. Since this triangle is drawn onto an image buffer and the image is drawn in the center, there is no need to convert local coordinates to global coordinates.

Second, notice that I added 100 to each of the point\x and point\y variables. This is necessary to remove the negative coordinates. Negative coordinates will be drawn off the buffer, and we need to move everything on, so we just need to get rid of the negative values.

Third, notice that I added 1 to the size of the buffer in the call to CreateImage() . This addition was made to allow the entire image to be drawn on the buffer. It is usually a good idea to give a little lee way (here, 1 pixel) to make sure that everything appears on the image.

Last, you will notice that the scaled bitmap is blurry. This happens when bitmaps are scaled. Since a bitmap has only a finite amount of information, when you try to stretch it, the computer has to make up the information to fill in the blanks to make it bigger. The computer does this by averaging pixels in the bitmap and then computing what would be between them. The results of this are blurry images because the computer has to guess what the new and larger image should look like. Well, that's pretty much it for scaling. We can now move on to a really cool subject: rotation.

Rotation

So far, you have learned two of the three types of transformations. Rotation is the final one that you will learn. Rotation is usually extremely hard to pull off, but Blitz Basic makes it much easier.

Like scaling, Blitz Basic provides a function for rotation RotateImage. RotateImage is defined as this:

 RotateImage image, value# 

with image being the handle of the image you want to rotate, and value# being the number of degrees (between 0 and 360) you want to rotate the image in a clockwise fashion.

Take a look at Figures 7.18 and 7.19. They demonstrate clockwise and counterclockwise directions, respectively. Oh yeah, a clock turns clockwise.

Figure 7.18. Clockwise .


Figure 7.19. Counter- clockwise.


Value# may be equal to any number between 0 and 360. 360 is the number of degrees in a circle. Refer to Figure 7.20 to see the degrees in a circle.

Figure 7.20. Degrees in a circle.


As you can see, rotating an image is pretty simple. Rotating shapes is extremely difficult and requires complex mathematics, so it is often a good idea to use CreateImage() and ImageBuffer() , as done with scaling in demo07-08.bb. Using CreateImage() allows you to turn your shape into an image, which makes rotation algorithms easier to perform.

Let's write a program that rotates a shape. This program loads an image from the hard drive, asks the user how many degrees he wants to rotate it, and performs the action.

Following is the rotation section of the code in demo07-09.bb.

 ;Draw the beginning image DrawImage shipimage,400,300 ;Find out what the rotation value is rotationvalue# = Input ( "How many degrees would you like to rotate the image? ") ;Rotate the Image RotateImage shipimage, rotationvalue# Print "Your new image is now drawn on the screen" ;Draw the new and rotated image on the screen DrawImage shipimage, 440,300 

This section draws the shipimage (which was loaded earlier in the program) at the center of the screen. The program then retrieves rotationvalue# from the user, and rotates the image using the command

 RotateImage shipimage, rotationvalue# 

This line rotates the shipimage the number of degrees entered in rotationvalue# . The program then draws the new image to the right of the old image.

That's demo07-09.bb. Figure 7.21 shows a screenshot from the program.

Figure 7.21. The demo07-09.bb program.


Asking a user for a rotation value is nice, but what about real-time rotation? Real-time rotation allows you to rotate an image at the spur of the moment. This effect is used in games such as Asteroids , where a spaceship is rotated onscreen.

Real-time rendering is as simple as waiting for the user to press a button and calling RotateImage , right? Wrong. If you do this, your program will run extremely slowly. You need to preload your images to allow the program to run at full speed. Before we get into pre-loading, though, let's see what a rotation program will look like without it.

Following is the main loop of demo07-10.bb. Read through it and try to understand. An explanation follows .

 ;MAIN LOOP While Not KeyDown(1) ;Clear the Screen Cls ;If the player presses left, rotate four degrees right, if he presses right, rotate four degrees right If KeyDown (203)         RotateImage shipimage, -4 ElseIf KeyDown (205)         RotateImage shipimage,4 EndIf ;Draw the ship DrawImage shipimage, 400,300 Flip Wend ;END OF MAIN LOOP 

As you can see from the Flip and the Cls commands, this program uses page flipping. The rest of the program is pretty self-explanatory. If the user presses left (key code 203), the ship is rotated counter-clockwise 4 degrees. If the user presses right (key code 205), the ship is rotated 4 degrees clockwise.

This program should work correctly, huh? Unfortunately, it doesn't. Because rotating takes a lot of processor power, the program runs slowly. To fix this problem, we use a technique called preloading,

Preloading is sort of hard to understand, but let me walk you through it. The first thing you want to do is decide how many frames you want. With a lot of frames, you get a little bit better animation. Since the image has a larger number of rotations , there is a smaller difference in degrees of each separate frame. However, with more frames , your program takes up more space in the memory. The program might also run a bit slower.

NOTE

What Is Preloading?

Preloading is often a tough concept for beginning programmers to understand. The basic concept behind preloading is that an image that is changed and saved once in the program and displayed on the screen later is faster than changing the image each time change is necessary. Preloading does just this: at the beginning of the program, you create the images you will need later in the program and save it to an array that can be called at any time. When you need to display the saved images later, you just draw the necessary image from the array that you created earlier. Think of it like doing your homework. It will make the process a lot easier if you pull out all of your school supplies right when you begin, rather than getting a book only when you need it and getting your pencil when it is necessary. Your homework gets done either way. But it can get done quicker if you have "preloaded" all of your supplies . Of course this analogy only works if you actually do you homework.

In the following rotation program, I rotate the image as many times as I want frames. When the user presses left or right, the next frame is displayed. By the way, when I use frame in this section, I am referring to frames of an imagenot iterations of the main loop.

I usually choose a frame count of 16it provides less obviousness than a lesser amount but does not take up a lot of memory like higher frame amounts. Figure 7.22 shows each of the

Figure 7.22. The ship's 16 rotations.


Anyway, let's get to the preloading. The first thing we do is create a constant that will hold the amount of rotations (frames) we want to have for our image. It might look something like this.

Const rotations = 16

Next, we create an array that holds all of our frames.

 Dim imagearray(rotations) 

As you can see, this array will hold 16 images: one for each rotation. Don't forget that each array begins with 0, so 16 frames will have rotations from 0 to 15.

Now comes the semi-hard part. We have to load the frames into the array. This can be accomplished through a For Next loop.

 For frame = 0 To rotations - 1         imagearray(frame) = CopyImage (shipimage)         RotateImage imagearray(frame), frame*360/rotations Next 

What? Let's go through this loop line by line. This particular loop runs 16 times. It begins with and counts to rotations 1 . Here, rotations 1 is equal to 15 (161 = 15). Now we get into the actual copying. Each frame of the array has shipimage , the image of the actual image, copied into it. This is accomplished by CopyImage() . The following line does the rotating of each frame, and it looks like this:

RotateImage imagearray(frame), frame*360/rotations

If you remember, RotateImage 's first parameter explains what will be rotated and the second parameter says how much it should be rotated. Here, imagearray(frame) is rotated. This frame is the one that was just copied into the array. The rotation amount is a bit harder to understand. The frame number is multiplied by the number of degrees in a circle (360) and then divided by the total number of rotations. Table 7.7 shows values for an image with 16 rotations.

Table 7.2. Rotation Angle in Degrees with 16 Rotations for a Full Circle

Frame

Subscript Number

Degrees Rotated

1

2

1

22.5

3

2

45

4

3

67.5

5

4

90

6

5

112.5

7

6

135

8

7

157.5

9

8

180

10

9

202.5

11

10

225

12

11

247.5

13

12

270

14

13

292.5

15

14

315

16

15

337.5


NOTE

Notice that the image is never rotated 360 degrees. Rotating an image 360 degrees is the same as rotating it 0 degrees (the rotations come to a full circle). Therefore, the final frame rotates the image a tiny bit less than a full 360 degrees.

Let me display the full source for the rotation program now. We will go over the main loop right after.

 ;demo07-11.bb - Demonstrates preloading and real-time rotation  Graphics 800,600 ;Set up AutoMidHandle and BackBuffer() AutoMidHandle True SetBuffer BackBuffer()   ;IMAGES ;Load the spaceship image that will be rotated shipimage = LoadImage ("spaceship.bmp")   ;CONSTANTS ;How many rotations do you want total? Const rotations = 16   ;Create the rotation array Dim imagearray(rotations)   ;For all of the rotations you want, copy the spaceship image and rotate it the correct amount of degrees For frame = 0 To rotations - 1     imagearray(frame) = CopyImage (shipimage)     RotateImage imagearray(frame), frame*360/rotations Next   Print "Press Left to rotate counter-clockwise and right to rotate clockwise," Print "Press Esc to exit."   ;Begin at frame 0 (facing upwards) frame = 0   ;MAIN LOOP While Not KeyDown(1)   ;Clear the screen Cls   ;Rotate the ship left if user presses left If KeyDown (203)   ;Decrement frame by 1 (thus rotating it left)     frame = frame - 1   ;If the frame count is less than 0, put it back at the max it the max value of the array     If frame <= 0       frame = rotations - 1       EndIf ;Rotate the ship right if user presses right ElseIf KeyDown (205) ;Increment frame by 1 (thus rotating it right)         frame = frame + 1 ;If frame gets too big, set it to the first frame (0)          If frame >= rotations                frame = 0          EndIf EndIf ;Draw the current frame DrawImage imagearray(frame), 400,300 Flip ;Wait for a while Delay 50 Wend 

A beauty, huh? As usual, the beginning of the program sets up the graphics and initializes the rotation array. It then prints the introductory text to the user and resets frame to 0 (so that the ship faces upward).

The main loop tests for two keysleft and right. If the user presses left, the frame amount is decreased by one, and if right is pressed, the frame amount is increased by 1. If frame is 0 and left is pressed, frame becomes rotations 1 , and if frame is the max number of rotations and right is pressed, frame becomes 0.

Make sure you understand rotations and preloading by now. If you're still a bit uneasy with the material, please read through the section again. We are now moving onto the subject of parallaxing. Think about it, with a word as cool as parallaxing , how can it not be fun?

[ LiB ]


Game Programming for Teens
Game Programming for Teens
ISBN: 1598635182
EAN: 2147483647
Year: 2004
Pages: 94
Authors: Maneesh Sethi

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net