EffectsAn effect as I define it for my game is anything you can see happening on the screen. Fades, particle systems, displaying text, etc. all fall into this category. I store an effect in a structure that looks like this: typedef struct GAMEFX_type { DWORD dwID; // effect ID number DWORD dwFlags; // control flags int nCurrent; // for FOR loops int nTarget; // '' int nStep; // '' int nLinkedScript; // ID of script this is linked to (or -1 for none) int x, y; // a screen location void *buffer; // optional data table } GAMEFX, FAR* LPGAMEFX; Let me run through the parameters and explain what I'm using all this stuff for. DWORD dwID: This is the effect identifier. It takes a value from one of a number of FX_ constants I have defined, such as FX_FADE, FX_TEXTIN, or FX_PARTICLESYSTEM. DWORD dwFlags: This can be used to store miscellaneous flags for each effect, and so its use varies from one effect to another. int nCurrent, nTarget, nStep: These are used to define for-style loops, or incremental effects. For example, I have a script command called FadeDown. An example call would be: FadeDown 34, 11 This means, "Fade down to 34% by increments of 11%." The way the GAMEFX structure is set up for this, nCurrent is set to the current fade percentage, whatever that happens to be, nTarget is set to 34, and nStep is set to -11. In each iteration of the main loop, nCurrent is decreased by 11, and that percentage of fade is applied to the frame. Once nCurrent is less than or equal to nTarget, the effect terminates. int nLinkedScript: This is the important one. If this is -1, the effect executes side-by-side with any scripts that are running. But if this value is nonnegative, then the script with that index cannot continue execution until the effect finishes. This is important! Let's go back to our earlier example. DoSomeAnimation ShowText 12, 133 Suppose these commands were found in the script in slot #1. Then when the effect generated by DoSomeAnimation was set up, it would be linked to script #1, and thus the script would halt execution until the effect was done, at which point the text would be displayed, like we wanted! This adds one more step to our script handler: if the script is linked to any effects, don't execute it. Also note that any script command which will link the script to an effect must be terminal, so script execution stops right away. int x, y: Many effects, like displaying text, require a location on the screen, so I provide one here. If you need multiple locations for an effect, well, that's what the final parameter is for... void *buffer: Many effects can easily be specified by using only the above parameters. But what if you want to create a blizzard by sweeping 800 particles across the screen on increasing-amplitude sine waves? You don't want to be calculating that stuff as you go! The solution is to take this pointer, malloc() yourself some memory, and you've got a lookup table. My philosophy is to precalculate everything that can be precalculated. Just remember to free() the memory when the effect is done. So is this all starting to fall into place? With the above specifications laid out, you can easily make your effects such that they can be executed one frame at a time. The only question that remains is how to store the effects in memory. Well, effects aren't something I like to have a limit on, and since you're going to be accessing them in sequential order all the time, this is a great candidate for a linked list. When you want a new effect, just stick a structure on the end of the list. When an effect is finished, just cut the links. It's as simple as that! If you're not familiar with linked lists, find a tutorial, because I use them a lot. :) One other thing I'll do with effects is to give them an extra parameter in their corresponding script commands that flags whether or not they should link the script that calls them. For instance, if I wanted to fade the screen down, and only then display some text, I could put this in my script: FadeDown 50, 5, TRUE ShowText 4, 230 This says that the call to FadeDown should link the script, and thus the text should be shown only after the fade is complete. But change the TRUE to FALSE, and the text will show while the screen is fading. This is a very simple, yet very effective way to either run several effects one after the other or simultaneously, with very little effort on your part. ClosingAll right, that about wraps up this long-overdue article. We covered splitting a program into multiple components which can be developed independently of one another, and I think you've got all the basics now for designing a scheme that will let you run scripts and graphical effects that make Windows happy. In other words, they don't hog the processor for too long at once. You should be able to implement all these things and still maintain a good frame rate. One thing to notice from this article is just how much of the game can be controlled with scripts. I use them for everything -- initialization, image loads, map loads, NPC setup, battle effects, fading, enemy AI, menu setup, etc. And as we'll see in a future article, probably #9, this can all be achieved with a scripting language that is very straightforward and easy to implement, with no need for any black magic on the programmer's part. Now that I've gone over the basics of how you structure a Windows game program, the next article will finally be the long-awaited tile engine. I promise this time. :) I'm not sure how long it will be until I get around to it, but it's coming. In the meantime, if you've got any questions, go ahead and hit me with them: E-mail: ironblayde@aeon-software.com Happy coding, until we meet again... Copyright © 2000 by Joseph D. Farrell. All rights reserved. |