TDK_Man's Dark Basic Programming For Beginners
Part 3 - Elementary Commands
In this part of the series, we will be looking at some of the elementary commands in Dark Basic - many of which will work in any version of Basic and are therefore 2D only. DB's more advanced 2D and 3D commands will be covered later in the series. For now, we are just learning how to program, so we need to keep things simple.
If you look at a toddlers first reading books, you won't find any Chaucer or Shakespeare - just Janet and John stuff. It's important to remember that if you don't get to grips with the Janet and John stuff in DB (Text & 2D), then you have no chance of understanding the Shakespeare stuff (3D) when we get to it! I'll try to group the commands in categories of similar types, but in no particular order.
The commands in this part of the series will all be 2D, but 3D will be mentioned occasionally only if there is anything important to say. The main thing is that a grasp of the simple commands in this tutorial will allow you to create complex but simple complete programs in Dark Basic.
Finally, it's worth mentioning that the order in which the commands are covered may seem strange. This is because I have tried whenever possible to cover commands in such a way as to prevent the need to jump ahead in the text to look up something not already covered.
CLS is a good example as you can use the RGB command as an additional parameter. So, RGB is covered first when really CLS (being one of the more basic commands) would have normally come before it.
When you write any computer program, you need a method of putting information onto the screen. A number of commands are available for both text and graphics - some of which do the same thing, but with subtle differences. You choose which to use depending on the circumstances.
Screen Output
A screen consists of a grid of dots - initially all set to black. Each dot (or pixel) on the screen has a co-ordinate comprising of an X and Y value.
The X value starts at 0 on the left hand side of the screen and increases as you move right. The maximum value for X depends on the current screen size. By default, unless you specifically change the screen size, DB programs run in a screen size (resolution) of 640x480. This means that X runs from 0 to 639.
The Y value starts at 0 at the top of the screen and increases as you move down the screen. The maximum value for Y also depends on the current screen size and in DB's default resolution of 640x480 Y will run from 0 to 479.
The X and Y maximum values are always 1 less than the actual screen resolution and specifying a location on the screen is simply a case of providing the X and Y positions.
SET DISPLAY MODE
This command is placed at the start of your program and allows you to specify the screen mode you want your program to run in and uses the syntax Set Display Mode Xrez,Yrez,BitDepth.
Most of the usual X/Y Windows modes are allowed, such as 800x600, 1024x768 and so on and the BitDepth parameter is usually 16 or 32 to select the maximum number of colours available. 16 is the norm as some graphics cards will not run at 32 bit in some screen resolutions. Personally, I tend to do everything with:
Set Display Mode 800,600,16
[Edit] This tutorial was written a few years ago when most DB users worked in this resolution. These days, systems are a little better and 1024x768x32 (or even higher) is more common.
RGB(Rval,Gval,Bval)
RGB stands for Red, Green, Blue and is the method DB uses to specify a colour. All colours on a standard CRT monitor are created by red, green and blue guns which light up dots on the screen.
The intensity of each of the colour components defines the colour of the dot produced. The lowest intensity value for the three components is 0 (zero) and with red, green and blue all set to 0, you get a black dot. The highest value for each component is 255 and with red, green and blue all set to 255, you get a white dot. By varying the values for all three components, any desired colour can be reproduced.
The three intensity values are supplied in parenthesis in the order red, green blue. So, if you wanted full intensity green, you would use RGB(0,255,0), whereas RGB(0,100,0) would produce a darker shade of green.
The colour created can be applied to any screen output and the command must be issued again to switch to another colour. The colour is always selected before the command which draws to the screen - not after. Anything already on the screen will remain the designated colour when RGB is used to select another colour. Here are some other common colours as RGB commands:
Red - RGB(255,0,0)
Blue - RGB(0,0,255)
Magenta - RGB(255,0,255)
Yellow - RGB(255,255,0)
Cyan - RGB(0,255,255)
Grey - RGB(150,150,150)
Internally, these intensity values are converted to a colour number which you can also use rather than RGB if you want. One advantage of doing this is that the removal of a calculation makes your program run quicker - albeit only by a miniscule amount. Don't forget however that there may be many, many RGB calculations in your programs and all these tiny amounts soon add up...
However few people do use colour numbers as three RGB values are infinitely more user-friendly to use. What's easier to use for a colour: RGB(100,255,50) or a number like 32742?
RGBR(), RGBG() and RGBB()
If however you do have a colour number like 32742, how do you turn it into red, green and blue colour values? Well, it's easy with the RGBR(), RGBG() and RGBB() functions where you put the colour value into parenthesis:
Red=RGBR(32742)
Green=RGBG(32742)
Blue=RGBB(32742)
This will pull out the red, green and blue intensities from our example colour value of 32742 placing the values into the variables red, green and blue respectively, after which:
Ink 32742,0
... and
Ink RGB(Red,Green,Blue),0
will produce the same colour.
INK
Having the ability to define a colour, you now need to apply it.
This is done with the Ink command which uses the syntax:
Ink ForegroundCol,BackgroundCol
The two parameters can both be RGB values, colour values or a combination of both.
If you imagine the capital letter A is produced by dots on a grid, then the foreground colour is the colour of the dots which form the letter A and the background colour is the the colour of the dots which form the unused part of the grid.
If you have a red screen and want to print white text onto it, then you would set the foreground text colour to white and the background colour to red with:
Ink RGB(255,255,255),RGB(255,0,0)
Once again, colour values can be used with the Ink command, but are seldom used. The only exception is the colour black which is 0, so setting either the foreground or background to black can be done with a 0 rather than RGB(0,0,0) - it's one less calculation for DB to carry out so it's faster. So, white text on a black screen would be done with:
Ink RGB(255,255,255),0
CLS
The CLS command clears the
2D screen. It has no effect on 3D screens. If you have a screen with both 2D and 3D areas, then CLS will only clear the 2D portion of the screen.
Used on it's own, CLS clears the screen to black, (assuming you have not changed the current ink background colour), though you can also use an optional RGB command (or colour number) to clear the screen to any other desired colour. CLS RGB(255,0,0) for example will clear the screen to red.
Text Commands:
PRINT
Print is the easiest way to get text onto the screen and can print literal strings enclosed in quotes, or variables. The screen output appears at the current cursor position (which after a CLS, is in the top left corner of the screen). You can however position it anywhere you want.
Some examples:
Print "The cat sat on the mat"
Print A$
Print NumVar
The first example is a literal string. You can use this to print a simple text message on the screen like "Press Any Key To Start".
The second and third examples both print the contents of variables - not the names of the variables used (A$ and NumVar). Output appears on the screen at the current cursor position.
After priniting to the screen, the cursor position drops down to the start of the next line on the screen - ready for the next print statement. You can prevent this from happening (so you can print more on the end of the same line) by placing a ; (semi-colon) on the end of the Print line.
Following a Print statement with a ; on the end, the next Print statement will appear on the end of the line just printed.
Print "Somewhere"
Print "Over"
Print "The"
Print "Rainbow"
will make the following appear on the screen:
Somewhere
Over
The
Rainbow
Whereas...
Print "Somewhere";
Print "Over";
Print "The";
Print "Rainbow"
will result in:
SomewhereOverTheRainbow
...appearing. Notice that everything is all joined together? That's because we didn't include a space after each word inside the quotes. Don't worry too much about that as it's very unlikely you would ever use the Print statement like that anyway - you would just use:
PRINT "Somewhere Over The Rainbow"
Another useful variation of using PRINT is:
Print "Somewhere ";"Over ";"The ";"Rainbow"
Which at first glance doesn't appear to be of much use. However, when combined with variables allows you to created formatted strings. Take for example:
A$="Henry VIII"
WifeCount=6
Print A$;" had ";WifeCount;" wives."
This will print onto the screen:
Henry VIII had 6 wives.
As you can see, this allows you to present personalised messages in your games. Also worth noting is that you can also include variable formulas in Print statements too. Such as:
A$="Henry VIII"
WifeCount=6
Print "If ";A$;" had married again he would have had ";WifeCount+1;" wives."
This will print:
If Henry VIII had married again he would have had 7 wives.
There are other ways to control what appears on screen when using strings, but they are covered later in the 'More Strings' section.
SET CURSOR
As mentioned in the Print command section, Print will print your text at the current cursor position. If however you want to print something somewhere else, you need to be able to position the cursor where you need it. This is done with the SET CURSOR X,Y command where X is the position across the screen and Y is the position down the screen.
Set Cursor 10,10
Print "Cursor position 10,10 is here!"
Note: The X and Y referred to here is NOT a pixel position, but a character position, so the above message does not print at 10 pixels across and 10 pixels down. The values you can use depend on how many characters will fit across and down the screen - ie the screen mode.
STR$()
Str$() is a function which will convert a number or numeric variable into a string. This is most commonly used for the Text command - why it is introduced here. An example:
A=42
MeaningOfLife$=Str$(A)
Print MeaningOfLife$
This will print '42' on the screen, but it's important to realise that it is the string 42 - not the number 42! Also, as a string, any of the many string functions can be applied to it which couldn't when it was a numeric variable. This is a valuable feature which will become more apparent later on.
TEXT
This is a much improved version of the Print command and uses the format:
TEXT X,Y,Output$
...where X and Y are the desired
pixel positions (not character positions) on screen and Output$ is the required information you want to appear. The output part is similar to when using the Print command so you can use literal strings or variable strings. The main difference is that you cannot use numeric variables with the Text command as you can with Print - you need to convert them to strings first using the Str$() function.
Another difference is that when using Text to create formatted strings, the + symbol is used instead of the ; symbol.
Also, unlike the Print command, anything placed on the screen with Text is printed in the current font face and size - Print just uses the default system font. Our above Print example using the Text command to place the message 100 pixels across the screen and 100 pixels down the screen would look like this:
A$="Henry VIII"
WifeCount=6
Text 100,100, A$+" had "+Str$(WifeCount)+" wives."
CENTER TEXT
A variation on the Text command is the Center Text command which will - believe it or not - print a message centred around a given X position on the screen. The command syntax is:
Center Text X,Y,Output$
...where the parameters are the same as the normal Text command apart from X which is the screen X position at which the text is to be centred.
In other words, X is the position on screen at which the centre of your string will be positioned. If your screen is 800x600, then for your message to be in the centre of the screen along the X axis you would use:
Center Text 400,300,"This is in the centre of the screen"
The command does all the string length calculations for you and all the usual Text command formatting rules apply. You must figure out the Y position yourself!
Graphics Commands:
As well as text output using the Print and Text commands, you also have the ability to output basic pixel graphics. Once again, DB is aimed at the 3D games programmer, so you do not have a substantial subset of such commands - they just aren't called for often enough.
DOT
This command lets you 'turn on' a single pixel on the screen and is basically a 'Plot' function. Using the syntax:
DOT X,Y
...the pixel at co-ordinate X,Y will appear in the current Ink colour.
BOX
This command creates a filled box and uses the syntax BOX Left,Top,Right,Bottom. In effect, Left and Top define the X and Y co-ordinates of the box's top left corner while Right and Bottom define the X and Y co-ordinates of the box's bottom right corner. Unlike some BASICs, in DB you do NOT define the top left corner position then the box's width and height.
As with all 2D graphics commands, the Box command uses the current Ink colour.
LINE
This command will draw a line in the current ink colour. The syntax is:
LINE StartX,StartY,EndX,EndY
...and you simply supply the X and Y screen co-ordinates of the start and end of the line.
CIRCLE
This command will draw a circle in the current ink colour. The syntax is:
CIRCLE CentreX,CentreY,Radius
...and you simply supply the X and Y screen co-ordinates of the circle's centre along with the circle's radius in pixels.
ELLIPSE
This command draws an ellipse in the current ink colour. The syntax is ELLIPSE CentreX,CentreY,Radius1,Radius2 and you simply supply the X and Y screen co-ordinates of the ellipse's centre along with the ellipse's X radius and Y radius in pixels.
POINT()
This function will return the colour number of a pixel on the screen. Using the syntax
Point(X,Y)
...X and Y are the required pixel's X and Y co-ordinates. The value returned is the colour value which needs to be converted to R, G and B values to be of any real use.
Non-Output Commands:
The following commands do not send anything to the screen - they just alter the
way things are sent.
SET TEXT OPAQUE
This command switches text transparency off and applies only to the Text command - not Print. When transparency is off then the current text background colour is shown. If this colour is not the same as the current screen colour then the text will appear in a coloured rectangle in the chosen Ink background colour.
SET TEXT TRANSPARENT
This command switches text transparency on and applies only to the Text command. When transparency is on, then the text background colour is not displayed and the background screen colour shows through. Enter and run this example...
CLS RGB(30,0,50)
Ink RGB(255,255,255),RGB(150,0,0)
Set Text Opaque
Print "1. This is text produced with the Print command"
Text 100,100,"Text produced with the Text command (Opaque)"
Set Text Transparent
Print "2. This is text produced with the Print command"
Text 100,120,"Text produced with the Text command (Transparent)"
In this example, the screen is cleared to a dark purple colour and the ink set to white foreground and red background.
The text is set to opaque and a message is then printed to the screen using both Print and Text commands. Notice that only the message produced with the Text command has the visible background red colour whereas the Print command text is unaffected.
The text is then set to transparent and the colour scheme left unaltered. The messages are printed again and this time the Text command does not show the red background colour.
You will also notice that the Text command has no effect on the screen cursor position. The message created with the Print command appears on the next line to the last printed message - even though a Text command has placed a message in the middle of the screen since the Print command.
More Strings:
As mentioned earlier, there are a lot of useful string formatting functions which can be used - some of which we will cover now. Many can be used to present numeric information on the screen in a more tidy fashion. For example, ever noticed in games where the player's score is say 3250, it appears on the screen as 0003250? The score is always 7 digits long even though the actual score is only 4 digits!
The score is stored in a numeric variable and you can't add on the leading zeros. So, you convert the score from a number to a string, use the string functions to add those zeros and print the resulting string onto the screen rather than the contents of the numeric variable. Just one of the many things you can do with strings.
So let's look at what we need in order to do the score thing...
LEN()
Len is short for Length and as the name suggests, the Len() function will tell you the length of a string. If our player's score (say it's 200 for example) is stored in the numeric variable Score, then we can't use a string function on it so we have to convert it to a string first with Str$() which we covered earlier.
This is done with:
ScoreStr$=Str$(Score)
Now we have a string variable called ScoreStr$ which contains the players score value (200) as a string. We can now use Len() to tell us how long the string is with:
ScoreLen=Len(ScoreStr$)
You will notice that Len() returns a number - not another string. This is so we can use the number in calculations. In this case, Len() will return the value 3 as the score 200 is three characters long. We can use this number to find out how many 0's to add to the front of the string. We want our score to always be printed onto the screen with 7 digits, so we create a small program loop which will repeat the Len() function until the string is the required length:
ScoreStr$=Str$(Score)
Repeat
ScoreStr$="0"+ScoreStr$
ScoreLen=Len(ScoreStr$)
Until ScoreLen=7
What this does is add 0 onto the front of our string containing 200 and then test the length of the string. If it is less than 7 then the loop is repeated. When the last 0 is added to make ScoreLen equal to 7 then the loop is exited.
All well and good - apart from one major problem... can you spot it?
What if the current score is say 1253843? In this case, the length of score as a string is already 7 so the loop will add another 0 to the front making it 8 long. When you get to the Until ScoreLen=7 condition for continuing the loop, it will not be 7 so the loop will continue adding 0's trying to reach 7 - and of course it never will!
So, what we have hit on is a prime example of when you have to choose your loops carefully. We said in an earlier tutorial that there were different ways to create a loop and they seemed to do the same thing so why have so many?
Let's see the above code written using a different type of loop and you should see the difference more easily:
ScoreStr$=Str$(Score)
While Len(ScoreStr$)<7
ScoreStr$="0"+ScoreStr$
EndWhile
It's shorter, so therefore faster at doing the job, but more importantly the While...EndWhile loop will not be entered at all if the current score is already 7 characters long (or in fact longer) - unlike the Repeat...Until version. This is because the condition for carrying out the code inside the loop is at the beginning of the loop - not the end!
Knowing which type of loop to use and where, comes when you have a little more experience, but the point is that they do all have their differences - and uses.
On the subject of speed, you will quite often hear people talking about doing something 'this way' instead of 'that way' as it's faster. Most computers these days have fast processors - measured in GigaHertz rather than MegaHertz. My first PC had a 33MHz processor and 4MB of memory! So, you will probably think that speed is no longer an issue. However, there are a couple of things you have to consider:
1. Not everyone has a super-fast computer. There are still a lot of older machines out there, so what runs smoothly on your machine may not do so on eveyone else's machines. This however is only important if you intend other people to use the programs you write. If you only write stuff for yourself then you don't need to worry about this aspect.
2. Dark Basic Classic is an interpreted language. When your program has grown quite large (as they always do) and there is a lot going on, it will slow down even on the fastest of machines. DBPro is a compiled languages and is therefore a lot fasterand less of a problem in this respect.
So, although using one method may only be a tiny fraction quicker, all these fractions can add up and overall make a significant difference in the speed that your program runs.
Chopping And Changing
Sometimes, you need to manipulate strings. As with all BASIC's, DB provides a number of commands to do this. When you are learning to program in DB you start off with text only programs then move onto 2D graphics then finally 3D graphics. Those who are completely new to programming and jump straight into 3D don't usually last very long.
Actually, you would probably be surprised how vital the string commands can be for all types of program - 2D and 3D as well as text programs. Formatting information printed on the screen, shuffling a pack of cards and saving files to disk all involve working on strings...
LEFT$()
This function will pull out a number of characters (a substring) from the beginning (left end) of any string. The syntax is:
Left$(Main$,NumChars)
...where Main$ is the string you want to extract the substring from and NumChars is the length of the required substring. If the value used for NumChars is greater than the length of Main$ then just the available characters are returned.
As with all DB commands, you can use literal strings where the data is entered directly into the command, or variables. Examples:
Print Left$("Children",5)
... will print 'Child'
So, assuming that A$="ABCDEFGHIJKLMNOPQRSTUVWXYZ":
Print Left$(A$,5)
... will print 'ABCDE'
B$=Left$(A$,3)
... will take 'ABC' from A$ and place it in B$
A trivial example which demonstrates the sort of thing possible with Left$(). Just copy it into DB and run it!
A$="Darth Vader was the baddy in Star Wars."
B$="k is the 11th lower case letter of the alphabet."
C$="Basil is a herb."
D$="icicles are made of frozen water."
Print Left$(A$,3);Left$(B$,2);Left$(C$,3);Left$(D$,2)
RIGHT$()
This is exactly the same as Left$() but the substring is taken from the end of the main string rather than the start. The syntax is also the same - Right$(Main$,NumChars).
Print Right$("Children",5) ... will print 'ldren'
And again assuming that A$="ABCDEFGHIJKLMNOPQRSTUVWXYZ":
Print Right$(A$,5)
... will print 'VWXYZ'
B$=Right$(A$,3)
... will take 'XYZ' from A$ and place it in B$
MID$()
The third in the collection is MID$() which allows you to pull out a substring from the middle of the main string. To be honest though, saying 'substring' is a little misleading as the syntax is
Mid$(Main$,StartPos)
...and as you can see there is no 'length' parameter like many versions of BASIC provide - just a start position. This means that you can only grab one character at a time.
If you need to extract more than one character to build a substring you can do so with a loop. Example time:
Print Mid$("Children",3)
... will print 'i'
Once again, assuming that A$="ABCDEFGHIJKLMNOPQRSTUVWXYZ":
Print Mid$(A$,5)
... will print 'E'
B$=Mid$(A$,4)
... will take 'D' from A$ and place it in B$
To grab a substring from say position 7 to 11 in A$ you would use:
A$="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Sub$=""
For N=7 To 11
Sub$=Sub$+Mid$(A$,N)
Next N
... after which Sub$ would equal 'GHIJK'. For those of you with prior knowledge of other BASIC's, this is the same as Mid$(A$,7,5).
As you can see, using Left$(), Right$(), Mid$() and adding strings together, you can do some really clever stuff. There are however some other goodies...
VAL()
This function will return the numeric value of a string containing a number. In effect it is the reverse of Str$() so if you have Score=1000, A$=Str$(Score) will create a string called A$ containing '1000'. Score=Val(A$) will then turn the '1000' string back into a numeric variable called Score - replacing what was there already.
Things You Should Know About VAL()
If the string you are getting the value of starts with anything other than a number, 0 (zero) is returned.
If the string starts with a number but also contains other non-numeric characters then VAL will return the value of the numeric characters
UP TO (but not including) the first non-numeric character. It's up to you to check that the string you are using with Val() is in the correct format.
Examples:
Age=Val("32"): Rem Numeric variable Age will contain the value 32
Age=Val("24 Years"): Rem Numeric variable Age will contain the value 24 ('Years' ignored)
NumVal=Val("HSGH32433"): Rem Numeric variable NumVal will contain the value 0
ASC()
ASC is short for ASCII and this function will return the ASCII code of a single character. Each alpha-numeric character capable of being printed to the screen has an ASCII code. The character 'A' for example has the ASCII code 65, 'B' is 66 and so on. Also, remember that numbers also have ASCII codes, so Asc("7") is legitimate and will return the ASCII code of the character '7'.
Asc() can accept a string, but only the first character will be checked. Asc("F") will return exactly the same ASCII code as Asc("Fred") as only the F is decoded. Likewise, Asc("7") will return exactly the same ASCII code as Asc("73621").
Asc() can also be useful for sorting lists of strings into alphabetical order.
CHR$()
This is the reverse of ASC() and given an ASCII code will produce the equivalent string character. Printing CHR$(65) to the screen will result in a capital 'A'.
UPPER$() and LOWER$()
These two functions are used on strings to convert the string's contents to upper and lower case respectively. A good use for Upper$() is when you need to compare two strings in your programs.
Say for example you were writing a program where the user has to enter a string and what your program does next depends on what is typed. In a space game for example, your 'on-board computer' may ask for a destination planet which the user types in. Your code may say:
Input "HAL: What is your new destination? ",Planet$
If Planet$="Neptune" Then Gosub Dest_Neptune
But, what if the user typed NEPTUNE or neptune - or any other combination of upper and lower case characters? Well, the subroutine would never get called - and they would never get to Neptune!
You would have to have an If line for every possiblilty - and that's only for a single destination!
Or, you could just use Upper$()!
Input "HAL: What is your new destination? ",Planet$
If Upper$(Planet$)="NEPTUNE" Then Gosub Dest_Neptune
Now, it doesn't matter how Neptune is typed, as long as it's spelled correctly, as the upper case version will always be 'NEPTUNE' and the subroutine will always be called. The original contents of Planet$ in this example are unchanged - you are just creating a temporary upper case version to compare with.
If you want to do a permanent conversion, you would use:
Planet$=Upper$(Planet$)
... when the user originally enters the information.
Yet More String Functions!
If you are reading these tutorials as you are new to DB but not programming, then you may notice that DB isn't bursting with string functions. This is because DB is primarily a game writing language and generally speaking there isn't a great need for them in games. What you do get is usually sufficient though.
If however you need something more, I would suggest that you download my string function library from the downloads section of my web site (link in Sig at the botom of this post). It contains some 16 new functions including INSTR(), an improved MID$() which has the missing 'length' parameter, multi-colour text printng and more.
Other Useful Commands For Beginners
INC
Inc is short for Increment and is a quick way to do addition with variables. If we wanted to add an amount to the variable 'A' we would normally use A=A+1 (or + any other value). This can be replaced with the following:
Inc A: Rem Increments the value currently in numeric variable A by 1
Inc A,3: Rem Increments the value currently in numeric variable A by 3
DEC
Inc is short for Decrement and is a quick way to do subtraction with variables. If we wanted to subtract an amount from the variable 'A' we would normally use A=A-1 (or - any other value). This can be replaced with the following:
Dec A: Rem Decrements the value currently in numeric variable A by 1
Dec A,3: Rem Decrements the value currently in numeric variable A by 3
RND()
All games rely on an amount of randomness. A card game wouldn't be any fun if the cards always came out in the same order or the aliens came down the screen in the same place every time. So, you have a function RND() in DB which will return a random number from a given range. Using the syntax Rnd(Value), it returns a value between 0 and the supplied value. The number returned is inclusive, so Rnd(10) can return 0, 10 or any number between.
The seed for the random number generator is based on a register in your PC's hardware when it is turned on and doesn't change until you restart your PC. As such, the random numbers are the same every time you run the program - hardly random. Run the following program a few times:
For N=1 to 5
Print Rnd(10)
Next N
You will see that each time you run it, the same 5 numbers are produced. To generate a different sequence of numbers each time it is run, we need to re-seed the random number generator with RANDOMIZE.
RANDOMIZE
This command will re-seed the random number generator and uses the syntax:
Randomize Seed
...where Seed is the numeric seed value.
However, if the seed is a constant number then the next set of randomly generated numbers will be different, but only the first time they are generated - from then on they will be repeated. Still not random!
So, what we need to do is use a different number for the seed each time the program is run and the PC's built in clock is ideal for this purpose. Timer() is a DB function which will return the number of milliseconds which have elapsed since the PC was turned on. Using this as a seed will give us a different set of random numbers each time the program is run. Simply use:
Randomize Timer()
at the start of your program. To test that this works, add this line to the above example to make:
Randomize Timer()
For N=1 to 5
Print Rnd(10)
Next N
Run this a few times and you should see a different set of numbers each time.
Random Numbers In A Specific Range
OK, so Rnd(10) will give us random numbers from 0 to 10, but what if we want the numbers to be between say 100 and 150?
Well this isn't difficult. All we have to do is calculate the actual range of numbers - if we want a number between say 100 and 150 then we use 150-100 giving us an actual range of 50. As the smallest number we actually want to be returned is 100, that's what we add to our random number.
A=Rnd(50)+100
... will do the trick!
Getting Information Into Your Programs:
Your programs would be pretty useless if there was no user-interaction - especially games! The user may need to enter their name, select buttons on the screen, guide a spaceship, steer a vehicle or simply answer questions. This is done by the mouse and keyboard or a joystick.
A number of commands are available to capture this information and in keeping with the beginners tutorial theme I'll just be covering the basic essentials for the novice programmer.
The Mouse...
As you move the mouse around the screen, it generates an X and Y value and if your mouse has a wheel, it will create a Z value when moved. The buttons also create a value, so let's see what functions we have to use to get these values...
MouseX(), MouseY() and MouseZ()
These three functions return the respective mouse values as integers, though it's unlikely you will need MouseZ() at this stage of learning. The syntax is:
NumVar=MouseX()
NumVar=MouseY()
NumVar=MouseZ()
where NumVar is the integer variable you want to store the relevant mouse position:
MouseClick()
This function returns an integer value representing the current mouse button status. When no buttons are being pressed this function returns 0.
The left button is given the value 1, the right button the value 2 and the middle button (if present) the value 4.
Pressing more than one button returns the total of the pressed button's values so pressing the left and right buttons simultaneously returns 3 (1+2). Pressing the centre button with the right button returns 6 (2+4).
At first, you will only be interested in whether or not the left or right button has been pressed so you will only need to check if the value returned is simply 1 or 2 and act accordingly.
In your programs just place the following line at the beginning of your main program loop:
Mx=MouseX(): My=MouseY(): Mc=MouseClick()
With this line, at any point in your program, Mx will equal the mouse's X position, My it's Y position and Mc the button status.
Clickable Screen Buttons
With the combination of knowing the X and Y position of the mouse at any time, along with the button status you can create areas of the screen designated as 'clickable' in a button fashion. All you need to do is check to see if the mouse is within these areas and if it is, when the mouse button is pressed. An example for you to try:
Ink RGB(255,0,0),0
Box 100,100,200,120
Ink 0,0
Box 101,101,199,119
Ink RGB(255,255,255),0
Text 135,102,"Exit"
Set Text Opaque
Do
Mx=MouseX(): My=MouseY(): Mc=MouseClick()
If Mx>100 and My>100 and Mx<200 and My<120
If Mc=1
End
Else
Text 0,0,"Mouse Now Over Button. "
Endif
Else
Text 0,0,"Mouse Not Over Button. "
Endif
Loop
This creates a red button on the screen with it's top left corner at X=100 Y=100 and it's bottom right corner at X=200 Y=120. The word Exit is then printed in the middle of the button and the text set to opaque so that messages on the screen overwrite each other without messing up the screen.
In the main Do...Loop the mouse functions are called to get the X, Y and button values and we do a test to see if the current mouse X and Y position is within the defined button area. If it is not, then the Else part of the code is executed and the message "Mouse Not Over Button. " is displayed.
If the mouse is within the button area another test is made on the current value of Mc - to see if the left mouse button is pressed (Mc=1). If it is not (Mc=0), then once again, only the Else part of the code is executed and the message "Mouse Now Over Button. " is displayed.
If the mouse button is pressed then the condition is met and the program ends - just like the label on the button says!
You would repeat this process so you had as many sections of code as you had buttons - each checking for different X/Y co-ordinates and doing different tasks for each button.
Also, the buttons here are drawn with code and look very basic. There's nothing stopping you from loading in images for your buttons and placing them on the screen. As long as you know the X/Y co-ordinate of each button's top left corner and the width and height of each button, the process to detect which one has been clicked on is identical to that outlined above.
HIDE MOUSE
This command simply removes the pointer from the screen when you don't want it visible. While it is hidden, it will still return all the normal values allowing you to replace the cursor with an image of your own if you want to.
SHOW MOUSE
This turns the mouse pointer back on.
The Keyboard...
Getting information via the keyboard has more options. The first one is INPUT, but it has it's limitations so you also have Inkey$() and Scancode(). Each are used for a specific task.
INPUT
This is the main keyboard input function you will use at first, though it does have it's bad points. It's slow and clunky and only uses the default system font and colours. It uses the syntax:
Input StringVar$ or
Input Message$,StringVar$
...and your program literally stops running until the user types something in and presses the Enter key. This makes it OK for entering the players name on the hiscore table or at the start of the game - but little else.
Input Name$
This will halt the program and wait for the user to type in their name and press the Enter key at which point what they typed in will be placed into the string variable Name$. The program will then carry on it's merry way.
Input Age
This will do exactly the same, but this time a numeric value is expected which when correctly entered will be stored in the numeric variable 'Age'. If a non-numeric entry is attempted, it will be ignored.
This raises an important point. As nothing appears on the screen, how on earth is the user expected to know exactly what they are supposed to type in?
That's what the alternative Input method is for:
Input "Please Enter Your Name: ",Name$
Input "Please Enter Your Age: ",Age
This version of Input will print a message on the screen before waiting for the user's input so at least they know what the program is expecting them to type. You can of course replace the literal string with a string variable if you wish.
At some later stage, you will decide that you don't like the way that the standard Input command works. With a bit more knowledge you will probably decide to write your own function to do it.
Inkey$()
This function is called a 'polling' function as it polls the keyboard for keypresses without stopping like Input does. When a key is pressed, it is stored in a string variable for use. Press the b key and the letter b is stored. Press the p key and the letter p is stored and so on.
The next time the Inkey$() function is called the previous value is lost so if you don't 'grab' it while it's there, it's immediately replaced by something else. When no key is pressed, the variable used contains a NULL string ("").
Inkey$() only works with characters which have ASCII codes. Some keys do not return any value at all - such as the Shift, Control and Function keys.
So a good way to use this function is to create a loop which is 'locked' until the correct key is pressed. Here's a useful Yes/No example:
Start:
CLS
Print "Do you want to end this program (Y/N)?"
Repeat
I$=Upper$(Inkey$())
Until I$="Y" or I$="N"
If I$="Y"
End
Else
Goto Start
Endif
OK, I know I said using Goto is bad, but this is only an example to demonstrate using Inkey$() and I wanted to keep it short. I could have written a mere half a dozen lines to avoid it, but it wouldn't have demonstrated Inkey$() any differently. In your proper programs just try to avoid Goto OK?
The example prints the message and drops into a closed loop which cannot be exited from unless the user presses the Y or N key. Inside the loop, Inkey$() grabs the current state of the keyboard and places the upper case version of it in the string variable I$.
As soon as the user presses either Y or N, the condition will be met and the program drops out of the loop and into the If..Else...Then test where I$ is tested to see if it equals "Y" or "N". If it is Y then the program ends. If it is anything else (and it can only be N if it isn't Y as any other keypress would not have allowed an exit from the loop), then the program is not exited and is allowed to continue.
How about "Press Any Key To Continue" messages?
This is just as easy using Inkey$(). We know that when no key is being pressed, then Inkey$() will return a NULL string, so all we need to do is stay in our loop until anything but "" is returned:
Print "Press Any Key To Continue"
Repeat
I$=Inkey$()
Until I$<>""
We don't need the Upper$ bit as it doesn't matter what key is pressed. The loop will keep going until I$ doesn't equal "" and that can only happen if a key is pressed.
"OK, clever clogs" I hear you ask, "What can you use for menus"?
Inkey$() again! Take a look at the following simple example and then we'll go through it...
Center Text 320,100,"1. Menu Item 1"
Center Text 320,120,"2. Menu Item 2"
Center Text 320,140,"3. Menu Item 3"
Center Text 320,160,"4. Menu Item 4"
Center Text 320,180,"5. Menu Item 5"
Center Text 320,200,"6. Menu Item 6"
Repeat
I$=Inkey$()
Until Asc(I$)>48 and Asc(I$)<55
CLS
If I$="1" Then Print "You selected menu option 1"
If I$="2" Then Print "You selected menu option 2"
If I$="3" Then Print "You selected menu option 3"
If I$="4" Then Print "You selected menu option 4"
If I$="5" Then Print "You selected menu option 5"
If I$="6" Then Print "You selected menu option 6"
The first six lines simply print the menu entries 1 to 6. The loop is like the previous examples with the exception of the exit loop condition. This time, we use ASC() to test the ASCII value of the key pressed. The number 1 key has an ASCII value of 49 and 6 has the value 54. So, we only exit the loop if the key pressed has an ASCII value greater than 48 and less than 55 - in other words only the number keys between 1 and 6.
On exiting the loop, the screen is cleared and the value of I$ is checked and the appropriate message printed depending on what I$ equals. In your program, you would Gosub a routine rather than print a message, but the method is still the same.
That will do for the Inkey$() function so let's take a look at the last keyboard input method - Scancode:
Scancode
This function will return a code representing the physical key on the keyboard as opposed to what is actually printed on the key. This code value is totally unconnected with ASCII codes. For example, with Asc(), pressing the A key on it's own will return 97 - the ASCII code for lower case 'a'. Pressing the A key with the shift key will return 65 - the ASCII code for upper case 'A'.
Scancode uses the syntax NumVar=Scancode() and returns 30 when you press the A key. There is no shifted version as the shift key has it's own Scancode. Remember, the 30 represents the key itself - not what's on it.
It's also worthwhile remembering that not all keyboards across the world are the same.
For example, your program uses the Q button to accellerate your vehicle and it uses the Scancode 113 to detect when it's pressed. On your instruction screen you say 'Press Q to go faster'.
Pierre in France however presses Q and nothing happens! That's because he uses a French keyboard on which the top row of letters are not QWERTY, but AZERTY. His Q key is in a different place and the Scancode of his Q key is different. I use a Spanish keyboard and it has a different layout too.
If you had used Inkey$() instead of Scancode, then the Q key would have worked on all keyboards! You may need to use Scancode, but bear this in mind.
Keystate
As each and every key on the keyboard generates it's own Scancode, then it's impossible to detect more than one key at a time using it as the last key you press replaces the code generated by the previous key pressed - even if it's still being held down.
To get around this, you can use Keystate() which uses the syntax:
Keystate(Keycode)
...where Keycode is the Scancode for the key you are polling. When the corresponding key is not being pressed, Keystate returns 0 and a 1 when it is.
The cursor up key has the Scancode 200 and Space Bar is 57. If you wanted to use the cursor up key to move forward and the space bar to fire, then Scancode on it's own would not work as you would stop moving every time you fired. With Keystate it is possible to do both at the same time using something like:
If Scancode()=200
Rem Move Forward
If Keystate(57)=1
Rem Fire
Endif
Endif
To be honest, at this point we have only scratched the surface of commands in the DB library, but the commands covered - few as they may be, are quite sufficient to write some quite sophisticated programs.
The only thing left to cover which we haven't done already is File Access, so we'll do that in the next part of the tutorial.
TDK_Man