Getting Started
Introduction
Sample Programs
QBasic IDEs
History
Advice
Tools
Mini-Tutorial
Tutorial
Code Snippets

Resources
Web Sites
More Tutorials
Vendors
Books
Magazines
NewsLetters
NewsGroups
Forums
User Groups
Talk Shows
Blogs

GBIC >> QBasic >> Tutorial >> Gaming (Sprites)

QBasic Information Center Tutorials - Gaming (Sprites)
These tutorials were written to help you get a quick, but thorough, understanding of QBasic - the scope of the language as well as it's specific capabilities.

Gaming
When it comes to the number of games written in a language, QBasic takes a back seat to no one. As one of the earliest (and free) languages, QBasic was and is used by programmers the world over to create games.

Whether checkers, chess, asteroids, or pacman, the common thread between all games is the use of on-screen, animated graphics to keep the game exciting for the user. More often than not, game graphics involve objects moving around the screen on top of an animated background. A first person shooter game is a great example of an object (the shooter) moving around on a background (the battlefield) which changes as the shooter seeks out targets.

In this tutorial we'll cover these objects, which are called sprites. We'll also cover handling background images. Animation of both will be explained.

Here are the graphics commands which apply to controlling sprites, creating background images and creating smooth animation.

    • Sprite Creation
     data, read, restore 
    • Sprite Control
     get, put 

This page does not cover the use of QBasic graphical shapes - circle, line, draw, paint. Their use was covered in a previous page and are not directly applicable to sprites and game animation except as they are used to create backgrounds on which sprites are displayed, or even parts of the sprites themselves. Also, the cls, pset, point, and color statements are used in this discussion but were covered in earlier tutorial topics and are not covered in detail here.

Sprites
Games often have small images which move about the screen, such as cartoon characters, space ships, jewels, or other small but recognizable objects. These simple objects are called sprites. They are usually no more than 20-30 pixels on a side and are very colorful.

Gaming animation is all about creating, placing, and moving sprites under program or user control. Complex games have not only multiple sprites on-screen at a time, but also have complex and changing background images.

Simply put, a sprite is a rectangular group of colored pixels. Fortunately QBasic has several features which make the task of controlling pixel colors easier - although not as simple as more modern, advanced languages.

The creation, saving and loading of sprites within QBasic is discussed below. However there are also several available sprite editors, similar to paint programs, which can save the images they create in a format that QBasic can load quickly. These can save you a lot of time and effort in creating sprites, but also provide speed up your QBasic programs by providing sprite files which are quickly and easily loaded. Here are links to free sprite editors.

The sections below describe how to load and display the files provided by sprite editors.

Before covering sprites in more detail, let's look at creating the backgrounds on which sprites will be placed. Then we'll look at how to add the sprites on top of the backgrounds.

Backgrounds - Random Points/Shapes
The simplest of backgrounds is made by randomly coloring each pixel on the screen, as shown in the following code.

    For x = 0 to 319: For y = 0 to 199
        pset(x,y), rnd*256                ' replace 256 with max colors in the screen mode
    Next y : Next x

It's equally easy to create a background of randomly placed lines, boxes, or circles. The following code shows how to fill the screen with randomly placed lines which also have random colors and line lengths.

    randomize   12345   ' fixed seed gets same result each time 
    for i = 1 TO 200    ' use larger number for denser background
        line (rnd * 640, rnd * 480)-(rnd * 640, rnd * 480), rnd * 16   
        'random placement, color, and length
    next i

Background - Displaying Windows Bitmaps
Randomly placed points and lines aren't very exciting. Placing a Windows bitmap (JPG, GIF, BMP) on the screen would be more visually desirable but QBasic does not provide a built-in function to display bitmaps on the screen.

