Game Programming for Teens
[ LiB ] |
Random Numbers
The first part of artificial intelligence we're going to cover is how to use random variables in programs. Using random variables in programs isn't really intelligent , but it's a first step, right?
NOTE
Random Variables: Really Random?
Finding a true random number using a computer is almost impossible . Computers only take input, process it, and produce outputthey are not built to produce random numbers, for the most part. Therefore, computers can only produce pseudo-random numbers. For example, if you print out ten-million random numbers, you shouldn't be able to detect any patterns, and there should be an equal distribution between the numbers. However, if you try this on a computer, you will see patterns and you will see slight unevenness in the numbers, but for our purpose, the pseudo-random numbers are close enough to random!
In order to generate random numbers, we need to call two functions. The first one we call is named SeedRnd .
SeedRnd is declared like this:
SeedRnd seed
What is a seed? Well, SeedRnd works like this: it feeds the computer a number that will be used later to create pseudo-random numbers. We need to make seed equal to a number that changes every time the game is played; otherwise , the "random" numbers will always be the same every time the game is played .
The function MilliSecs() is a function that changes every time the program runs. This function returns the number of milliseconds in the system timer (since the computer last started up). Because the time on the system timer changes continuously, MilliSecs() is a good choice for a value to feed SeedRnd .
At the beginning of a program that uses random variables, we call SeedRnd as follows :
SeedRnd MilliSecs()
Pretty cool, huh? After doing that, we can continue to use random numbers. Note that SeedRnd doesn't actually perform any noticeable functions in a program, but it sets up the program to use random variables later in the program.
Now that we have set up the random generator (by calling SeedRnd ), we are able to actually find those random numbers. There are two functions that are provided by Blitz Basic. These functions are Rnd and Rand .
Both of these functions have similar declarations.
Rand ([start], end) Rnd (start#, end#)
As you can see, they are almost the same. First of all, let me help you understand what those parameters are.
The parameter names for both Rand() and Rnd() are the same. The startstart# parameter is the least possible value for the random number, and endend# is the largest possible value for the random number. That was probably a little hard to comprehend, so let me explain it better. When using one of the random functions, you will usually feed it two parameters. For example, you may do something like this:
randomvalue = Rand (100,200)
Because you handed Rand() the parameters 100 and 200, randomvalue will now contain a number between 100 and 200. You can change the parameters slightly to see what I mean. If you changed the 100 to 50 in the previous call, randomvalue would contain a number between 50 and 100.
Also, one other thing. You might have noticed that the [start] parameter in Rand() is optional (as signified by the brackets). Since it is optional, you are only required to provide Rand with one parameter. If you neglect to include [start], Blitz Basic will assume that you want the [ start] parameter to be equal to 1. Therefore, calling Rand() as follows
Rand (205)
will return a random number between 1 and 205.
Both Rand() and Rnd() have the same parameters, except Rand() 's are integers and Rnd() 's are floating points. Remember, an integer is a number without a decimal point (for example, 314), whereas a floating-point variable has a decimal attached (for example, 314.13, where ".13" is the decimal).
The fact that Rnd() allows you to provide it with floating-point parameters means that you can make your random variables contain numbers with decimal points. If you call Rnd() like this:
Rnd (1.000,14.000)
the function will return a number between 1.000 and 14.000. It could end up being a number such as 3.133 or something like that.
If you decided to call Rand() in the same way, the number would end up being only an integer, such as 4 or 9. Take a look at Figure 12.1 to see what happens when you call the Rand() function with floating parameters. As you can see, even if you provide Rand() with floating-point numbers as parameters, it will still return an integer. It does this by finding the random number and deleting the decimal point.
Figure 12.1. Using Rand() with floating-point parameters.
Alright, excellent . Hopefully you understand how to determine random numbers. Now, let's put this into a program.
Following is the source to demo12-01.bb.
;demo12-01.bb - Demonstrates random variables Graphics 800,600 ;Set up automidhandle and backbuffer AutoMidHandle True SetBuffer BackBuffer() ;Make sure we seed the random generator SeedRnd MilliSecs() ;Now we load the image that we will use. flyimage = LoadAnimImage ("fly.bmp",64,64,0,4) ;create a starting frame value frame = 0 ;create the x and y values for the fly flyx = 400 flyy = 300 ;MAIN LOOP While Not KeyDown (1) ;Clear the screen Cls Text 0,0,"Fly X: " + flyx Text 0,20,"Fly Y: " + flyy ;move the fly a random amount flyx = flyx + Rand(-15,15) flyy = flyy + Rand(-15,15) ;Draw the fly on screen DrawImage flyimage,flyx,flyy,frame ;increment the frame frame = frame + 1 ;If frame gets too large or small, reset it If frame > 3 frame = 0 ElseIf frame < 0 frame = 3 EndIf ;Flip the buffers Flip ;Wait a little bit Delay 25 Wend ;END OF MAIN LOOP
Figure 12.2 is a screenshot taken from demo12-01.bb.
Figure 12.2. The demo12-01.bb program.
Alright, the program is nice and all, but it's not smooth, is it? The fly is extremely jerky and looks terrible when drawn on the screen. The reason for this is that his x and y variables are updated every single frame, which means his position changes drastically more than 30 times a second (because usually about 30 frames per second occur during games such as these).
Let's redo this program, but instead of changing the fly's coordinates 30 times every second, we will do it only once every few seconds. How are we going to do this? Well, first of all, we need to learn the art of making a timer using the MilliSecs() function.
Creating a MilliSecs() Timer
You have probably noticed the frequent use of MilliSecs() we use it to seed our random generator with the SeedRnd command. If you remember, the reason we use milliseconds to seed the random generator is because MilliSecs() is never the same twice. So, if it is never the same twice, how can we use this to create a timer?
MilliSecs()'s value increases every millisecond that the computer is running. For example, if MilliSecs() is equal to 100123 right now, in exactly one millisecond, it will be equal to 100124. A millisecond is equal to one one-thousandth of a second (in other words, there are 1000 milliseconds in a second), so 101123 occurs exactly one second later than 100123.Now, we need to use MilliSecs() to create a timer. Even though MilliSecs() will never be called at the exact same time, this isn't a big problem. What we are going to do is create a variable that holds MilliSecs() at the starting time. We then check MilliSecs() every frame until its value is equal to or greater than the starting variable we created at the beginning plus the amount of time we want the timer to last.
So, let's put a timer into code. The following snippet shows how a three-second timer would work.
;Create timerbegin which holds the value of the starting timer timerbegin = MilliSecs() ;We would begin the main loop here ;test if the current number of MilliSecs() is equal to the timerbegin + 3 secs If MilliSecs() >= timerbegin + 3000 ;Do Something EndIf
NOTE
CAUTION
If you happened to cut and paste this code into a program, it would not work correctly. The timer would never run out! The reason is that the timer would reset every frame because of the timerbegin = MilliSecs() line, and the If MilliSecs() >= timerbegin + 3000 would never stray more than a few millisec onds from timerbegin . In order for the program to work, we need to separate the initialization of the timer and the timer test. If you only need to use the timer once, you can just place the initialization at the beginning of the program and insert the test in the main loop.
Let's go through this line by line. First of all, we created the timer.
;Create timerbegin which holds the value of the starting timer timerbegin = MilliSecs()
This creates a timer with a value equal to the amount of MilliSecs() at the time of the creation of the timer.
Next , we need to test the timer to see if it has been in existence long enough.
;test if the current number of MilliSecs() is equal to the timerbegin + 3 secs If MilliSecs() >= timerbegin + 3000
How does this work? Well, the function tests the current value of MilliSecs() against timerbegin plus 3 seconds (3000 milliseconds). If you remember, timerbegin is equal to the value of MilliSecs() at the time of creation of the timer. Because MilliSecs() increases every millisecond that the computer is running, the test will return true 3 seconds after the timer was created.
Pretty sweet, if I do say so myself . The full source to demo 12-02.bb is on the CD. It's pretty lengthy and similar to the code in demo12-01.bb, so I'll just cover the important parts here.
Let's discuss the changes. First of all, I created a constant that determines how long a pause there is between the changes in speed and direction for the ship. This constant is named CHANGEDIRECTIONS .
The next part of the program that I changed was the fly itself. I created a type around the fly and set up its starting variables. Following is the code from the source that creates and initializes the fly.
;The fly type Type fly Field x,y ;the coordinate position Field xv,yv ;the fly's velocity Field image ;The fly's image End Type ;let's create the fly fly.fly = New fly ;Start the fly in the center of the screen fly\x = 400 fly\y = 300 ;Give the fly a random velocity fly\xv = Rand(-15,15) fly\yv = Rand(-15,15) ;Now we load the fly image fly\image = LoadAnimImage ("fly.bmp",64,64,0,4)
As you can see, the fly type makes it a lot easier to find out all of the variables that pertain to the fly. We start him in the center of the map, give him random coordinates, and load his image in the previous section.
The next thing I changed was directly before the main loop. I added a section that creates the timer.
;Create starting timer timerbegin = MilliSecs() ;Create a variable that says the timer does not need to be reset timeractive = 1
You already know what timerbegin does, but you may be wondering what timeractive is there for. Timeractive is equal to 1 when the timer is working correctly, but when the timer completes, timeractive is set to 0. The timer then resets, and timeractive is set to 1 again.
Next we move on to the main loop. We go through the usual process of clearing the background and drawing out the pertinent info on the screen. In this program, the x and y coordinates, as well as the time remaining on the timer, is written on the screen.
The following If EndIf statement does the grunt work for the timer.
;If the counter has run through, update the fly's velocities If MilliSecs() >= timerbegin + CHANGEDIRECTIONS ;move the fly a random amount fly\xv = fly\xv + Rand(-5,5) fly\yv = fly\yv + Rand(-5,5) ;make sure timer is reset timeractive = 0 EndIf
This block begins with a test that checks if the timer has finished. It does this by testing the current value of MilliSecs() against the value of MilliSecs() when the timer began ( timerbegin ) + the length of the counter ( CHANGEDIRECTIONS ). If the test returns true, then the timer has run through. That means that the commands within the block are executed.
When the timer runs out, the fly gets new random x and y velocities, which move the fly in a different direction at a different speed. The timeractive variable is then set to 0, which means that the timer is unusable and needs to be reset. The code that resets the timer occurs directly after the previous code. It looks something like this:
;If the timer is inactive, reset the timer
If timeractive = 0
timerbegin = MilliSecs()
timeractive = 1
EndIf This section of the code resets the starting point of the timer to the current value of MilliSecs() . Because the timer no longer needs to be reset (at least until the new timer has completed), timeractive is set to 1.
There are two other changes between the first program and the second program in this chap-ter. The first is the addition of code that tests if the fly has hit any walls. The code looks like this.
;Test if fly hit any walls If fly\x <= 0 Or fly\x > 800 fly\xv = -fly\xv EndIf If fly\y <= 0 Or fly\y >= 600 fly\yv = - fly\yv EndIf
This code tests if the fly has moved offscreen , and if it has, it reverses the direction the fly is traveling.
The other change was the addition of velocity values in the program. In the first program, we only changed the x and y coordinates of the fly, and in this program we used x and y coordinates along with x and y velocities.
Alright, that's the end of the first part of this chapter. The next section introduces you to some of the easiest things you will ever learn: chasing and evading .
[ LiB ] |