Beginners Guide To Programming - Part III Elementary Commands

Started by kevin, April 21, 2008, 07:13:16 AM

Previous topic - Next topic

kevin


TDK's Play Basic Programming For Beginners

Back To Index

Part 3 - Elementary Commands Part#1 of 2

 In this part of the series, we will be looking at some of the elementary commands in Play Basic - many of which will work in any version of Basic and are therefore 2D only. PlayBASIC's more advanced 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 PB (Text & 2D), then you have no chance of understanding the Shakespeare stuff when you 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 Play 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, PB programs run in a screen size (resolution) of 800x600. This means that X runs from 0 to 799.

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 PB's default resolution of 800*600 Y will run from 0 to 599.

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.




OPENSCREEN


 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 OpenScreen Width,Height,BitDepth,DisplayType.

 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.  DisplayType lets you select if want a windowed (1) or full screen (2) display.   Personally, I tend to do everything with:

OpenScreen 800,600,16,2

 This will open a full screen exclusive display  that's 800 by 600 pixels in size with 16bit colour.

[Edit] This tutorial was written a few years ago when most 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 PB 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

... and

Ink RGB(Red,Green,Blue)

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 RGBColour

 You can think of changing the INK colour, much like drawing a picture with a set of coloured pencils, where changing INKs represents changing to a different coloured pencil.


; set the INK co,lour to a Bright RED
  Ink rgb(255,0,0)
  ;draw a line
  LIne 100,100,400,200
 
; set the INK co,lour to a Bright GREEN
  Ink rgb(0,255,0)
  ;draw a line
  LIne 400,200,150,250
 

; set the INK co,lour to a Bright GREEN
  Ink rgb(0,0,255)
  ;draw a line
  LIne 150,250,100,100

  Sync
  Waitkey





CLS


The CLS command clears the  screen to a user defined colour.



   ; clear the screen to RED
   Cls rgb(255,0,0)

   ; display a message
   Print "A Red Screen"

  ; tell PB to Show the screen and wait for a key press
   Sync
   waitkey
 







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 printing to the screen, the cursor position drops down to the start of the next line on the screen - ready for the next print statement.



There are other ways to control what appears on screen when using strings, but they are covered later in the 'More Strings' section.





SetCursor


 As mentioned in the Print command section, Print will display 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 SetCursor X,Y command where X is the position across the screen and Y is the position down the screen.


SetCursor 10,10
Print "Cursor position 10,10 is here!"






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$
Sync
Waitkey


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.


A$="Henry VIII"
WifeCount=6
Text 100,100, A$+" had "+Str$(WifeCount)+" wives."
Sync
Waitkey





CENTERTEXT


 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:

   CenterText 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.




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,FilledFlag.   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.   The last parameter FillFlag, lets you specify if PB should draw open (0) or filled (1) box.

  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,FillFlag

    ...and you simply supply the X and Y screen co-ordinates of the circle's centre along with the circle's radius in pixels.  The last parameter FillFlag, lets you specify if PB should draw open (0) or filled (1) circle.



 


ELLIPSE


   This command draws an ellipse in the current ink colour. The syntax is ELLIPSE CentreX,CentreY,Radius1,Radius2,FillFlag 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.  The last parameter FillFlag, lets you specify if PB should draw open (0) or filled (1) circle.




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.






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:

+ Code Snippet

ScoreStr$=Str$(Score)
Repeat
 ScoreStr$="0"+ScoreStr$
 ScoreLen=Len(ScoreStr$)
Until ScoreLen=7



 What this does is adds '0' character 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:

+ Code Snippet

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.  Since PlayBasic is an interpreted language, the methods in which you choose solve your programming problems, can dramatically enhance or hinder your program performance.   A generally a rule of thumb is that the first programs we write as beginners, tend to bit a clunky.   So as our knowledge improves, so does our ability to write more elegant and better performing programs..


  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, PB provides a number of commands to do this. When you are learning to program in PB 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 PB string 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!

+ Code Snippet

A$="Playing drums in fun."
B$="y are you looking at me ?"
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)
Sync
WaitKey





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,NumberOfChrs)


  Mid$() can be used to grab any many characters as you like from a string.  The following examples we'll demonstrate how to grab individual characters from the string.  For example:

   Print Mid$("Children",3,1)

    ... will print 'i'
 
   Once again, assuming that A$="ABCDEFGHIJKLMNOPQRSTUVWXYZ":

    Print Mid$(A$,5,1)

    ... will print 'E'


     B$=Mid$(A$,4,1)

    ... 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:

       B$=Mid$(A$,7,4)

      This will grab characters at position 7,8,9 & 10 from the A$ and return to B$

     ... aof b$ would equal 'GHIJK'.