Fortunately, there are several techniques available for loading bitmaps onto a QBasic screen.

  • Decoding
    With an understanding of the file structure of bitmaps, QBasic code can (and has) been written to read the files byte by byte and to transfer pixel information to the screen. Here's an example of loading a GIF file onto a QBasic screen, done by reading/decoding the file content and individually setting pixels to the appropriate colors. Header, palette, and color information all have to be dealt with in this approach.

        ... in work
        

    Because of QBasic's color and resolution limitations, the result is almost always a reduction in the quality of the image. Today's 1280+ resolution and 1M+ color images simply don't display well on the limited 256-color or 320x200 resolution screen modes supported by QBasic.

  • Tiling
    Strictly speaking this approach doesn't use a single background image. Instead, a small sprite-like image is used repeatedly (like a tile on a floor or wall) to cover the entire surface of the screen. Any small image will work as a tile, but the highest quality tiled backgrounds consist of tiles which end on a screen boundaries and which connect to one another seamlessly (invisible edges). Here's a very simple example of using tiling to completely fill a screen. The key point of this example is that a sprite can be stored in an array and quickly placed on the screen with a PUT command.

             SCREEN 13 : CLS
             'define the sprite
             DATA 0,0,1,0,0
             DATA 0,0,1,0,0
             DATA 1,1,1,1,1
             DATA 0,0,1,0,0
             DATA 0,0,1,0,0
             'put sprite information onscreen for capture
             For y = 1 to 5 : For x = 1 to 5 
                 READ icolor% : PSET(x,y), icolor%
             Next x : Next y
             'capture the sprite
             Dim sprite%(25)
             GET (1,1)-(5,5), sprite%   'tile now captured in sprite% array
             'cover the screen with the tile image
             For x = 0 to 319 Step 5 : For y = 0 to 199 step 5
                 PUT(x,y),sprite%
             Next y : Next x
        

  • BLOAD an Existing Image
    Once a sprite is captured in an array, as in the tiling example above or in any program where an image is saved in an array using GET, the entire array can be saved to a file using the QBasic function BSAVE. The resulting file can be read quickly and easily by any QBasic program - avoiding repeating the lines of code and the time required to create the image array.

    This technique applies to saving an entire screen background as well to saving a simple sprite/tile image.

    Here's the code to write the array. It uses the QBasic BSAVE statement, as well as the varptr and varseg statements, which tell QBasic where the array can be found in memory.

             DEF SEG=varseg(sprite%(0))
             BSAVE "sprite.bsv", varptr(sprite%(0)), 25
        

    To read the saved file in another QBasic program, use the BLOAD as shown in the following code. The DEF SEG/varseg statement tells QBasic the segment in which to load the array data. The varptr function is used in the BLOAD to indicate the offset within the memory segment where the array is located.

             Dim sprite%()
             DEF SEG=varseg(sprite%(0))
             BLOAD "sprite.bsv", varptr(sprite%(0))
             For x = 0 to 319 Step 5 : For y = 0 to 199 step 5
                 PUT(x,y),sprite%
             Next y : Next x
        

Animated Background
Even a simple game can be improved by providing a background which changes with time, with movement of sprites in the game or simply at a user's request. There are three basic techniques available for creating animated backgrounds.

  • Bitmap scrolling
    loading a bitmap as discussed in the prior section but moving its position on screen. the bitmap is treated much like a tile, where it is used to fill exposed areas of the screen as the current background image is scrolled.

  • Tiling
    similar to scrolling of a bitmap, tiling scrolls the tiled images and rewrite tiles to the newly exposed positions on the screen. generally, larger tiles are used in this approach (such as a tile that covers top-to-bottom of the screen) to speed up refreshing the screen.

  • Real-Time Texture
    creating background imagery by code rather than using pre-defined images. like the other methods, this may involve scrolling the background but using code to write texture graphics to the exposed regions of the screen.

All three of these animated background techniques involve scrolling of the onscreen image.

These techniques are discussed in a separate section of the tutorial. For the remainder of the discussion on sprites, a static background image will be assumed.

Defining a Sprite
The only information needed to define a sprite, or any on-screen image, is simply the color of each pixel on the screen. For mode 7 resolution of 320x200, that's 64,000 values. For a mode 13 resolution of 640x480, that's 307,200 values. For a sprite that's 10x10 pixels, that's 100 values.

So, how to get these values into a QBasic program? There are several methods used by QBasic programmers. The DATA statement is used to store values inside the source code. This is typically used to store sprites or other small images used in tiling. For larger images, with thousands of pixels, files are typically used with values stored as text or binary data. Each storage provides advantages/disadvantages which will be discussed later.

In addition to fixed storage of image data, QBasic programmers have also learned to generate background image on a real-time basis. This approach is typically used to creating changing backgrounds in games. More on this later.

Storing Values in DATA Statement
QBasic provides the DATA statement, which allows values to be stored as part of the program source code. When the program is executed the values can be read into variables with the READ statement. Below are two examples of placing values in DATA statements. In both examples, the color values of a 5x5 image (a plus sign +) are stored. The left set of DATA statements is a blue plus sign on a black background. The right set of DATA statements is a red plus sign on a blue background.

    DATA 0.0.1,0,0              DATA 1.1.4,1,1
    DATA 0.0.1,0,0              DATA 1.1.4,1,1
    DATA 1,1,1,1,1              DATA 4.4.4,4,4
    DATA 0.0.1,0,0              DATA 1.1.4,1,1
    DATA 0.0.1,0,0              DATA 1.1.4,1,1

