| |||||||||||||||||||||||||||||
|
| |||||||||||||||||||||||||||||
Print This Article REALbasic University: Column 020
REALbasic Animation: Sprites Part IVOur program is really coming along, but now we've got to write the most complicated part: the NextFrame event. This is the core of the program: all this code will be executed at 30 frames per second (if you set spriteSurface1's frameSpeed to 2). Before I give you the complex code, let me explain a bit about what we need to do in this routine. Our basic goals are the following:
Whew! That's a lot of stuff to do. You can see how this could get complicated in a more sophisticated game! It's not so bad if we take each step piece by piece, though. Here's the first part of the code: dim i, j, x, y as integer This first part is just a big loop through every duck. We check to see if the duck's visible (alive) or not, and if it is alive, we move it. We move it left or right according to the duck's direction property, and the amount we move it -- the number of pixels -- is the speed value. After we've moved the duck, we check to see if we moved it too far so it's off the screen. If so, we move it to the opposite end so it "wraps around" and starts over from the other side. If our duck is "dead" (hidden), we check to see if it is exploding. (If the exploding property is greater than one, we know it's exploding.) We increment the exploding property. If it's greater than 30, then enough time has passed for the explosion to be seen, so we terminate the sprite and set its exploding property to zero. Our final step is to draw the score after the explosion has gone away. The next bit of code directly follows the above: if j = 0 then // all ducks shot, game over First we see if the all the ducks have been shot. If so, we stop the execution of spriteSurface1 -- that's the me.close line-- there's no point in continuing if there are no ducks to shoot. Next, just like we move the ducks with each frame of animation, we now move the missile (if there is one). The ducks move only horizontally while the missile moves vertically, so for the ducks we added or deleted numbers to the x property. With the missile, we work with the vertical -- y -- property, and we only subtract since the missile only moves upward. Since we want the missile to move quickly, we move it 5 pixels at a time. (Feel free to change this and see how that changes the gameplay for you.) Finally, we check to see if the missile has gone off the top of the screen (obviously without hitting anything). If it has, we delete it and set missileLaunched to false. This routine is a long one, but breaking it into a few steps helps. This final part handles player interaction. The SpriteSurface object is a bit strange: since it's running in its own little world, you can't use a window's normal KeyPressed event to see if the user pressed a key. Instead you must ask SpriteSurface if the user has pressed a particular key. But even then SpriteSurface is strange: you can't just ask it if a particular ASCII character has been pressed -- you have to instead pass it a hexadecimal keycode. Keycodes are unique to keyboards: the French keyboard thus uses slightly different keycodes for certain letters than the English keyboard! Here's a chart showing the hexadecimal keycodes for the English keyboard: [Click for full view] Here's the code for the user interaction section:
Most of this code is very similar to what we've done before: we move a sprite by adding and subtracting to its x and y properties, and we wrap it around the screen if it goes too far. One new twist is the buildUp variable. Again, this is subtle detail that I wanted my game to implement. Basically, I wanted the movement of the player's ship (or gun) to accelerate: the longer the player holds down the arrow key, the faster the ship moves (up to a point). Now I could have just incremented the speed value, but since this happens every time nextFrame is called (at least 30 times per second), that would happen almost instantly (less than a second). So instead I use a second value, buildUp, and I increment that during each frame of animation. When buildUp is evenly divided by ten (every ten buildUps) I then up the speed one notch. Thus in one second (assuming 30 fps animation), the speed only goes up by 3. Since the maximum speed is 10, it takes 3.3 seconds to achieve maximum speed. The result is a gradual acceleration of the player's ship. If the player changes direction, speed and buildUp are set back to 1. The flaw (bug?) in this implementation is that the above code makes no provision for resetting the user's speed if the arrow key is released. The player can therefore go to maximum speed and as long as the player doesn't change direction, always move at maximum speed.
Our next bit of code check to see if the player pressed the space bar to launch a missile. This is only allowed if a missile isn't already in the air: a more complex game could allow the user to shoot multiple shots at once. Then you -- the programmer -- would have to keep track of multiple shots (probably with an array of missiles, similar to the current array of ducks). To launch the missle we simply create a new sprite and set its initial location. Once it's created, our previous code will automatically move it starting with the next frame of animation. Finally, we finish by checking to see if the user terminated the game by pressing either the Escape or Return keys. Neither key quits the program -- they just halt spriteSurface1 and abort the game in progress. (Obviously, in a real game, you'd want to confirm this with the user before stopping.) Well, guess what? We're finished! That's it for Shooting Gallery. I hope you enjoyed it and learned a little about sprites. Like I promised, we'll do something more elaborate in the distance future, but this should get you started and help motivate you into writing your own animation-based game. The complete, finished project file with all the required pictures and sounds is available for download. Experiment with it: change some of the variables and try different things. Enhance it, make it play better. For instance, how about letting the player's "ship" (gun) move vertically as well as side to side? What about making the ducks drop bombs and/or dive downward? How about different kinds of ducks? Instead of the game ending when all the ducks are shot, why not advance the player to a tougher level? There's plenty more to do: I've just given you a step in the right direction! The Week After Next Week: Next week I'm off on vacation, so have a great Fourth of July holiday. When I come back, we'll have a look at REALbasic Basics. Some of our tutorials have been a bit advanced, so we'll have something special for the newbies in the audience.
LettersRemember how last week I mentioned there was an alternate method of animating a spriteSurface -- calling its update method repeated via a timer control? Well Sarah tried that and wrote:
Interesting info, Sarah. Thanks for the update. I can only surmise that sprites are problematic under Mac OS X for now, though I'm sure REAL Software will eventually fix the problems. I'll have to play with the update method myself at some point: I like the idea of the better control, but Timers are notoriously unreliable for precision timing. (To see what I mean, start a Timer doing something every second, like moving a staticText. Then pull down the Edit menu of your running program. The Timer doesn't fire. See? Timers don't get called when the Mac is too busy with other stuff. Of course that may only apply under Mac OS -- OS X could be different.) As to the second part of your question, you got me! One certainly shouldn't know which of the two sprites is which: theoretically either could be the missile. But in practice, it's always s1. Why? I don't know. But when you switch the two around and check for s2 being the missile, it never is. It could be something along the lines that the missile sprite was created more recently and therefore it is the one that's listed first. I did notice that bug and thought about changing it, but the deadline was looming and I half wondered if anyone would notice: I ought to give your sharp eyes a prize! While the way I did it works, it's not a good programming technique. Never depend on a fluke of your programming environment, even if it consistently works. The bug or oddity might be fixed in a new release of REALbasic and then your program will suddenly start crashing and you'll be driven nuts trying to figure out why. Christoph Scherer also wrote in with a sprite issue:
I wish I could be of more help, Christoph. I downloaded your prototype game -- the graphics are very impressive -- but I couldn't really tell much from that. I'd probably need to poke around in your actual source code to see if I saw anything. I did notice that the game was extremely crash prone on my Mac OS 9 system. That could be because it's a Carbon version, which also could be part of the problem. Do you have the same problems in a straight PPC version? (It's been my experience so far that Carbon RB apps tend to be slower and buggier than PPC ones.) Your application wouldn't let me enlarge the memory partition, so I couldn't tell if the problem was low memory. It could also be that your game is just too complicated: how many sprites did you have going at once? Maybe if there are too many REALbasic doesn't handle the collision issue well. If anyone else has ideas for Christoph, let me know. It also might be fun to point out some great REALbasic animated games: if you have any you've written or know about, send me the URL and I'll post a link to it next column. About the Column REALbasic University is a weekly instructional column on programming with REALbasic and is brought to you by REALbasic Developer, the magazine for REALbasic programmers. Each week we answer select reader questions, and we're always open to ideas for future columns. Send your questions to . (Keep your questions simple and specific. General queries like "How do I write my own web browser?" will be neglected.) Your question won't be answered immediately, but will be answered in a future column. (If you don't want your correspondence published, just be sure to indicate that when you write. Otherwise it's fair game.) About the Author See the REALbasic University Archives
REALbasic University contents ©2001-2004 by Marc Zeedar and REALbasic Developer. All Rights Reserved.
| |||||||||||||||||||||||||||||