Lesson 5: Graphics
BASIC originally came with a primitive set of graphics commands to create dots, lines, boxes, circles and to allow painting of screen areas. Even though the commands are very simple a creative programmer could do very interesting things with them. In this lesson we'll cover these primitive commands and in a later lesson step it up with new fancy graphics commands offered by QB64.
A quick note about new QB64 commands first
In this lesson commands are going to be introduced that were not native to QuickBasic but instead introduced by QB64. These new commands enhance the BASIC language to take advantage of today's more powerful computers and features that were not available many years ago. Most new commands introduced by QB64 start with an underscore ( _ ) character. This was done for compatibility reasons since QB64 was written to allow BASIC source code from the 1970's and later to compile and run.
One of the new commands you'll learn about is _NEWIMAGE(). Let's say someone wrote a program back in 1992 and created a variable in their code called NewImage%. If QB64 did not precede these new commands with an underscore that variable created way back then would be interpreted as a command in QB64. By adding the underscore to new commands old source code has a better than 99% probability of compiling and running just fine in modern versions of QB64.
A programmer can also create custom subroutines and functions and name them anything they like (more on subroutines and functions in Lesson 6). If a programmer made a function called NewImage() then again QB64 would interpret this as a command rather than a custom function written many years ago.
There are literally hundreds of thousands, if not millions, of BASIC source code files scattered around the Internet. Because the developers of QB64 took compatibility into account you can be fairly certain that code from 1989 you found on the Internet will compile and run in QB64 today.
Colors
Before we get into graphics commands we need to learn about color use in QB64 first. Most graphics commands have a color option attached to them allowing for color customization. While there are options in QB64 to work with 256 color screens this course is only going to work with 32bit color (16 million) screens. This is just going to be a quick introduction to colors in QB64. We will delve into this topic in much greater detail in Lesson 14.
The _RGB32() statement is the most common way to set or create colors in QB64. There are a wide variety of color commands available in QB64 but this will be the one we use in this lesson. _RGB32() returns an unsigned long integer data type. Unsigned long integers have a range of 0 through 4,294,967,295 which is also the maximum value of a 32bit number (232 = 4,294,967,296). The _RGB32() statement creates colors by mixing red, green, and blue values together.
Color~& = _RGB32(red%, green%, blue%)
There are 256 levels of each color available (0 through 255). By varying the levels for red, green, and blue you can make up to 16 million different color combinations (256 * 256 * 256 = 16,777,216). There is also a fourth optional parameter for changing the alpha layer, or transparency of a color, using the same 256 levels. This is why we need to use an unsigned long integer variable type to store color information (red(256) * green(256) * blue(256) * alpha(256) = 4,294,967,295). Unsigned long integers can be created with the use of ~& (the tilde character means "unsigned" and as we learned before the & character means long integer). Some examples are:
BrightRed~& = _RGB32(255, 0, 0) ' red set to full intensity
Red~& = _RGB32(127, 0, 0) ' red set to half intensity
BrightGreen~& = _RGB32(0, 255, 0) ' green set to full intensity
BrightBlue~& = _RGB32(0, 0, 255) ' blue set to full intensity
White~& = _RGB32(255, 255, 255) ' all three colors set to full intensity
Gray~& = _RGB32(127, 127, 127) ' all three colors set to half intensity
Many times you'll see programs that have constants with common colors used throughout the source code:
CONST WHITE~& = _RGB32(255, 255, 255)
CONST GRAY~& = _RGB32(127, 127, 127)
CONST DARKGRAY~& = _RGB32(63, 63, 63)
CONST PURPLE~& = _RGB32(255, 0, 255) ' red and blue full intensity
CONST YELLOW~& = _RGB32(255, 255, 0) ' red and green full intensity
We'll get much deeper into the workings of color and the other color statements available in Lesson 14.
The SCREEN Statement
Up to this point we have been doing everything in a text only screen (known as SCREEN 0) where graphics are not permitted. The first thing we need to do is get QB64 into a graphics screen mode. Think of a graphics screen as a blank sheet of graph paper with each individual little square at your command. Each one of those little squares is known as a pixel which is the smallest point that can be referenced on a graphics screen. A pixel is literally a single point of colored light that you can control. Your computer's monitor screen is made up of millions of pixels (2,073,600 for a 1920x1080 monitor). You can reference each and every one of those pixels using an X,Y coordinate system built into QB64.
Legacy Screens
The SCREEN statement is used to put QB64 into a graphics mode. SCREEN 0 is the text mode we have been working with up to this point and is considered the default SCREEN. Since QB64 was developed to support GWBASIC and QuickBasic source code from the 80's and 90's, SCREEN 1 through SCREEN 13 graphics modes are also available. However, these legacy SCREEN graphics modes are very limited in both size and colors available when working with today's graphics cards and monitors. As graphics cards evolved over time (MDA, CGA, EGA, VGA) the BASIC language had to add new SCREEN modes to take advantage of them. The syntax for the legacy SCREEN statement is (used in GWBasic, QuickBasic, and supported by QB64):
SCREEN mode%, , active%, visual%
mode% sets the screen into the desired mode (see list below)
active% is the page of video RAM currently being written to (more on this in a bit)
visual% is the page of video RAM currently being displayed on the monitor (again, more in a bit)
NOTE: there is a parameter missing between the first two commas in the syntax above. This was used for color switching (on and off) in GWBasic and QuickBasic and is no longer supported by QB64.
Here is a list of the legacy SCREEN modes supported by QB64:
SCREEN 0: Text mode only (MDA - Monochrome Display Adapter and greater)
The default BASIC screen when no SCREEN mode is supplied.
40 x 25 or 80 x 25 text only with up to 2 colors (16 colors with CGA and greater)
SCREEN 1: 320x200 Color Graphics (CGA - Color Graphics Adapter and greater)
40 x 25 text with up to 16 colors (but only 4 at a time from selected palettes)
SCREEN 2: 640x200 Monochrome Graphics (CGA and greater)
80 x 25 text with up to 2 colors (black and white)
SCREEN 7: 320x200 Color Graphics (EGA - Enhanced Graphics Adapter and greater)
40 x 25 text with up to 16 colors
SCREEN 8: 640x200 Color Graphics (EGA and greater)
80 x 25 text with up to 16 colors
SCREEN 9: 640x350 Color Graphics (EGA and greater)
80 x 25 or 80 x 43 text with up to 16 colors (from a palette of 64 colors)
SCREEN 10: 640x350 Monochrome Graphics (EGA and greater)
80 x 25 or 80 x 43 text with up to 2 colors (black and white)
SCREEN 11: 640x480 Monochrome Graphics (VGA - Video Graphics Array and greater)
80 x 30 or 80x 60 text with up to 2 colors (black and white)
SCREEN 12: 640x480 Color Graphics (VGA and greater)
80 x 30 or 80 x 60 text with up to 16 colors from a palette of 262,144 colors (256K)
SCREEN 13: 320x200 Color Graphics (VGA and greater)
40x25 text with up to 256 colors from a palette of 262,144 colors (256K)
You probably noticed the omission of SCREEN modes 3 through 6. These modes were available on systems with specialized video adapters such as the IBM PCjr computer, the Tandy 1000 series of computers (known as TGA), the Hercules video adapter, the Olivetti M series of computers, and the AT&T 6300 series systems (an Olivetti M24 in disguise). These systems shipped with a modified version of GWBasic to take advantage of the enhanced video adapters they included. QuickBasic never natively supported these specialized legacy SCREEN modes although there were third party libraries available if these modes were needed but it was still quite uncommon.
A program included with the tutorial named LegacyModes.BAS displays all of the above SCREEN legacy modes along with their color capabilities. Load the program into your IDE and run it now. Press the ENTER key to move through the SCREEN modes or the ESC key to exit at any time.
( This program can be found at .\tutorial\Lesson5\LegacyModes.bas )
Figure 0: The legacy SCREEN modes
As you can tell after running the program these legacy screens are quite small and limited in their color capabilities. SCREEN 13, the 320x200 256 color mode, was by far the most popular when writing games in QuickBasic but it would be much too small to be usable on today's large monitors. You can literally fit 30 SCREEN 13s onto a standard 1920x1080 wide screen monitor! Heck, all of them fit into a nice little image as Figure 0 illustrates above. It's also important to note that while some of these SCREEN modes are extremely rectangular, on a CRT monitor they would have been stretched to a 4:3 aspect ratio creating pixels greater in height than in width.
The _NEWIMAGE Statement
SVGA (Super Video Graphics Array) and XGA (eXtended Graphics Array) added the screen modes 800x600, 1024x768, and 1280x1024. Other standards followed to offer even higher resolutions to accommodate wide screen and high definition formats. QB64 allows the creation of these formats by pairing the SCREEN statement with the _NEWIMAGE() statement. _NEWIMAGE creates an image canvas in RAM identified by a numeric handle value that can be used as the output screen. QB64 has modified the SCREEN syntax as follows to allow this:
SCREEN ImageHandle&, ,active%, visual%
ImageHandle& is any image created through the use of QB64's image statements
active% is the page of video RAM currently being written to (more on this in a bit)
visual% is the page of video RAM currently being displayed on the monitor (again, more in a bit)
The syntax for the _NEWIMAGE statement is:
Handle& = _NEWIMAGE(Width%, Height%, Mode% {0|1|2|7|8|9|10|11|12|13|256|32})
Width% is the width of the image in pixels to be created
Height% is the height of the image in pixels to be created
Mode% is the image mode to emulate (0 for text, 1 through 13 for legacy, 256 for 256 color, and 32 for 32 bit color)
To create an SVGA screen of 1024x768 that supports 32 bit color (16+ million colors) the following line of code would be used:
SCREEN _NEWIMAGE(1024, 768, 32) ' create a 1024x768 32 bit color graphics output screen
_NEWIMAGE(1024, 768, 32) creates an image canvas in RAM and assigns a numeric value to it. The SCREEN statement uses that numeric value to identify the output screen. When _NEWIMAGE is paired with the SCREEN statement that numeric value will always be zero (0). Statements that you'll learn about in later lessons allow you to identify the image canvas to draw on. Image zero (0) will always be the output screen.
It's even possible to create a text only screen with custom text character dimensions:
SCREEN _NEWIMAGE(132, 66, 0) ' create a text only output screen of character dimension 132x66
By choosing a Mode% of zero (0) the _NEWIMAGE statement will emulate the text only legacy SCREEN 0. Here we've chosen to make the screen 132 characters wide by 66 characters high. These are the exact dimensions of a wide format sheet of printer paper. Note that when emulating SCREEN 0 the dimensions supplied are in characters and not pixels. Emulating screen modes 1, 2, 7, 8, 9, 10, 11, 12, and 13 need dimensions supplied in pixels. The same is true for modes 256 (256 colors) and 32 (32 bit color).
The _PIXELSIZE statement
At some point in your program you may need to know which mode was used to create an image or SCREEN. The _PIXELSIZE statement returns one of three values that identify the mode your SCREEN or an image is in. The syntax for the _PIXELSIZE statement is:
Psize% = _PIXELSIZE(ImageHandle&)
ImageHandle& is the numeric handle value of the image you wish to test. Psize% will contain one of three values:
0 - the image or output screen is text mode only
1 - the image or output screen is emulating legacy modes 1 through 13 or 256 color
4 - the image or output screen is in 32 bit color mode
Remember, the current output screen will always be zero (0) so just supply a value of zero (0) for ImageHandle& to identify the current SCREEN mode.
SCREEN _NEWIMAGE(640, 480, 32) ' 32 bit color mode
PRINT _PIXELSIZE(0) ' will print the value of 4
While _PIXELSIZE may not seem to be a handy statement at the moment it will become useful later on as your programming skills progress. The value _PIXELSIZE returns is actually the number of bytes needed to store a single pixel's color information in RAM. This information is required when dealing with direct image memory manipulation or routines that need to create new images that match the current mode of existing images or the output screen.
The Graphics SCREEN
Lesson 13 will introduce the different ways that QB64 can create images but in this lesson we'll focus on the _NEWIMAGE() statement to create an image that can be used as an output screen. Type in the following program that illustrates how a graphics screen is created and laid out as shown in Figure 1.
( This code can be found at .\tutorial\Lesson5\ScreenDemo.bas )
'------------------------
'- SCREEN demonstration -
'------------------------
CONST WHITE = _RGB32(255, 255, 255) ' set colors
CONST YELLOW = _RGB32(255, 255, 0)
SCREEN _NEWIMAGE(640, 480, 32) ' switch into a 640x480 graphics screen
CLS ' clear the graphics screen
PSET (0, 0), WHITE ' turn on white pixels
PSET (319, 239), WHITE
PSET (639, 479), WHITE
LINE (5, 5)-(50, 50), YELLOW ' draw yellow lines
LINE (5, 5)-(5, 15), YELLOW
LINE (5, 5)-(15, 5), YELLOW
LINE (319, 234)-(319, 189), YELLOW
LINE (319, 234)-(309, 224), YELLOW
LINE (319, 234)-(329, 224), YELLOW
LINE (634, 474)-(584, 424), YELLOW
LINE (634, 474)-(624, 474), YELLOW
LINE (634, 474)-(634, 464), YELLOW
LINE (0, 279)-(639, 279), YELLOW
LINE (0, 279)-(10, 269), YELLOW
LINE (0, 279)-(10, 289), YELLOW
LINE (639, 279)-(629, 269), YELLOW
LINE (639, 279)-(629, 289), YELLOW
LINE (150, 0)-(150, 479), YELLOW
LINE (150, 0)-(160, 10), YELLOW
LINE (150, 0)-(140, 10), YELLOW
LINE (150, 479)-(160, 469), YELLOW
LINE (150, 479)-(140, 469), YELLOW
LOCATE 4, 2 ' place text on screen
PRINT "Coordinate 0,0"
LOCATE 5, 3
PRINT "(White dot)"
LOCATE 11, 34
PRINT "Center 319,239"
LOCATE 12, 35
PRINT "(White dot)"
LOCATE 26, 62
PRINT "Coordinate 639,479"
LOCATE 27, 66
PRINT "(White dot)"
LOCATE 18, 23
PRINT "Width of screen 640 pixels (0-639)"
LOCATE 25, 3
PRINT "Height of screen 480 pixels (0-479)"
SLEEP ' wait for user to press a key
SYSTEM ' return to Windows
Figure 1: A 640x480 graphics screen
A graphics screen of 640 pixels wide by 480 pixels high was created using this line of code:
SCREEN _NEWIMAGE(640, 480, 32) ' switch into a 640x480 graphics screen
It's important to remember that to a computer zero is a number like any other and most operations start with zero instead of one. In the example program we created a 640 by 480 screen but Figure 1 points out that the coordinate system is actually referenced as 0-639 for width and 0-479 for height. That's because of the computer starting with zero. If you count zero as a number then 0 through 639 is actually 640 pixels and 0 through 479 is 480 pixels. That's also why in Figure 1 the center point is referenced as 319, 239 instead of 320, 240. Because the computer starts at zero everything seems "off" by one pixel. You'll need to remember this when creating graphics using QB64. Note that this is not something QB64 alone does, all programming languages use zero as a starting point for their graphics screen.
SCREEN Pages and Flipping
An output screen can have multiple pages associated with it. Remember these two methods of creating output screens?
Legacy SCREEN:
SCREEN mode%, , active%, visual%
mode% sets the screen into the desired mode
active% is the page of video RAM currently being written to
visual% is the page of video RAM currently being displayed on the monitor
QB64 modified SCREEN:
SCREEN ImageHandle&, ,active%, visual%
ImageHandle& is any image created through the use of QB64's image statements
active% is the page of video RAM currently being written to
visual% is the page of video RAM currently being displayed on the monitor
It's time to discuss the active% and visual% video RAM pages associated with them. Legacy SCREENs were created in the video card's RAM. In many cases an output screen would only use a portion of that video RAM. That extra video RAM could be used to create multiple instances of the output screen that could be called upon.
For instance, If you had an EGA video card that contained 256K of RAM and created a game using SCREEN 7 (320x200 with 16 color) that output screen only required 32K of video RAM. The remaining video RAM could be broken into 32K chunks containing more output screens for a total of 8 screens (256K divided by 32K). Those 7 extra screens could then be written to and viewed at will. Many GWBasic programmers took advantage of this and used these hidden pages to hold sprite images instead of using the precious little system RAM that early computers had. Through the use of the GET and PUT graphics statements these sprite images could be copied from a hidden screen and written to the output screen.
I used this to my advantage while writing my Connect Four game back in 1992 (more on that here). When the game was first started page 0, the output screen, remained blank until page 1 was finished drawing the title screen. Page 1 was then copied to page 0 to reveal the title screen while page 2 drew the sprites needed for the game. When that was finished an image of the playing field was drawn on page 3. The whole time this drawing was going on in the background page 0 was still showing the title screen. Page 3 now contained a clean image of the playing field whenever I needed it, page 1 held a copy of the title screen, and page 2 a copy of the sprites. Once all the drawing was finished page 3 was copied to page 0 and the game began. When the game finished I could simply copy page 3 back to page 0 to clear the screen with a fresh playing field to start a new game. Or, if the player decided to exit the game back the title screen I could simply copy page 1 which contains a copy of the title screen back to page 0. Drawing graphics on a Tandy 1000EX 8088 computer was painfully slow in GWBasic but I took advantage of that by using that time to display just the title screen. The title screen would display for about 5 seconds, the time needed to draw all the graphics assets needed in the background. If you were to run that same program in QB64 today the title screen would blip by so fast you wouldn't even see it!
Traditional animation, film, and video is achieved by flipping images very quickly in front of the eye. This same "page flipping" technique was very common in QuickBasic using one page to draw on while the other was being shown, then the pages were flipped, with the opposite pages being drawn and shown. Here is a quick example of page flipping:
'---------------
' Page flipping
'---------------
x% = 159 ' x coordinate
y% = 99 ' y coordinate
a% = 1 ' active page being written to
v% = 0 ' visual page being shown
DO ' begin animation loop
SCREEN 7, , a%, v% ' set active and visual pages
IF y% > 0 THEN ' draw another square?
LINE (x%, y%)-(319 - x%, 199 - y%), 14, B ' yes, draw yellow square
x% = x% - 3 ' decrement x coordinate
y% = y% - 3 ' decrement y coordinate
END IF
a% = 1 - a% ' flip active page
v% = 1 - v% ' flip visual page
_DELAY .1 ' 1/10th second delay (10 FPS)
LOOP UNTIL _KEYDOWN(27) ' leave when ESC key pressed
While some legacy screens had more pages than others, and some of them only having one, QB64 has removed these limitations completely. All SCREENs, regardless of mode (this includes legacy SCREENs), can have as many video pages as available system RAM allows. This even includes SCREEN 0 and an emulated SCREEN 0 created with _NEWIMAGE.
The PCOPY Statement
The PCOPY statement allows the programmer to take advantage of the multiple pages associated with a SCREEN as discussed in the section above. The PCOPY statement allows any SCREEN page to be copied to a different SCREEN page. The syntax for the PCOPY statement is:
PCOPY SourcePage%, DesinationPage%
SourcePage% is the SCREEN page you wish to copy and DestinationPage% is the SCREEN page to receive the copy. Let's look at an example of this in code first.
Type the following program into your IDE.
( This code can be found at .\tutorial\Lesson5\PCOPYexample.bas )
'---------------------------------------------------
' PCOPY Example
'
' by Terry Ritchie 04/22/24
'
' A simple game using PCOPY to update the screen
'
' Page 0 - the main viewing page
' Page 1 - a copy of the title screen (animation 1)
' Page 2 - a copy of the title screen (animation 2)
' Page 3 - a copy of the game screen
'---------------------------------------------------
CONST WHITE~& = _RGB32(255, 255, 255) ' define the color white
CONST BLACK~& = _RGB32(0, 0, 0) ' define the color black
DIM ScreenImage& ' the main graphics screen
DIM ActivePage% ' page being written to
DIM ViewPage% ' page being viewed
DIM KeyPress$ ' any key press on keyboard
DIM x% ' player x coordinate
DIM y% ' player y coordinate
DIM ex! ' enemy x coordinate
DIM ey! ' enemy y coordinate
DIM Distance! ' distance from player to enemy
ScreenImage& = _NEWIMAGE(640, 640, 32) ' create main graphics screen
SCREEN ScreenImage&, , 1, 0 ' page 1 active and viewing page 0
'+-----------------------------------------------------+
'| Create an animated title screen using pages 1 and 2 |
'+-----------------------------------------------------+
LOCATE 15, 21 ' position text cursor
PRINT "WELCOME TO MY AWESOME MONOCHROME GAME!" ' print title screen text
LOCATE 18, 24
PRINT "PRESS ANY KEY TO BEGIN THE GAME!" ' print instructions
LOCATE 21, 33
PRINT "(ESC to Exit)"
PCOPY 1, 2 ' copy this page to page 2
ActivePage% = 0 ' set active page
FOR x% = 0 TO 640 STEP 64 ' cycle horizontally in steps of 64
SCREEN ScreenImage&, , ActivePage% + 1, 0 ' set active screen page
LINE (x%, 0)-(x% + 63, 63), WHITE, BF ' draw a filled box at top of screen
LINE (639 - x%, 575)-(575 - x%, 639), WHITE, BF ' draw a filled box at bottom of screen
ActivePage% = 1 - ActivePage% ' flip active page
NEXT x%
'+------------------------------+
'| Draw a game screen on page 3 |
'+------------------------------+
SCREEN ScreenImage&, , 3, 0 ' make page 3 the active page
CLS , WHITE ' clear the page white
LINE (10, 10)-(629, 629), BLACK, BF ' draw black box (creates border)
LOCATE 2, 18: PRINT "DON'T LET THE SOLID CIRCLE CATCH YOUR CIRCLE"
LOCATE 3, 27: PRINT "USE THE ARROW KEYS TO MOVE"
LINE (0, 50)-(639, 59), WHITE, BF ' draw border line under text
'+-----------------+
'| Begin main code |
'+-----------------+
SCREEN ScreenImage&, , 0, 0 ' active and view page set to 0
DO ' begin main program loop
ViewPage% = 0 ' set view page
DO ' begin title screen loop
_LIMIT 5 ' 5 frames per second
PCOPY ViewPage% + 1, 0 ' copy page 1 or 2 to page 0
ViewPage% = 1 - ViewPage% ' flip view page
KeyPress$ = INKEY$ ' get any key that was pressed
_DISPLAY ' update screen (remove flicker)
LOOP UNTIL KeyPress$ <> "" ' leave loop when key pressed
IF KeyPress$ = CHR$(27) THEN SYSTEM ' return to OS if ESC pressed
x% = 539 ' initialize player position
y% = 539
ex! = 99 ' initialize enemy position
ey! = 99
DO ' begin game play loop
_LIMIT 240 ' 240 frames per second
PCOPY 3, 0 ' clear screen with page 3 image
CIRCLE (x%, y%), 10, WHITE ' draw player
CIRCLE (ex!, ey!), 10, WHITE ' draw enemy
PAINT (ex!, ey!), WHITE, WHITE ' paint enemy
IF _KEYDOWN(19712) THEN x% = x% + 1 ' move player right
IF _KEYDOWN(19200) THEN x% = x% - 1 ' move player left
IF _KEYDOWN(18432) THEN y% = y% - 1 ' move player up
IF _KEYDOWN(20480) THEN y% = y% + 1 ' move player down
IF x% < 20 THEN x% = 20 ELSE IF x% > 619 THEN x% = 619 ' keep player x coordinate within limits
IF y% < 70 THEN y% = 70 ELSE IF y% > 619 THEN y% = 619 ' keep player y coordinate within limits
Distance! = _HYPOT(x% - ex!, y% - ey!) ' distance between player and enemy
ex! = ex! + (x% - ex!) / Distance! ' add normalized x vector to enemy
ey! = ey! + (y% - ey!) / Distance! ' add normalized y vector to enemy
SOUND (1660 - Distance! * 2), .03 ' higher pitch as enemy gets closer
_DISPLAY ' update screen (remove flicker)
LOOP UNTIL Distance! <= 20 ' leave when enemy catches player
PLAY "O4L8CO3L16GGL8A-GRBO4C" ' shave and a haircut 2 bits
_KEYCLEAR ' clear the keyboard buffer
LOOP ' end main program loop
Lines 34 through 39 of the code print the text instructions to the active page 1 as set in line 28. Since the title screen is going to be animated through the use of pages 1 and 2 line 40 of the code copies the text currently on page 1 to page 2.
SCREEN ScreenImage&, , 1, 0 ' page 1 active and viewing page 0
'+-----------------------------------------------------+
'| Create an animated title screen using pages 1 and 2 |
'+-----------------------------------------------------+
LOCATE 15, 21 ' position text cursor
PRINT "WELCOME TO MY AWESOME MONOCHROME GAME!" ' print title screen text
LOCATE 18, 24
PRINT "PRESS ANY KEY TO BEGIN THE GAME!" ' print instructions
LOCATE 21, 33
PRINT "(ESC to Exit)"
PCOPY 1, 2 ' copy this page to page 2
Lines 42 through 47 of the code draws solid boxes across the top and bottom of the screen. While these boxes are being drawn the active page is alternated between page 1 and page 2. This causes the boxes to be drawn in alternate positions on each active page and creates the animation effect. We now have our two title screen pages that can be flipped back and forth.
FOR x% = 0 TO 640 STEP 64 ' cycle horizontally in steps of 64
SCREEN ScreenImage&, , ActivePage% + 1, 0 ' set active screen page
LINE (x%, 0)-(x% + 63, 63), WHITE, BF ' draw a filled box at top of screen
LINE (639 - x%, 575)-(575 - x%, 639), WHITE, BF ' draw a filled box at bottom of screen
ActivePage% = 1 - ActivePage% ' flip active page
NEXT x%
Lines 53 through 58 of the code draws the game play screen on page 3. Page 3 can now be used to clear the screen during the game updates and retain the look of the playing field without having to redraw it over and over again.
SCREEN ScreenImage&, , 3, 0 ' make page 3 the active page
CLS , WHITE ' clear the page white
LINE (10, 10)-(629, 629), BLACK, BF ' draw black box (creates border)
LOCATE 2, 18: PRINT "DON'T LET THE SOLID CIRCLE CATCH YOUR CIRCLE"
LOCATE 3, 27: PRINT "USE THE ARROW KEYS TO MOVE"
LINE (0, 50)-(639, 59), WHITE, BF ' draw border line under text
Lines 67 through 73 in the main code section simply copies page 1 and page 2 using PCOPY repeatedly while waiting for the player to press a key. Since page 1 and page 2 have alternating graphics the view screen appears animated. These 2 pages eliminate the need to constantly redraw the animated squares while waiting for the player.
DO ' begin title screen loop
_LIMIT 5 ' 5 frames per second
PCOPY ViewPage% + 1, 0 ' copy page 1 or 2 to page 0
ViewPage% = 1 - ViewPage% ' flip view page
KeyPress$ = INKEY$ ' get any key that was pressed
_DISPLAY ' update screen (remove flicker)
LOOP UNTIL KeyPress$ <> "" ' leave loop when key pressed
Finally, line 81 of the code uses the image of the game playing field on page 3 to clear the screen in preparation for player and enemy movement and object redraws. Once again, no need to redraw the static parts of the playing field over and over again.
PCOPY 3, 0 ' clear screen with page 3 image
The PSET Statement
The PSET() statement (Pixel Set) is used to turn a single pixel on with a given color. PSET() requires the following parameters:
PSET(x%, y%), color~&
The x% and y% coordinate pair refer to the location on the screen where the pixel is to be manipulated. Color~& is provided by the _RGB32() statement explained above. In the previous example program PSET() was used to turn the upper left (0, 0), middle (319, 239), and lower right pixel (639, 479) on with a color of WHITE.
PSET (0, 0), WHITE ' turn on white pixels
PSET (319, 239), WHITE
PSET (639, 479), WHITE
The LINE Statement
The LINE statement is used to draw lines, boxes, and filled boxes. The LINE statement takes the following parameters:
LINE(x1%, y1%) - (x2%, y2%), color~&, BF, style~%
The LINE statement needs a start coordinate (x1%, y1%), an end coordinate (x2%, y2%), a color~&, an optional B to create a box or an optional BF to create a filled box. An optional style~% can also be defined giving the line a pattern. Type the following program into your IDE to see all of the different ways LINE can be utilized.
( This code can be found at .\tutorial\Lesson5\LineDemo.bas )
'----------------------
'- LINE demonstration -
'----------------------
CONST YELLOW = _RGB32(255, 255, 0)
SCREEN _NEWIMAGE(640, 480, 32)
CLS
LOCATE 2, 30
PRINT "Line Demonstration"
LOCATE 4, 16
PRINT "LINE (69, 89)-(569, 429), _RGB32(255, 255, 0)"
LINE (69, 89)-(569, 429), YELLOW
LOCATE 29, 34
PRINT "PRESS A KEY";
SLEEP
CLS
LOCATE 2, 30
PRINT "Line Demonstration"
LOCATE 4, 16
PRINT "LINE (69, 89)-(569, 429), _RGB32(255, 255, 0), B"
LINE (69, 89)-(569, 429), YELLOW, B
LOCATE 29, 34
PRINT "PRESS A KEY";
SLEEP
CLS
LOCATE 2, 30
PRINT "Line Demonstration"
LOCATE 4, 16
PRINT "LINE (69, 89)-(569, 429), _RGB32(255, 255, 0), BF"
LINE (69, 89)-(569, 429), YELLOW, BF
As the above example code illustrates the B and BF parameters are optional. (x1%, y1%) is also optional. If you omit the start coordinates (x1%, y1%) from the LINE statement it uses the last known position as a starting point. Type in the next example to see how this works.
( This code can be found at .\tutorial\Lesson5\LineDemo2.bas )
'-------------------------
'- LINE demonstration #2 -
'-------------------------
CONST YELLOW = _RGB32(255, 255, 0) ' define yellow
SCREEN _NEWIMAGE(640, 480, 32) ' switch into a 640x480 graphics screen
CLS ' clear the graphics screen
LINE (299, 219)-(319, 0), YELLOW ' draw a line
LINE -(339, 219), YELLOW ' draw a line from endpoint of last line
LINE -(639, 239), YELLOW ' draw a line from endpoint of last line
LINE -(339, 259), YELLOW ' ""
LINE -(319, 479), YELLOW ' ""
LINE -(299, 259), YELLOW ' etc..
LINE -(0, 239), YELLOW
LINE -(299, 219), YELLOW
LOCATE 16, 36
PRINT "PRESS A KEY"
SLEEP ' pause computer until key pressed
SYSTEM ' return control to OS
This is possible because a graphics cursor is kept track of by QB64. The graphics cursor resides at the endpoint of each line that is drawn by the LINE statement.
You can even add a little style to the lines you draw. Type in the next example program.
( This code can be found at .\tutorial\Lesson5\LineDemo3.bas )
'-------------------------
'- LINE demonstration #3 -
'-------------------------
CONST YELLOW = _RGB32(255, 255, 0) ' define yellow
DIM Style& ' the line's style
DIM Bit% ' the current bit in style to draw
DIM Dir% ' style direction
Dir% = -1
SCREEN _NEWIMAGE(640, 480, 32) ' switch into a 640x480 graphics screen
DO ' begin loop
CLS ' clear the graphics screen
_LIMIT 30 ' limit loop to 30 frames per second
LOCATE 2, 33 ' print directions
PRINT "A Stylized Line"
LOCATE 4, 21
PRINT "Press the Space Bar to change direction."
LOCATE 6, 23
PRINT "Press the Escape key to exit program."
IF _KEYHIT = 32 THEN Dir% = -Dir% ' change bit counter direction when space bar pressed
Bit% = Bit% + Dir% ' increment bit counter
IF Bit% = 16 THEN ' keep bit counter between 0 and 15
Bit% = 0
ELSEIF Bit% = -1 THEN
Bit% = 15
END IF
Style& = 2 ^ Bit% ' calculate the line style
LINE (99, 129)-(539, 409), YELLOW, B , Style& ' draw the line as a stylized box
_DISPLAY ' update screen with changes
LOOP UNTIL _KEYDOWN(27) ' leave loop when ESC key pressed
Figure 2: A line with style
Setting up line styles is done by creating a value that contains a 16 bit pattern. For example, if you were to create a line using the number 43690 you would get a line with every other pixel turned on. Using the value of 65280 you would produce a dashed line. Type in the following code and execute it to see different style values being used and how they affect the line's output.
( This code can be found at .\tutorial\Lesson5\LineStyle.bas )
'---------------
'- Line Styles -
'---------------
CONST YELLOW = _RGB32(255, 255, 0) ' define yellow
SCREEN _NEWIMAGE(640, 480, 32) ' switch into a 640x480 graphics screen
CLS
LINE (10, 10)-(629, 10), YELLOW, , 43690 ' a dotted line 1010101010101010
LINE (10, 50)-(629, 50), YELLOW, , 52428 ' a dotted line 1100110011001100
LINE (10, 90)-(629, 90), YELLOW, , 61680 ' a dashed line 1111000011110000
LINE (10, 130)-(629, 130), YELLOW, , 65280 ' a dashed line 1111111100000000
Decimal numbers ranging in value from 0 to 65535 translate to binary numbers containing 16 places (16 bits). The decimal number 43690 can be converted to the binary number 1010101010101010 creating a pattern that can be used in the line's style. Where ever a 1 resides the line's pixel is turned on and a 0 means the pixel is turned off. This pattern is repeated over and over throughout the length of the line. Likewise, 65280 converts to 1111111100000000 in binary so you get a dashed line, 8 pixels on, 8 pixels off, 8 pixels on, 8 pixels off, and so on.
We're not going to get into decimal to binary conversion here. You can simply use a calculator that supports programmer functions, such as the Windows calculator, to type in the binary pattern you wish and then have the calculator convert that pattern to decimal for you.
In the hands of a creative programmer the LINE statement can do some pretty wild things with very little code. Type the next program in to see the LINE statement on digital drugs.
( This code can be found at .\tutorial\Lesson5\Illusion.bas )
CONST CYAN = _RGB32(0, 255, 255) ' define cyan color
DIM x1%, y1% ' upper left coordinates
DIM x2%, y2% ' lower right coordinates
DIM Count% ' FOR...NEXT loop counter
DIM Bit% ' pixel to turn on in style
DIM Style& ' calculated style of line
SCREEN _NEWIMAGE(640, 480, 32) ' 640 x 480 graphics screen
DO ' begin animation loop
CLS ' clear the screen
_LIMIT 60 ' limit animation to 60 frames per second
x1% = 0 ' set starting coordinates
y1% = 0
x2% = 639
y2% = 479
PSET (0, 0), CYAN ' set pixel to start with
Bit% = Bit% + 1 ' increment style bit counter
IF Bit% = 16 THEN Bit% = 0 ' keep bit between 0 and 15
Style& = 2 ^ Bit% ' calculate line style
FOR Count% = 1 TO 60 ' cycle through line corkscrew 60 times
LINE -(x2%, y1%), CYAN, , Style& ' draw lines with style
LINE -(x2%, y2%), CYAN, , Style&
LINE -(x1%, y2%), CYAN, , Style&
y1% = y1% + 4 ' update coordinates
x2% = x2% - 4
y2% = y2% - 4
LINE -(x1%, y1%), CYAN, , Style& ' draw next line
x1% = x1% + 4 ' update coordinate
NEXT Count%
_DISPLAY ' update screen with changes
LOOP UNTIL _KEYHIT ' leave animation loop when key pressed
Figure 3: An animated illusion made with lines
And here is the code with straight lines.
Warning: This code creates an effect that may induce an epileptic seizure. If you are prone to such seizures do not execute this code.
( This code can be found at .\tutorial\Lesson5\Illusion2.bas )
CONST CYAN = _RGB32(0, 255, 255) ' define cyan color
DIM x1%, y1% ' upper left coordinates
DIM x2%, y2% ' lower right coordinates
SCREEN _NEWIMAGE(640, 480, 32) ' 640 x 480 graphics screen
x2% = 639 ' set starting coordinates
y2% = 479
PSET (0, 0), CYAN ' set pixel to start with
FOR Count% = 1 TO 60 ' cycle through line corkscrew 60 times
LINE -(x2%, y1%), CYAN ' draw lines
LINE -(x2%, y2%), CYAN
LINE -(x1%, y2%), CYAN
y1% = y1% + 4 ' update coordinates
x2% = x2% - 4
y2% = y2% - 4
LINE -(x1%, y1%), CYAN ' draw next line
x1% = x1% + 4 ' update coordinate
NEXT Count%
SLEEP ' leave when key pressed
SYSTEM ' return control to OS
Figure 4: Makes the eyes go doink!
The CIRCLE Statement
The CIRCLE statement is used to draw full or partial circles and ellipses onto a graphics screen. Let's start with a simple circle. The CIRCLE statement requires a minimum of three parameters:
CIRCLE (x%, y%), radius%, color~&
The (x%, y%) pair are coordinates located on the graphics screen at the center of the circle. radius% is the distance from the center point to draw the circle's outer boundary. Remember that the radius of a circle is half the diameter. color~& is optional but will probably always be supplied as the default white can get boring. Type the following code into your IDE.
Figure 5: A yellow circle
By default the CIRCLE statement draws a full 360 degree arc with an aspect ratio of one. You can override these values to draw only part of a circle, or an arc, by specifying the start and stop radian values of the arc. You can also supply an aspect ratio to change the circle into an ellipse.
CIRCLE (x%, y%), radius%, color~&, start_radian!, stop_radian!, aspect!
To better illustrate this type in the following program and execute it.
Note: Don't be intimidated by the length of the code. Type in one line at a time until you're finished. If you are unwilling or highly hesitant to type this tiny bit of code in you may want to rethink your programmer ambitions.
( This code can be found at .\tutorial\Lesson5\Radians.bas )
'--------------------------------
'- Variable Declaration Section -
'--------------------------------
CONST PI = 3.1415926
CONST RED = _RGB32(255, 0, 0)
CONST YELLOW = _RGB32(255, 255, 0)
DIM StartRadian! ' starting point of circle arc
DIM StopRadian! ' ending point of circle arc
DIM Aspect! ' aspect ratio of circle (ellipses)
DIM Sign% ' holds the sign (-1 or +1) of arc radians
'----------------------------
'- Main Program Begins Here -
'----------------------------
SCREEN _NEWIMAGE(640, 480, 32) ' initiate a graphics screen
StartRadian! = 2 * PI ' set initial arc starting point
StopRadian! = 0 ' set initial arc ending point
Aspect! = 1 ' set initial aspect ratio of circle
Sign% = 1 ' set radian sign
DO ' main loop begins here
CLS ' clear the screen
StopRadian! = StopRadian! + 2 * PI / 360 ' increment stop radian in 360ths
IF StopRadian! > 2 * PI THEN ' has arc done a full sweep?
StopRadian! = 0 ' yes, reset the arc end point
END IF
LINE (319, 119)-(319, 359), RED ' draw vertical red line
LINE (199, 239)-(439, 239), RED ' draw horizontal red line
CIRCLE (319, 239), 100, YELLOW, Sign% * StartRadian!, Sign% * StopRadian!, Aspect! ' draw yellow arc
LOCATE 2, 18 ' display instructions
PRINT "CIRCLE radian demonstration for creating arcs"
LOCATE 4, 30
IF Sign% = 1 THEN
PRINT "Stop radian ="; StopRadian!
ELSE
PRINT "Stop radian ="; -StopRadian!
END IF
LOCATE 5, 29
PRINT "Aspect ratio ="; Aspect!
LOCATE 7, 32
PRINT "Half PI 1.5707963"
LOCATE 24, 32
PRINT "1.5x PI 4.7123889"
LOCATE 15, 57
PRINT "0 or 2x PI 6.2831852"
LOCATE 15, 13
PRINT "PI 3.1415926"
LOCATE 27, 3
PRINT "Press SPACEBAR to change arc type, hold UP/DOWN arrow keys to change ellipse"
LOCATE 29, 26
PRINT "Press ESC key to exit program";
IF _KEYHIT = 32 THEN ' did user press space bar?
Sign% = -Sign% ' yes, change the sign
END IF
IF _KEYDOWN(18432) THEN ' is user holding UP arrow?
Aspect! = Aspect! + .005 ' yes, increment aspect ratio
IF Aspect! > 3 THEN ' is aspect ratio greater than 3?
Aspect! = 3 ' yes, limit its value to 3
END IF
END IF
IF _KEYDOWN(20480) THEN ' is user holding DOWN arrow?
Aspect! = Aspect! - .005 ' yes, decrement aspect ratio
IF Aspect! < .25 THEN ' is aspect ratio less than .25?
Aspect! = .25 ' yes, limit its value to .25
END IF
END IF
_LIMIT 60 ' limit loop to 60 FPS
_DISPLAY ' update screen with changes
LOOP UNTIL _KEYDOWN(27) ' leave loop when ESC key pressed
SYSTEM ' return to Windows
Figure 6: Radian demonstration
Radians fall in the range of 0 to 6.2831852 or 2 times Pi. Supplying the CIRCLE statement with positive radian values produces an arc from start_radian! to stop_radian!. Supplying negative radian values to CIRCLE produces the same arc but this time with lines drawn from the radian endpoints back to the center of the arc. This creates a pie looking structure as seen in Figure 6 above. Arcs are always drawn in a counter-clockwise direction. If you were to designate Pi as the start radian value and half Pi as the end radian value the end result would not be an arc drawn between those two points in the upper left quadrant. The arc would include the entire circle except for this quadrant because of the arc being drawn counter-clockwise.
This Wikipedia article on radians does a very good job of explaining the relationship between a circle's radius and circumference.
The example program also illustrates how you can affect the CIRCLE statement's aspect ratio. The default aspect ratio for the CIRCLE statement is 1 resulting in a perfectly round circle. Smaller aspect values squash the ellipse while larger values make the ellipse taller. If you simply want to change the aspect ratio of a circle without having to give radians you can simply skip over them like so:
CIRCLE (319, 239), 100, _RGB32(255, 255, 0), , , 2 ' create a tall yellow ellipse
By supplying no values for the start and end radian the CIRCLE statement uses the default values that create a closed arc, also known as a circle.
Here is a program that shows the power of the CIRCLE statement and how creative programming can use statements in ways that seem to defy their purpose. This example uses the CIRCLE statement to draw straight lines creating the hands of a clock and the tick marks going around the clock face.
( This code can be found at .\tutorial\Lesson5\CircleClock.bas )
'-------------------------------------------------------
'Description: Draws a functioning clock using circles
'Author : Terry Ritchie
'Date : 11/12/13
'Updated : 04/09/20
'Version : 2.1
'Rights : Open source, free to modify and distribute
'Terms : Original author's name must remain intact
'-------------------------------------------------------
'--------------------------------
'- Variable Declaration Section -
'--------------------------------
CONST GRAY = _RGB32(127, 127, 127) ' define colors
CONST DARKGRAY = _RGB32(63, 63, 63)
CONST LIGHTGRAY = _RGB32(191, 191, 191)
CONST BLACK = _RGB32(0, 0, 0)
CONST WHITE = _RGB32(255, 255, 255)
CONST RED = _RGB32(127, 0, 0)
CONST YELLOW = _RGB32(255, 255, 0)
DIM Clock!(60) ' 60 radian points around circle
DIM Tick% ' counter to keep track of tick marks
DIM Tc~& ' tick mark color
DIM Radian! ' FOR...NEXT radian counter
DIM h% ' value of hour extracted from TIME$
DIM m% ' value of minute extracted from TIME$
DIM s% ' value of second extracted from TIME$
DIM Tm$ ' current TIME$ value
'----------------------------
'- Main Program Begins Here -
'----------------------------
SCREEN _NEWIMAGE(640, 480, 32) ' initiate graphics screen
Tick% = 15 ' first position is 15 seconds
FOR Radian! = 6.2831852 TO 0 STEP -.10471975 ' clockwise from 2*Pi steps of -60ths
Clock!(Tick%) = Radian! ' save circle radian seconds position
Tick% = Tick% + 1 ' move to next seconds position
IF Tick% = 60 THEN Tick% = 0 ' reset to 0 seconds position if needed
NEXT Radian!
DO ' begin main loop
CLS ' clear the screen
_LIMIT 5 ' 5 updates per second
CIRCLE (319, 239), 120, GRAY ' draw outer circle of clock
PAINT (319, 239), DARKGRAY, GRAY ' paint circle dark gray
CIRCLE (319, 239), 110, BLACK ' draw inner circle of clock
PAINT (319, 239), WHITE, BLACK ' paint face bright white
FOR Tick% = 0 TO 59 ' cycle through radian seconds positions
IF Tick% MOD 5 = 0 THEN Tc~& = BLACK ELSE Tc~& = LIGHTGRAY ' 5 second ticks are black in color
CIRCLE (319, 239), 109, Tc~&, -Clock!(Tick%), Clock!(Tick%) ' draw radian line from center of circle
NEXT Tick%
CIRCLE (319, 239), 102, DARKGRAY ' draw circle to cut off radian lines
PAINT (319, 239), WHITE, DARKGRAY ' paint face again to remove radian lines
CIRCLE (319, 239), 102, WHITE ' fill in cut off circle
CIRCLE (319, 239), 4, BLACK ' draw small black circle in center
PAINT (319, 239), 0, BLACK ' paint small black circle
Tm$ = TIME$ ' get current time from TIME$
s% = VAL(RIGHT$(Tm$, 2)) ' get numeric value of seconds
m% = VAL(MID$(Tm$, 4, 2)) ' get numeric value of minutes
h% = VAL(LEFT$(Tm$, 2)) ' get numeric value of hours
IF h% >= 12 THEN h% = h% - 12 ' convert from military time
COLOR BLACK, WHITE ' black text on bright white background
LOCATE 19, 37 ' position cursor on screen
PRINT RIGHT$("0" + LTRIM$(STR$(h%)), 2) + RIGHT$(Tm$, 6) ' print current time in face of clock
COLOR GRAY, BLACK ' white text on black background
Tick% = (h% * 5) + (m% \ 12) ' calculate which hour hand radian to use
CIRCLE (319, 239), 80, BLACK, -Clock(Tick%), Clock(Tick%) ' display hour hand
h% = h% + 6 ' move to opposite hour on clock face
IF h% >= 12 THEN h% = h% - 12 ' adjust hour if necessary
Tick% = (h% * 5) + (m% \ 12) ' calculate which hour hand radian to use
CIRCLE (319, 239), 15, BLACK, -Clock(Tick%), Clock(Tick%) ' display small opposite tail of hour hand
CIRCLE (319, 239), 95, BLACK, -Clock(m%), Clock(m%) ' display minute hand
m% = m% + 30 ' move to opposite minute on clock face
IF m% > 59 THEN m% = m% - 60 ' adjust minute if necessary
CIRCLE (319, 239), 15, BLACK, -Clock(m%), Clock(m%) ' display small opposite tail of min hand
CIRCLE (319, 239), 2, RED ' draw small red circle in center
PAINT (319, 239), RED, RED ' paint small red circle
CIRCLE (319, 239), 100, RED, -Clock(s%), Clock(s%) ' draw second hand
s% = s% + 30 ' move to opposite second on clock face
IF s% > 59 THEN s% = s% - 60 ' adjust second if necessary
CIRCLE (319, 239), 25, RED, -Clock(s%), Clock(s%) ' display small opposite tail of sec hand
CIRCLE (319, 239), 1, YELLOW ' draw small yellow circle in center
PSET (319, 239), YELLOW ' fill in small yellow circle
_DISPLAY ' update screen with loop's changes
LOOP UNTIL INKEY$ <> "" ' end program when key pressed
SYSTEM ' return to Windows
Figure 7: A clock made with CIRCLEs
The PAINT Statement
While the LINE statement offers a way to create a filled box the CIRCLE statement does not offer a method of creating a filled circle. There also needs to be a mechanism for painting irregular shaped objects created with multiple LINE statements. This is where the PAINT statement comes in. Type in the following program to see the PAINT statement work its magic.
( This code can be found at .\tutorial\Lesson5\Paint.bas )
CONST RED = _RGB32(127, 0, 0) ' define colors
CONST BRIGHTRED = _RGB32(255, 0, 0)
CONST GREEN = _RGB32(0, 127, 0)
CONST BRIGHTGREEN = _RGB32(0, 255, 0)
SCREEN _NEWIMAGE(640, 480, 32) ' initiate a graphics screen
CLS ' clear the screen
LINE (50, 50)-(300, 300), BRIGHTRED, BF ' draw a bright red filled box
LINE (50, 50)-(300, 300), RED, B ' draw a darker red border around box
CIRCLE (319, 239), 100, GREEN ' draw a green circle
PAINT (319, 239), BRIGHTGREEN, GREEN ' paint bright green until darker green is encountered
Figure 8: A PAINTing
The PAINT statement is used to fill in an area with a color until another color is encountered. In the example program this line:
PAINT (319, 239), BRIGHTGREEN, GREEN ' paint bright green until darker green is encountered
is instructing the computer to fill in a color at coordinate (319, 239) (the center of the circle) with the color bright green, _RGB32(0, 255, 0), until the color green, _RGB32(0, 127, 0) is encountered. The green color of the circle creates a border that the PAINT statement stays within. Since the portion of the red box inside the circle is not green the PAINT statement will paint right over top of it.
Because of the way the PAINT statement operates it's important to remember the order in which objects are drawn. Let's take the previous example and simply swap the LINE and CIRCLE statements so the circle is drawn first to see what happens.
( This code can be found at .\tutorial\Lesson5\Paint2.bas )
CONST RED = _RGB32(127, 0, 0) ' define colors
CONST BRIGHTRED = _RGB32(255, 0, 0)
CONST GREEN = _RGB32(0, 127, 0)
CONST BRIGHTGREEN = _RGB32(0, 255, 0)
SCREEN _NEWIMAGE(640, 480, 32) ' initiate a graphics screen
CLS ' clear the screen
CIRCLE (319, 239), 100, GREEN ' draw a green circle
LINE (50, 50)-(300, 300), BRIGHTRED, BF ' draw a bright red filled box