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.
|
| data, read, restore
|
|
| 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.
|