So I have been using this character generator for quite some time now and I personally think its the best FREE generator out there. I just happen to stumble across it one day and Ive been wanting to share but could never find the site from which I found it, that is, until recently. A new version of Quick Sprites has just been released on the Mac App Store. Version 1.10 extends its Retina support with an option to automatically scale down a sprite to obtain a normal resolution one from a 2x image.
- Quick Scripts Okc
- Quick Man Sprites
- Quick Scripts
- Quick Basic Sprites
- Quick Sprint Workout
- Quick Spritz Cookie Recipe
- Quick Spritz Cookies
Home > Articles > Web Design & Development > Usability
Like this article? We recommend
Like this article? We recommend
The idea (and the last part of the name) for CSS sprites originates in the early days of video games, when memory and speed were at a premium. To overcome system limitations, video game producers would lay out the thousands of small graphics used to create a game into a grid and then display each 'sprite' as needed, masking out all but the needed part of the larger image.
Using CSS sprites takes a little lateral thinking. Generally, we think about building a Web page similar to building with Legos: one block at a time. Sure, you can reuse the same image 'block' several times throughout the same page (relying on that nifty caching feature to speed render time), but at the end of the load, each image is an island unto itself. There are several drawbacks, not the least of which is that every little image used in your interface, no matter how small, represents a server call that can slow down your Web page’s overall rendering.
CSS sprites allow you to create a single file that contains all the images laid out in a grid, meaning only a single image and only a single server call, with roughly the same file size because the empty space is compressed. In that file, you will place all individual 'sprites' that make up your interface separated by enough space that they don’t start running over each other. You’ll then set the background position (using negative values to move the background up) and include enough space around each sprite so that only it appears in the background of the element, effectively masking the rest of the sprite images (see Figure 1).
Using CSS sprites takes some care and planning. You have to keep track of the pixel position of each sprite, but that’s not much harder than keeping track of the paths for the individual image files this single file will replace.
A Quick Example: Button Rollovers
CSS buttons have become a common technique in most Web developers’ arsenal of tricks. Where once we used clunky JavaScript to change the appearance of a graphic when the user rolls over or clicks it, modern Web designers use the CSS :link, :visited, :hover, and :active pseudo-classes to swap out images in the links background without ever having to program a single line of scripting code. However, there is a drawback in the form of a slight (but annoying) delay as the image files swap out. If we bang all of those images into a single sprite file, that problem goes away.
Creating the Background Image
First, we need to start by creating our four different image states, separated by enough space so that even if the links wrap some, we will not see the next button state poking up, as shown in Figure 2.
Here, I made the buttons a pretty good size and separated them by 100 pixels from the top of one button state to the top of the next state. This is important to remember because that means each button starts 100 pixels after the next, with a 10-pixel margin at the top so that they are a little better centered with the text of the link they will be placed next to. If you know the maximum width of a column, you can run your sprite images horizontally instead of vertically. Of course, whichever way you orient your sprites (vertically or horizontally), the extra space will add slightly to the file size, but if you save it as a GIF or PNG, the impact will be almost negligible.
One image element type that cannot be included in the sprite file is the tiled background. Obviously, if you need the image to repeat, you can’t have just a single area of the sprite image repeat.
Creating the Styles
Now it’s time for the code. To begin, let’s set up the links using a list:
Nothing breathtakingly remarkable here. Without CSS, this list of links will look something like Figure 3.
Of course, we will be providing our own bullets, so we need to redefine the <li> tag to take out the default bullets:
Notice that I also set the height of list elements to 90 pixels (remember that there are 100 pixels between each button state) and hide any overflow. You will need to set the height to your specific needs, but this will ensure that the other button states stay masked when not needed. If you are designing with browsers other than Internet Explorer, you can set this as max-height, so that 90 pixels are not taking up the space unless needed.
Now it’s time for the links. We start with the generic link appearance when it is within a list tag:
We are loading the image we saved (I called mine buttons.png and saved it in an image folder), have set its position to the top-left corner (0px 0px) meaning that the default state (link) is showing, and no repeating. We also need to set enough padding on the left side so that the text part of the link doesn’t overlap the image bullet in the background. This is, of course a matter of design. I’m using a pretty boring looking light bulb bullet for this example, but you may want to do something more exciting where the background image and text overlap and interact.
Now we need to define the background position for each state:
For each state, we just move the background image up by 100 pixels. For each state, the background image slides up and down to show the relevant image for that state.
This rollover example is just a simple example of what you can do with CSS sprites. You can create much larger images used to provide not only interactive states but also with all the graphic chrome (except for backgrounds) contained within your site.
For more information on Web design, visit our Web Design Reference Guide or sign up for our Web Design Newsletter.
by Ted Felix
What are sprites?
Sprites are small pictures that can be moved around the screen and animated. By using sprites, you can make games that have a more realistic look. Sprites are two dimensional (flat) but if you are good at drawing, you can make them look like they have depth.
For example, Nintendo's Legend of Zelda is an entirely sprite-based game, and perhaps one of the best. Take a look:
Link and the Octorox are clearly flat two-dimensional sprites.
But, there's no reason to stick with a 2D look when you can fake a 3D look. Age of Empires is a good example of 3D-looking sprites.
(Age of Empires for more 3D-looking sprites)
QBASIC has two statements that support sprites: GET and PUT. We will be working with GET and PUT quite a bit in this section, but in the end we will have to find a new way to draw sprites. There are some pretty serious problems with GET and PUT as you will see.
If you need to brush up on your QBASIC skills, see my QBASIC Tutorial before you continue:
Making a Sprite
Since sprites are small pictures, we need a picture to make a sprite. There are many ways to do this. The simplest way is to first draw the picture on the screen using the graphics statements in QBASIC (PSET, LINE, CIRCLE, PAINT, etc..). Let's start with a simple filled in CIRCLE using CIRCLE and PAINT:
Once we have the picture on the screen, we can use GET to copy the picture from the screen into an array. Normally we think of arrays as being lists of variables. GET treats an array as if it is simply a chunk of memory where it can store a picture. We need to make sure the array is big enough to hold our picture. For this picture, we will need an array that can hold 37 integers (%):
How did I know it had to be 37? Well, there is a formula you can use to figure this out, but since this program is so small and there's only one sprite, I used trial-and-error. I kept picking numbers and trying to run the program until I found the right number. QBASIC will give you an error if the array is too small. See the QBASIC Help topic 'Screen Image Arrays and Compatibility' for the formula.
Now that we have our array, we can use GET to copy the picture from the screen into the array:
The coordinates (0, 0)-(8, 7) define a rectangle on the screen. This is done the same way you tell the LINE statement where you want to draw a box on the screen. GET will copy the picture from that rectangle into the Ball% array.
Once the sprite is stored in the Ball% array, we can draw it anywhere we want on the screen using PUT. To prove that all of this has worked, let's use PUT to copy the picture from the Ball% array back to the screen. How about near the center of the screen:
So, here's our first sprite example all together and with comments (comments are good):
Running this example, you will see a filled circle in the upper left corner of the screen. This is the first circle we drew to make the sprite. Then you will see another filled circle near the center of the screen. This is a copy of the sprite made by the PUT statement.
Making a Sprite Move
I have to admit that I am writing this chapter of the book first. So, I'm assuming that I've already discussed the basics of motion in an as yet unwritten chapter on real-time text games. I will fairly quickly go over motion here, but not discuss it in too much depth.
Making a sprite move is very similar to making an object move in any other kind of 2-D game. We keep track of the sprite's X and Y position, and change them as time goes by. Here's an example that adds motion to our sprite:
You might need to adjust the 30000 in the timing loop to get this to go at a reasonable speed on your computer.
The strangest thing about this example is how it uses PUT both to draw the sprite and to remove it. PUT can do this because it draws our sprite on the screen using something called an 'exclusive or' or 'XOR' for short. We will talk more about this later.
Making a More Interesting Sprite
Using CIRCLE and PAINT is fast, and handy, but let's face it. It's hard to draw anything really interesting this way. For that, we need what's called a bitmap. A bitmap is a picture stored in a way a computer can understand. Since computers are most fond of numbers, it's no surprise that a bitmap is made up of numbers.
Here's an example of a tiny bitmap stored in DATA statements:
It's like coloring by number. Where there is a 0, there will be a black dot (pixel) on the screen. Where there is a 15, there will be a white pixel on the screen. This particular bitmap is organized to be 3 pixels wide by 3 pixels high. It is very small as we will see when we draw it.
This reads the bitmap from the DATA statements and draws it on the screen using PSET. You will see a very tiny diamond in the upper left corner of the screen. That's our bitmap.
Quick Scripts Okc
Try changing the bitmap to this and see what it looks like.
And finally, let's try a more colorful bitmap.
Our bitmaps don't have to be tiny, we can make them as big as we want. Here's a bitmap that is 15 pixels wide by 15 pixels high. Hopefully you can cut and paste this one into QBASIC. Otherwise, it's a lot of typing. Can you guess what it is a picture of?
You can use graph paper and colored pencils to design your bitmaps, then copy them block by block into DATA statements. Making large bitmaps like this can take a lot of time. Fortunately, there are bitmap editors that can make the job much easier. Unfortunately, those bitmap editors can't write DATA statements for you. However, if you wanted to, you could write your own bitmap editor in QBASIC and have it write the DATA statements you need. Might be a good project.
See the links section at the end for some bitmap editors that might be of interest.
Making a Bitmap Fly
Combine the bitmap above with GET and PUT, and you've got yourself a new sprite.
Bitmaps and Files
Since DATA statements and files are pretty much the same thing, you could store your bitmaps in files, and read them in from those files.
The nice thing about DATA statements is that they are always right where you can find them. The bad thing is that DATA statements make your program bigger. I also think DATA statements look a bit messy.
The nice thing about files is that they do not make your program bigger (or messier). The bad thing about files is that you need to find them on the hard disk. Very large game programs usually store their sprites in files to reduce the size of the program.
PUT and Backgrounds
Let's go back to the bouncing ball program, and this time we'll add a little bit of a background. We are going to make half of the screen yellow before we start drawing the ball. We can do this by adding a LINE statement after we set up the screen size constants. Here's the part of the program we've modified:
Notice how as the ball enters the yellow area, it changes colors. Although this is an interesting effect, it might not be what we want. Many times we will want the background and foreground to stay the same.
The problem is XOR. As I mentioned before, we are using an exclusive OR, or 'XOR' for short. While XOR is really great for drawing and erasing using the same bitmap, XOR causes problems when it encounters a background other than black. We need something new.
One possible solution is to use a mask along with the bitmap. We PUT the mask with AND, then PUT the bitmap with OR. This works fine. However, there are so many problems with PUT that we are going to have to stop using it. Before we do, we will discuss why in the next section.
The Problem With PUT: Clipping and Flipping
The first problem with PUT is that it will not clip. If you use PUT too close to the edge of the screen, QBASIC will stop your program with an error. Try this:
SCREEN 13 goes all the way out to 320 on the X coordinate, yet QBASIC gives us an Illegal Function Call when we try to draw our sprite at X coordinate 312. Superal driver download for windows 10. Why? Because the sprite would go off the right edge of the screen. For our bouncing ball, this isn't a problem because we don't want it to go off the edge of the screen. In a more complex game, this could be a serious problem.
Consider a game where the screen scrolls (shifts) from left to right. It would be nice if the sides smoothly came in on the left, and went out on the right. But, if you can't draw your sprites at the edges of the screen, the left and right edge would suddenly disappear (or you'd get an illegal function call).
It would be better if PUT would 'clip' the sprite by cutting it off where the screen ends. Unfortunately, PUT cannot do this. This means we need to make our own PUT that can do clipping.
The second problem with PUT has to do with page-flipping which will be discussed later.
Why is PUT so bad? When PUT was originally created, computers were incredibly slow. No one ever imagined being able to write a serious game using BASIC. So, PUT wasn't taken too seriously. Now that computers are incredibly fast, we can write our own PUT, or use someone else's to solve the problems with QBASIC's PUT.
Making A New PUT
Now that I've convinced you that we need a new PUT, it's time to make one. Our first goal is to get something that acts like QBASIC's PUT. It has to be able to draw a bitmap on the screen. Although we could write a new GET, there really isn't a need. Most sprites will be bitmaps, so we can use DATA statements or files instead of GET to fill in our bitmap array.
Here's the first example of working with sprites and avoiding PUT. Instead of GET, we copy the bitmap from the DATA statements into an array. Then we use PSET to draw what's in the array to the screen.
Note that this is a two-dimensional array. Two-dimensional arrays are perfect for this kind of problem. After all, a bitmap is two-dimensional.
Before we go any further, we really need to break the drawing portion of this program off into a SUB. We will be needing it in many places when we start writing more complicated sprite code, so we don't want to copy that piece of code everywhere. Here it is as a SUB:
It is unfortunate that there is no way to break long lines in QBASIC. That first line is pretty long and ugly.
Now we can call the SUB like this:
The first argument 'Ball%()' is the array that holds our sprite. PUT2 will copy from that array to the screen. The next two arguments (9, 7) are the size of the sprite in pixels (9 across, 7 down). The last two arguments (0, 0) are the X and Y position where the sprite should be drawn.
Here is the complete program using the new SUB.
Not bad at all. It wouldn't be too big of a jump to add some motion to our ball, and see how well this works.
We'll eventually get to clipping, but first we will solve the infamous 'flicker' problem. This will have an effect on how we do clipping, so we'll get it out of the way first.
Page Flipping
In a simple ball-bouncing program, it is hard to see the need for page-flipping, but imagine a much larger game. One with hundreds of sprites on the screen at one time. Each time the sprites move, we need to erase them from the screen and redraw them. Not only that, when we erase them, we have to restore the background that was under them so they don't destroy it. What a mess. What a lot of work. This could take quite a bit of time. And all that time, the user will see what we are doing. The user will see us erasing and redrawing. It isn't pretty.
Page-flipping to the rescue! Page-flipping lets us hide what we are doing, and it will also let us simply start from scratch and redraw the entire screen instead of worrying about redrawing pieces of the background all the time. If we have hundreds of sprites, this will be a real time-saver.
Page-flipping (also called double-buffering) solves these problems by letting us draw to an invisible screen (a memory buffer, or double-buffer), then copy that invisible screen to the visible screen. This way our drawing process can't be seen.
Quick Man Sprites
QBASIC's PCOPY command can be used to do page-flipping. However, it only works in certain SCREEN modes. SCREENs 7 through 9 are the most notable since they provide color. Let's start with a simple example of something that could easily benefit from page-flipping. Try this program:
It goes so slow you can see it building up the picture each time before it clears the screen and starts to build up the next picture. Try making the '1000' larger to make the effect worse. This is pretty awful to watch. Now try the following changes. Note that only the SCREEN line has changed, and the PCOPY command has been added in this new version:
This is much better. We see the image each time, not the drawing of the image. This is what page-flipping is all about.
SCREEN 9 provides two pages for us to work with, page 0 and page 1. The SCREEN command lets us decide how we will work with those pages. In the previous program, the SCREEN line has mode '9' specified. The next '0' is ignored. After that is a '0' for the 'active page'. This is the page where the LINE command will draw. The next value, a '1' specifies that page 1 will be the 'visible page'. This is the page that is displayed on the screen. All together, this says that we will be drawing on mode 9's page 0 and looking at mode 9's page 1. This seems like we would never see anything at all. That would be true if it weren't for PCOPY. In fact, try commenting out PCOPY and see what happens. In a word, 'nothing.'
The PCOPY command copies everything from page 0 to page 1 almost instantly. So we can take our time drawing on page 0, then when we are done, use PCOPY to copy what we drew to page 1 so the user can see it.
As an aside, I cheated to make the program even slower. I didn't do a DEFINT A-Z. This means all the variables are treated as single-precision floating point. Try throwing in a DEFINT A-Z and you'll notice a slight performance boost.
Page Flipping Without PCOPY
Quick Scripts
Unfortunately, page-flipping with PCOPY is only available in a few SCREEN modes. It isn't available in the more colorful modes like SCREEN 13. If we want to do page-flipping in SCREEN 13, we'll have to write our own version of PCOPY.
Quick Basic Sprites
To make our own PCOPY, we'll need a way to draw to a memory buffer, then copy that memory buffer to the screen. This is called 'double-buffering'. The fastest way to copy something to the screen is to use QBASIC's PUT. It is written in Assembler, and is very fast. Its drawbacks when working with sprites (clipping, etc..) are not an issue when we are copying from a memory buffer to the entire screen. The only thing we need to figure out is how to draw into a memory buffer that PUT can understand.
Since we will use PUT to flip our memory buffer to the screen, we must understand the format of a memory buffer used by PUT. Then we can write some SUBs that allow us to draw in a PUT-compatible memory buffer. Our most important SUB will be our PUT2() that we wrote for drawing sprites.
(Sample using our own double-buffer and PUT)
Quick Sprint Workout
Transparency
Where there is a special value in the data, don't draw anything. Pick a number that isn't a valid color so that we still have access to all the colors.
(Sample doing transparency)
Clipping
Check the bounds. If out of bounds, don't draw. Update PUT2() to be bounds-aware.
(Sample with clipping)
Optimizations
The above examples have been designed primarily for education, not for speed. Many improvements can be made to increase performance. Here are some suggestions.
Profile. Be sure to do profiling to make sure your changes have actually improved things. (Give an example of profiling in QBASIC)
Assembler. Rewrite critical parts of the code in assembler. Our PUT2() routine is a good candidate.
Space. Make your sprites more space-efficient by storing them using bytes instead of 16-bit words.
???
Quick Spritz Cookie Recipe
Links
Pointless Products, Inc. has created a bitmap editor called QBPaint that exports QBASIC-ready bitmaps.
Quick Spritz Cookies
Outline
This is the outline I used to work on this section of the book. It is much more complicated than the book ended up. It discusses all the different directions you can go with sprites. In the end I decided to take the shortest possible path from PUT to page-flipping.