A$="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
B$=Mid$(A$,7,4)
print b$
Sync
WaitKey



 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 characters '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:


Age1=Val("32"): Rem Numeric variable Age will contain the value 32
Age2=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

; Display the values so we can see what Val returned for each one
Print Age1
Print Age2
Print NumVal
Sync
Waitkey



Example #2  VAL#()


; The val() function convert the string to an integer, So bellow
; we'll get 124  the .456 part is lost in translation
Print Val("123.456")


; The val#() function convert the string to an float, So this
; time we'll get what we're expecting.
Print Val#("123.456")

Sync
Waitkey



Example #3  Using VAL() to convert HEX$() and BIN$() values back to integers



; Create a integer variable SCORE and assiugn the value 123456
 Score = 123456

; Display score
 Print Score


; HEX$() convert converts the integer variable (123456) to string representation in Hexadecimal format.
; Hex is base 16 number system.  While it looks a bit bizarre to us humans, believe it or not
; computers love it.  
 HexScore$=Hex$(Score)

; Display this Value "$0001E420"
 Print HexScore$

;  use Val() to convert the hex format string, back into integer value and something we can understand.
 print val(HExScore$)  
 
Sync
Waitkey



 This also works for BINARY formated strings BTW.





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:


Planet$=StaticInput("HAL: What is your new destination? ")
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 Dest_Neptune would never get called - and they would never get to Neptune!    This is because when we compare two strings with the = operator, the comparison requires that the two string match perfectly.   Even the capitalization of the characters matters. So as far as programming languages goes, the strings "PlayBasic" and "PLAYbasic" are not a match.

 While you could have an If line for every possible capitalization the user may type, there is a simpler way.

  Or, you could just use Upper$()!

 Planet$=StaticInput("HAL: What is your new destination? ")
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.





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





DEC


  Dec is short for Decrement and is a quick way to do subtraction upon a variable.   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





RND()


  Pretty much 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 PB 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.

  You might not realize it, but computers are not actually very good at generating truly random numbers.  In fact  the vast majority of random generators can only product a set number randoms in any one sequence  before they start to repeat.     PB is no exception here.  However, I wouldn't worry about it too much, as the random sequences are many millions of numbers long (ie. you'd have to call Rnd() millions of times before you could possibly notice the start of the set).    Which is more than enough for gaming purposes.      


+ Code Snippet

For N=1 to 5
 Print Rnd(10)
Next N
Sync
Waitkey


   You will see that each time you run it, you'll set 5 different numbers are produced.    However,  what if you want to specifically seed the random number generator to produce a known set of numbers ?   We can do this with the RANDOMIZE command.




RANDOMIZE


 This command will re-seed the random number generator and uses the syntax:

   Randomize Seed

    ...where Seed is the numeric seed value.


  Now if the seed is a constant number (Say 42) then this initializes (Seeds) the random number generator to produce a particular set of numbers.   If we did this at the start of our program (I.e. Randomize 42),  then every time we ran our program, we'd get the same sequence of numbers generated.    While it might not seem that useful initially, this approach is often used generate graphical elements in games and computer demos.  Often called procedural generation.

  Check this a very simple example, if you run it a few times you'll get the same result.  While the picture is creates is not very interesting (a few random circles), the sky is the limit here with this concept..


; Tell PB to initialize the random generator with specific seed.
; This will make it program produce the same result every time
; try changing the value asn see what you get
Randomize 42
  For lp=0 to 100
      ink rndrgb()
      Circle Rnd(800),Rnd(600),50,true
  Next
 Sync
 Waitkey



  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 PB 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:

+ Code Snippet
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?   For this we'd use the RndRange(lowValue,HighVlaue) function.   The is specific version of the RND() built to return random values between a user specified range.

  A=RndRange(50,100)

  ... will do the trick!




kevin


TDK_Man's Play Basic Programming For Beginners

Part 3 - Elementary Commands Part#2 of #2





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:





MouseButton()


  This function returns an integer value representing the current combination of mouse buttons that are depressed. 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=MouseButton()

  With this line, at any point after this line 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:

+ Code Snippet


Do
  Cls Rgb(0,0,0)