In these two examples, five color values are placed in each of five DATA statement. All 25 values could be stored in a single DATA statement, but since the resulting sprite is to be 5x5, the five DATA statements actually provide a visual preview of the sprite shape.

READing DATA - Setting One Pixel At A Time
The READ function gets a value from a DATA statement and places the value into a variable, starting with the first DATA statement in the program. Each successive READ gets the next DATA value, skipping to the next DATA statement if once all values in the current DATA statement are read.

The simplest way to get DATA values onto the screen is shown in the following example, which uses READ to get values from DATA statements. PSET uses the values to set the screen color for each pixel.

      For y = 1 to 5
          For x = 1 to 5
              READ pixelcolor        ' read the first/next sequential value from DATA statements
              pset(x,y),pixelcolor   ' set the color of each pixel on the screen
          Next x
      Next y

READing DATA - Storing the Values
Sometimes it is useful to save the values into variables (usually an array) so the values can be used over and over again. While the DATA statements can be read over and over again, values in variables can be read more quickly and are more easily handled in code. The following example show how to place DATA values into an array.

      DIM color(5,5)
      For y = 1 to 5
          For x = 1 to 5
              READ pixelcolor(x,y)    ' read the next sequential value directly into the color() array
          Next x
      Next y

After executing this code, the array color() contains all of the values found in the DATA statements, ready for immediate access within the QBasic program.

PUTing Array Values on the Screen
Once pixel color values are stored in an array, the following simple code will create a sprite on the screen.

      For y = 1 to 5
          For x = 1 to 5
              PSET(x,y),pixelcolor   ' set the color of each pixel on the screen using PSET
          Next x
      Next y

However, while PSET will do the job, QBasic provides a much faster function to give the same results - the PUT statement. In general, the PUT statement takes color information from an array and places the information onto the screen. Here's an example how the PUT statement would be used to color the pixels on the screen.

      PUT(1,1), pixelcolor   ' pixel colors on screen set from values in array pixelcolor()

Yes - that's it! A single QBasic function will take the array content and place it on the screen.

Unfortunately, to use an array in a PUT statement requires special bit-level formatting of the array data. It can be done manually but is somewhat complicated. Most programmers use the following approach supported by QBasic.

Creating a PUT-Compatible Array
QBasic provides a GET statement which can capture a rectangular area of the screen and store the information in an array. The array can then be used with PUT to place the rectangular area anywhere on the screen.

To use GET, the sprite must first be placed on the screen, which can be done using the DATA/READ/PSET technique described above. Once the sprite is on the screen, the following code can be used to capture the sprite information into an array, ready for use in a PUT statement.

    GET (x1,y1)-(x2,y2),spritearray%   'upper-left, lower-right, arrayname

All the command does is give the upper-left and lower-right coordinates of the sprite, along with the name of the array in which QBasic will put the screen information.

With the sprite information loaded into the array, the PUT statement can be use to place the array anywhere on the screen, as with the following statements.

    PUT (1,1), spritearray%      'upper-left of sprite at 1,1
    PUT (120,50), spritearray%   'upper-left of sprite at 120,50
    PUT (72,230), spritearray%   'upper-left of sprite at 72,230

Now, here a complete example of using DATA to define a sprite, READ/PSET to put the sprite onscreen, GET to copy the sprite information into a PUT-compatible array, and PUT to place the sprite on the screen instantly into hundreds of locations.



Determining PUT-Compatible Array Size
The size of the array needed to contain information from a PUT command is somewhat complicated.

If you are not limited by memory, then simply size the array as width*height. This will create an array guaranteed to provide more than enough spaced to hold the information. Fortunately, QBasic does not require the array to be sized exactly. It will ignore any array positions which are not filled by the PUT statement.

To get an exact array size requires the use of the following equation:

    size% = 4 + INT(((PMAP (x2!, 0) - PMAP (x1!, 0) + 1) *
            (bits-per-pixel-per-plane%) + 7) / 8) * planes% *
            (PMAP (y2!, 1) - PMAP (y1!, 1) + 1)

    and for 3 commonly used screen modes:

    Screen mode    Bits/pixel/plane   Planes    Resolution
    ------------   ---------------    ------    -----------
        7                 1              4         320x200
        12                1              4         640x480
        13                8              1         320x200

See QBasic Help for more information.

Advanced PUTing Array Values on the Screen
A rectangular sprite almost always consists of unused pixels. For example, a diamond sprite would be typically be coded as a diamond on a black rectangular background. Using PUT to place the sprite on the screen results in the screen background being overwritten by the black pixels from the sprite - ugly!