; Draw the button
Ink RGB(255,0,0)
Box 100,100,200,120,true
Ink rgb(0,0,0)
Box 101,101,199,119,true
Ink RGB(255,255,255)
Text 135,102,"Exit"

; get the mouses position and button state
  Mx=MouseX(): My=MouseY(): Mc=MouseButton()
 
  ; Check if the mouse position is within the
  ; buttons rectangle
  If Mx>100 and My>100 and Mx<200 and My<120
     ; If it
    If Mc=1
      End
    Else
      Text 0,0,"Mouse Is Now Over Button. "
    Endif
  Else
    Text 0,0,"Mouse Not Over Button. "
  Endif

  Sync

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.

   PB actually has some built in functions for this particular purpose.  Those being MouseInBox and PointInBox.  The first if for detecting the mouse is within a rectangular area and the later is for detecting if a coordinate with a rectangular area.  Lets modify our example to use PointInBox. 

   

Do
  Cls Rgb(0,0,0)

; Draw the button
Ink RGB(255,0,0)
Box 100,100,200,120,true
Ink rgb(0,0,0)
Box 101,101,199,119,true
Ink RGB(255,255,255)
Text 135,102,"Exit"

; get the mouses position and button state
  Mx=MouseX(): My=MouseY(): Mc=MouseButton()
 
  ; Check if the mouse position is within the
  ; buttons rectangle
  If PointInBox(Mx,my,100,100,200,120)
     ; If it
    If Mc=1
      End
    Else
      Text 0,0,"Mouse Is Now Over Button. "
    Endif
  Else
    Text 0,0,"Mouse Not Over Button. "
  Endif

  Sync

Loop







SHOW & HIDE MOUSE


   Given that you can create programs that run within a Window.  This means that the Windows mouse pointer will be visible if the user moves the mouse over your programs screen.  Most of thime this is not really issue, but sometimes you'll have to HIDE the windows mouse and display your own mouse pointer graphics.  We can control the visibility of the windows mouse pointer using the Mouse command. 

   Mouse OFF  - Will hide the mouse pointer, and Mouse ON would show it again.

   This will only affect your program though, if the user moves the mouse outside of your pograms screen, then the normal windows mouse will reappear.



; Turn the Windows mouse pointer off
Mouse OFF

Do
  Cls Rgb(0,0,0)


; get the mouses position and button state
   Mx=MouseX()
   My=MouseY()
   Mc=MouseButton()
 
; if the button is not pressed, we'll draw an open circle
if MC=0
Circle MX,MY,10,false
else
; if the button was pressed we'll draw a filled circle
Circle MX,MY,10,true
endif
  Sync

Loop






The Keyboard...


  Getting information via the keyboard has more options.  Such as  Inkey$(), Scancode(), KeyState() and StaticInput (also know as Input). Each are used for a specific task.




Inkey$()


  This function is called a 'polling' function as it polls the keyboard for keypresses.    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:

+ Code Snippet

Repeat
CLS rgb(0,0,0)
Print "Do you want to end this program (Y/N)?"
Sync

; Enter the inner  repeat until loop.  This loop will wait until the either ther Y or N keys are pressed
Repeat
  I$=Upper$(Inkey$())
Until I$="Y" or I$="N"


; Print a message when you Press "N"
if I$="N"
Print "You Pressed The N Key"
Sync
Wait 500
endif

   ; A key was pressed.  If the Key was a "Y", then the UNTIL condition will be true,
   ; and our main repeat/until loop will finish.
until I$="Y"

; if the user selected Y then we tell PB to END this program
  End



The example prints the message and drops into a closed repeat until loop which cannot be exited from unless the user presses the Y key. Inside the main loop we have another repeat until loop. This inner loop uses Inkey$() to grab 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 of the inner loop will be met and the program drops out of the loop and into the If..EndiF test where I$ is tested to see if it equals " "N".   If it is N then the program display and message wait and second and continues.   If it is anything else (and it can only be Y if it isn't N as any other keypress would not have allowed an exit from the loop), then the program skips pass the IF/EndIF statement to the outer Until statement.

  The Main loop of our program is using a Repeat/Unitl to keep the program running until the statement after the Until is true.  For this to true the user much have pressed the "Y" key.  Sp if the I$ variable equals "Y" then main repeat/until is completed and the PB with leave the loop and contonie on to the END statement.  Which will close the program.



  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:

+ Code Snippet

Print "Press Any Key To Continue"
Sync
Repeat
  I$=Inkey$()