What is normally desired is to have the sprite's black background pixels be "transparent" - to let the screen beneath time show through. This would give the illusion of an irregularly shaped sprite moving across the screen.

The good news is that QBasic supports exactly that kind of operation, using the PUT command with a special setting, as in the following example.

    'small sprite, a + sign
    DATA 0,1,0
    DATA 1,1,1
    DATA 0,1,0
    'put the sprite onscreen
    For y = 1 to 3 : For x = 1 to 3
        READ pixelcolor         ' read from DATA statements
        PSET (x,y),pixelcolor   ' set pixel colors on the screen
    Next x: Next y
    'capture sprite from screen
    Dim pixelarray%(100)
    GET (1,1)-(3,3), pixelarray%
    ' put sprite in several locations
    PUT (10,10), pixelarray%, XOR
    PUT (20,50), pixelarray%, XOR
    PUT (40,90), pixelarray%, XOR

Gaming (Sprite) Function Reference
The following functions are available to display sprite graphics.

  • cls - clears the screen of all graphics and text
       
        cls           # clear entire screen
        cls 0         # clear entire screen
        cls 1         # clears viewport (screen if no viewport set)
        cls 2         # clears text viewport
        

  • color - sets the screen display colors (foreground, background, border)
       
        color fg%, bg%, border%        # syntax
    
        Color values:
        0   Black       8   Gray
        1   Blue        9   Light Blue
        2   Green       10  Light Green
        3   Cyan        11  Light Cyan
        4   Red         12  Light Red
        5   Magenta     13  Light Magenta
        6   Brown       14  Yellow
        7   White       15  High-intensity white
        

  • data - stores values within the source code. the values can be read and re-read during program execution.
     
             data 1,2,3,4,5
             data 5,6,7,8,9
             data 0,0,0,0,0  
        

  • get - stores a rectangular portion of the screen in an array
     
            GET [STEP](x1!,y1!)-[STEP](x2!,y2!), arrayname[(index%)]   'syntax
                - STEP          Specifies that coordinates are relative to the current
                                graphics cursor position.
                - (x1!,y1!)     The upper-left coordinates of the image captured by GET
                                or of the screen location where PUT displays the image.
                - arrayname     The name of the array where the image is stored.
                                See ?Screen Image Arrays and Compatibility? to determine
                                the required size of the array.
                - index%        The array index at which storage of the image begins.
    
            Dim sprite%(100)             ' create receiving array
            GET (1,1)-(10,10), sprite%   ' grab screen data, put into array
        

  • preset - set the color of a pixel
       
        preset(5,20),15     # reverse fg/bg
        

  • pset - set the color of a pixel
       
        pset (5,20),15      # uses current fg/bg
        

  • put - displays an image that has been stored in an array by get
       
            PUT [STEP] (x1!,y1!), arrayname[(index%)] [,actionverb]  'syntax
                 - STEP          Specifies that coordinates are relative to the current
                                 graphics cursor position.
                 - (x1!,y1!)     The upper-left coordinates of the image captured by GET
                                 or of the screen location where PUT displays the image.
                 - (x2!,y2!)     The lower-right coordinates of the captured image.
                 - arrayname     The name of the array where the image is stored.
                                 See ?Screen Image Arrays and Compatibility? to determine
                                 the required size of the array.
                 - index%        The array index at which storage of the image begins.
                 - actionverb    A keyword indicating how the image is displayed:
                                 Keyword    Action
                                 -------    ---------------------------------------------
                                 AND        Merges stored image with an existing image.
                                 OR         Superimposes stored image on existing image.
                                 PSET       Draws stored image, erasing existing image.
                                 PRESET     Draws stored image in reverse colors, erasing
                                            existing image.
                                 XOR        Draws a stored image or erases a previously
                                            drawn image while preserving the background,
                                            producing animation effects.
        

    A PUT statement contains image size and screen color depth information, so a PUT statement should always be executed in the same screen mode as the GET statement used to capture the image, or a compatible mode.

  • read - reads values from data statements and assigns them to variables
       
            data "one", "two", "three"
            read a$, b$, c$        ' read one or more values at a time
        

    Values are read starting with the first data statement and continuing on through all existing data statements. once the end of values in data statements is reached, an additional read will result in an error. at any time, the restore statement can be used to start reading from the first data statement.

  • restore - sets reading to begin from the first data statement
            data "one", "two"
            read a$             ' a$ = "one"
            restore
            read b$             ' b$ = "one"
        

If you have any suggestions or correction, please let me know.