Until I$<>""



   Since we're not worry about what keys was pressed (as we're happy to continue if any key was pressed) then we don't need to convert the returned INKEY$() string into it's uppercase format.   The loop will keep going until I$ doesn't equal "" and that can only happen if a key is pressed.


  Note: I Should point out that the above loop is the basically equivalent to the "WaitKey" command


  "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...

+ Code Snippet

; Display the menu options
CenterText 320,100,"1. Menu Item 1"
CenterText 320,120,"2. Menu Item 2"
CenterText 320,140,"3. Menu Item 3"
CenterText 320,160,"4. Menu Item 4"
CenterText 320,180,"5. Menu Item 5"
CenterText 320,200,"6. Menu Item 6"
Sync

; wait for a Number key to be pressed
Repeat
  I$=Inkey$()
Until Asc(I$)>48 and Asc(I$)<55
Flushkeys

CLS rgb(0,0,0)
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"

Sync
Waitkey



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 accelerate 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:

+ Code Snippet

If Scancode()=200
  Rem Move Forward
  If Keystate(57)=1
    Rem Fire
  Endif
Endif





STATIC INPUT


    Up until now, we've been looking at some methods of detecting and responding to user input from the mouse and simple key presses. But what if we want the user to respond to a question ? - Such as "What's your name?". This is one area where PlayBasic breaks away from the original dialects of the BASIC. Most traditionally forms of BASIC have a command called INPUT. However, in PlayBasic we've chosen to change the functionality and design of the original command. For both technical and design reasons.

    One  issue with the original command was that it largely comes from the text/dos era of computing. Programming languages of that era (pre 1980's) where primarily text based.  This design largely comes a from the types of computer hardware that was available then.   In those days bitmap graphics hardware was very limited. Both by the quality of the picture that could be displayed the and computing power required to render and move images.   Something we take for granted today. Back then, it was massive achievement to display a picture on some systems, let alone move or do anything with it.    Where Text only display modes we're considerably faster.   So it's no surprise that the major of games back then we're entirely text based. 

    However the graphics hardware we use in modern PC's are focused entirely around graphical interfaces. As such, game creation languages like PlayBasic are designed and work within this graphical environment also. What's the big deal ? - Well, without getting too deep into programmer jargon here, it comes down to text displays being single buffered.   While graphical displays are double buffered, to keep the visuals as flicker free as possible.   In a single buffered display we only have one copy of the screen image in the computers memory.  So the image we draw to, is the one the user of the computer is viewing.  This is not the case with double buffered displays.   With double buffering, we actually have two copies of the screen image inside the computers memory.   One copy is the what user sees and the other copy is the one we are drawing to.  When we've finished drawing we flip the screens and repeat.  This makes animation make more pleasant for the viewer, but makes implementation of such commands difficult.

   OK- So how do I get some basic input from the user then ? -  For this, we've added an optional function called StaticInput().    We've change the name to avoid confusion with the original INPUT command here.  But it virtually does the same thing. 

    Static input uses the following syntax.

          TextUserEntered$=StaticInput(Prompt$)

     The 'Prompt$' string can be string literal for string variable.  StaticInput will use this text as the input prompt, this might be a question or a character even.   This is optional,  you can simply enter a NULL string ("") if you don't want a prompt.     

     When StaticInput is called, the function halts our program until the users pressed the Enter key.   Upon pressing Enter, StaticInput will return whatever the user typed as string.

    Example.

   Name$=StaticInput("What's your ?:")   
   Month$=StaticInput("What's Month is it ?:")   
   Print Name$
   Print Month$
   Sync
   Waitkey

   
    If you cut and paste those into PlayBasic and try and run them,  PlayBasic will give you an error message.  Something along the lines of  StaticInput is not defined.     Well remember above where we mention that Static input is an optional function.      This means that StaticInput is not a built in command by default.   So in order to activate it, we need to tell PlayBasic to include the "Input"  library.   In short, this a library of input helper commands.   Since not every program will need them they are optional.

    So add StaticInput  support to our program we need to add the line  "#Include "Input" to the top of our program.   Like so..


   ; Tell PB to include Input library support
   #include "Input"

   ; ask the user a question and the return whatever they typed into the NAME$ variable
   Name$=StaticInput("What's your ?:")   

   ; ask the user a question and the return whatever they typed into the Month$ variable
   Month$=StaticInput("What month is your birthday in ?:")   

   ; display the Name$ and Month$ variables
   Print Name$
   Print Month$
   Sync
   Waitkey


    How can I set where  my input statement appears ? -  We do this same way as with using the Print statement via using the SetCursor command.   StaticInput displays it's question at the current cursors position.   Moreover it also uses the current font and ink settings. 
     


   ; Tell PB to include Input library support
   #include "Input"

   ; Position the cursor at x position 300x and y position 200y on the screen
    SetCursor 300,200

   ; ask the user a question and the return whatever they typed into the NAME$ variable
   Name$=StaticInput("What's your ?:")   

   ; display the Name$
   Print Name$
   Sync
   Waitkey




   Can StaticInput Return numbers ?

       StaticInput returns whatever characters the users types.  The users is free to enter any character on the keyboard be they alphabetical and number characters or something else.   So therefore it can be used to obtain either type of input.  However, what it can't do it screen for numerical input only.   To counter this, we use the VAL() function to convert the returned string (the characters the users the typed) from StaticInput into a number.



   ; Tell PB to include Input library support
   #include "Input"

   ; ask the user a question and the return whatever they typed into the TypedText$ variable
   TypedText$=StaticInput("Pick a Number Between 1 and 10 ?:")   

   ; use VAL() to convert whatever the user typed in a Integer Value
    ThisNumber = Val(TypedText$)

   ; Display the number
     Print "You Entered:"+TypedText$

   ; check if this number is within the range or not ?
   if ThisNumber<1 or ThisNumber>10
      ; If not, print a message
    Print "Hey that number is not between 1 and 10"
   else
      ; If it is, print a success message
print "Success - This number is between 1 and 10"
   endif


   Sync
   Waitkey



      If you experiement with this example you'll notice something odd if you enter any alphabetical characters.   For example if you typed "abc" The Number it returns is ZERO.  This occurs as VAL() simply doesn't know what to do with no numeric characters.  So what you might also want to do is screen the returned input string for invalid characters perhaps, rather than reply upon the user entering something that may or may not be valid.   Which i'll demonstrate bellow.



   ; Tell PB to include Input library support
   #include "Input"

Do
; Clear the Screen
Cls rgb(0,0,0)

   ; ask the user a question and the return whatever they typed into the TypedText$ variable
   TypedText$=StaticInput("Pick a Number Between 1 and 10 ?:")   



if IsStringANumber(TypedText$)=false
Print "Hey that's not a number.. Try again"
Sync
Wait 1000
else
; If the number seems ok (doesn't have any illegal character in it, we exit the do/loop and continue on
ExitDo
endif

loop


   ; use VAL() to convert whatever the user typed in a Integer Value
    ThisNumber = Val(TypedText$)


   ; Display the number
     Print "You Entered The Number:"+str$(ThisNumber)

   ; check if this number is within the range or not ?
   if ThisNumber<1 or ThisNumber>10
      ; If not, print a message
    Print "Hey that numbers not between 1 and 10"
   else
      ; If it is, print a success message
print "Success - This number is between 1 and 10"
   endif


   Sync
   Waitkey



; This Function does a


Function IsStringANumber(ThisString$)
result=true
; create string that holds all of the legal charatcers a numeric string might contain
; everything else willl be illegal
LegalNumberCharacters$="0123456789.- "

   ; Use a FOR/NEXT loop to move through the String$ charcter by character
For lp=1 to Len(ThisString$)
; Grab this character from the string we wish to compare
      ThisChr$=Mid$(ThisString$,lp,1)

; Check if this chr is within the Legal charcters string or not, if not this string isn't a valid number.
If InString(LegalNumberCharacters$,ThisChr$,1,false)=0
; This chacter wasn't found in the legal character string,
; so we'll change the Result flag to false.  So this string
; is not considered a number
result=false
endif
next
EndFunction Result




     I won't go through the sample entirely, but effectively we've added a function that will scan through all of the characters in a string and compare them to a list of characters that are legal numeric characters.  The function isn't actually concern with the formatting of the passed string though,  just what characters the string contains.   So it'll happily  tell you the following string are numeric,  even when they're obviously mall formed.    Ie.  "23.-3",  "....1",  "1000-"   

     This bring me to a critical point about program design.  As programmers,  it's important for us to beware that we must verity all input from the user.   Don't simply assume that the user entered something that makes sense,  they often won't.  This is a very common pitfall for new programmers, in particular when designing programs for other people to use.   So beware! - A general rule of thumb is that more error checking you do, the better !





   To be honest, at this point we have only scratched the surface of commands in the PB 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