|
By James S.
Note: hexadecimal values are marked 0x.
Introduction
Playing computer games is one thing but to be able to make your own is something special. It's no easy task whatever platform you program for but on this page I will present to you my own ideas that I've used for my games. I prefer to use C++ so examples will be shown using that language but even if it's not up your street so-to-speak it may still open your eyes as to how a typical video game works. Let me remind you one important thing and that's even the so-called experts make mistakes, some of them major. For example, collision testing can be very difficult and certainly most games lack perfect collision testing, if that can ever be done. Even the most advanced games can be glitchy and be plagued with common video game flaws.
It can be frustrating at times trying to squash bugs but keep at it as it's well worth the effort. Often the most simple mistakes can cause serious errors; once I got over 100 errors (one of the errors was that there were over 100 errors!) because I had forgotten a single brace in my program.
One final note and that's although I've probably had the same ideas as many people and so they can't be copyrighted, I will only be mentioning highlights from my game engines as they are my own work. Also, I deal with concepts of both 2D and 3D games. Artificial Intelligence (AI)In a typical game a human will control the lead character and there will be a number of computer controlled baddies or even helpers. These Artificially Intelligent (AI) objects are also known as bots, CPU , drones and NPC (Non-Playable Character) and mean pretty much the same thing. AI has advanced considerably over the years to better match a human (and other creatures) but there have been some bad attempts which tends to result in AI that's almost impossible to defeat, which is just unfair. A computer program can respond much faster than a human (a CPU can process thousands of operations in just a second) and as if that wasn't bad enough, AI often cheats. For example, in Super Smash Bros. Melee for the Gamecube, one of the Pokemon can cause the 'lights' to go out to effect the screen is blanked, but the CPU carry on since they don't need to see the screen to fight (they go by collisions). A better example of AI can be found in Resident Evil Outbreak for the PS2, when playing offline you have 2 CPU characters helping you. Assuming that you pick characters that get on well together, while not perfect, the AI will do its best to assist you. From small things such as the AI character thanking you for protecting him or her from a zombie to the CPU character handing you useful items ("A herb-for me? Thanks!") Pac-Man is a good choice for trying out AI, in the game you have the hero chased by a number of ghosts that home in on Pac-Man. For the ghosts to home in on Pac-Man you simply have to determin whether Pac-Man is to the left or right of a ghost and also if he is above or below the ghost. So you need to look up both Pac-Man's and the ghosts' coordinates and compare if they are less than or greater than (if equal the ghost needn't move in that direction). So, you move the ghost either left or right, and up or down to get to Pac-Man, that is if there isn't part of the maze in the way. You then have a choice, the ghost could wait for Pac-Man to move if it has got stuck or the ghost could move in a different direction.
Animation2D animation
To create 2D animation it is a simple matter of cycling through a number of pre-rendered frames with the correct timing. You can keep track of the current animation frame using an integer variable which acts as an index into which frame to use.
You could have each animation frame as a separate bitmap or texture or you can use just one large image for each animation and select part of the picture or tetxure to use. That is, to divide it up into the smaller frames and although that would mean only needing to load one image for each animation you will need to make sure each frame is aligned and the same size.
3D animation
There are two main ways you can animate 3D objects and that is either to use pre-calculated values for the positions of each body part or to calculate the positions as the game is played. The latter, which is known as procedural animation is more difficult to program but it has certain advantages over more traditional 3D animation.
If done right, with procedural animation, you can have animations where the individual body parts react to others for more life-like animation. How procedural animation is handled is the tricky part so I will talk about my method to get those body parts moving.
As with other types of 3D animation the body will need to consist of the body parts arranged in a hierarchy so that, for example, when the torso moves so does the arms and head. A stack can be used to remember the current transformations as well as the previous transformations along with the use of start and end nodes.
A start node is when want the current transformations to be applied to the other body parts down the hierarchy and an end node is the end of the chain so-to-speak. So, the torso, upperarm and lowerarm would all be start nodes and the hand an end node. That way moving the torso also move the upperarm, lowerarm and hand just as moving the upperarm also moves the lowerarm and hand and so on except that when the hand moves nothing else does.
To keep things simpler, I define a number of conditions and what types of movement for each body part should occur during those conditions. Examples of conditions are if the object is jumping, falling, running, etc. If we list what type of rotation should be used for each body part for every conditions along with other values such as the rotation limit, speed and so on it's a lot easier to define how each body part should behave.
BehavioursEach object in the game will behave in a certain way and there are several ways to handle these behaviours. First let's start with probably the most simple way and that to use behaviour flags that are combined using one or more integer variables.
What you need to do is declare a number of constants and give them each a unique binary value so you might want to use hex values. You would end up with something like this as an example (C++):
const AUTO_MOVE_HORIZ=0x01; const AUTO_MOVE_VERT=0x02; const BADDY=0x04; Then for each object you set its behaviour variable to a certain value using the handy names above. And if you want to have objects with several behaviours then all you have to do is OR the values together like this: int caps=AUTO_MOVE_HORIZ | BADDY Which would be two behaviours; to move left/right by itself and to attack the player. For handling the behaviours use an AND test to find out the necessary action such as: if (caps & BADDY) ATTACK=true; The variable caps is the behaviour variable which is ANDed with each constant we need to test, be sure to bracket the variable and constant if you want to combine two or more tests together: if ((caps & AUTO_MOVE_HORIZ) && (caps & AUTO_MOVE_VERT)) Please remember that the single ampersand (&) in C++ is used with binary values but for testing boolean (true or false) use two ampersands (&&). So the left and right tests end up as true or false results which then are combined into a single boolean test.
The behaviour (caps) variable can belong to each object or you could have a number of presets set up which each object can choose from. For example, you could have the tree behaviour which has the necessary behaviour flags set and any object that acts like a tree can use that preset, and the same for any other behaviours that you need.
The problem with using behaviour flags is that it limits greatly how the objects behave although changes can be made easily. A better way to handle object behaviours is for each object to have a pointer to the behaviour function which updates the object; the function can be as simple or complex as required. A variation is that instead of using a pointer an integer index variable is used instead which is used to look up the behaviour function in a table of pointers to behaviour functions.
Lastly, you could go for a hybrid approach, that is to use behaviour flags for common features such as for handling talking to objects, climbing etc, as well as a pointer or index to a complex behaviour function.
Collision TestingIt's one of the most troublesome parts of programming a video game but very important unless you are creating a virtual world without interaction. At the simplest level you can use bounding shapes which is a shape that the game object's graphics will fit into, often a rectangle. Then you can easily test if a rectangle overlaps another and if it does you can take the necessary action, such as do damage, trigger an event, and so on.
Since a collision occurs when a bounding shape overlaps another you will often see parts of an object's graphics go through another and even more so in 3D games. One way to fix this problem is to have two lots of position variables for every object; before moving an object you make a copy of the object's current position. Then, if you detect a collision after moving the object you can use the copy of the object's position to place the object to where it was before the collision was detected.
ConversationsIn some games, the game objects don't have much to say, in others you can have conversations and select options such as different responses. The way I handle conversations is to have a list of messages; an array of strings such as:
string messages[]={"Hello!",
"Bye!",
"How are you?"};
Each message can be selected using an index value so 0 would choose the message Hello!, 1 would select Bye! and 2 would pick How are you? Next, we need to create the conversations by using commands which are nothing more than constants:
const int MESSAGE=0;
const int END_CONVERSATION=1;
And you can have other commands to do things such as to give the player more lives, make an object attack, etc. Then we have an array of integer values which make up every conversation:
int conversations[]={MESSAGE,1,END_CONVERSATION};
The example above is of just one conversation which will display the message Bye! and then end the conversation. Typically you will either have a delay or wait for the player to press a button before processing the next command (end the conversation in this example).
The MESSAGE commands expect a value to follow which is the index of the message to be displayed from the messages[] array. But the END_CONVERSATION command requires no extra data. You can use a switch statement to execute the coding for the current command (since each command is a unique value).
You will need some way to indicate if the player or someone else is talking, especially if the objects have no mouth animation or if there is only text and no audible speech. A simple way is to change the colour of the text to represent who is talking or you could do something more complicated such as use speech bubbles or something like that placed above the character's head. Let's not forget that in many games the player says very little or nothing at all.
To tell the game who is talking you could store as a global variable (assuming only one conversation can take place at once) the index of the object. But you will still need to include with the conversation data if the player or some other object is saying something, as in most games the index of an object is not known until the object is created.
We can expand the MESSAGE command to use an ID, such as PLAYER_ID for a message that the player is saying and NON_PLAYER_ID for anything said by someone who is not the player. For example:
MESSAGE,2,PLAYER_ID,END_CONVERSATION
Would mean the player says How are you?
Because I have put all of the conversations into one array as to avoid needing a number of different size arrays due to different length conversations, there is a slight complication. If you want to use an index value to pick a certain conversation you will need a look-up table (an array of integers) that contain the index of the starting point of each conversation.
If only one conversation can happen at any time you could use a global variable to remeber the position in the conversation. I use -1 to mean there is no conversation which is an invalid index value for an array but the program can check that before trying to look anything up. Typically you cannot move the player or make the player attack, etc while in a conversation so you will need some way to determine if there is currently a conversation or not.
DebugWhat really helps when trying to fix a problem is to be able to display information on the screen as you're playing, games such as Sonic the hedgehog have been famous for leaving in such features.
So, you could show the values of variables that belong to the object that's causing problems and perhaps you might add the ability to modify the variables directly as you play. Another form of debug, is to display the collision boxes, which can be toggled on or off by pressing a pre-defined key on the keyboard. If you have a function to draw a rectangle, just an outline's best so you can see the sprite within the box, then you shouldn't have trouble writing code to display the collision boxes. You could use two different colour boxes for each object, one for the visible size and the other for the collision box. Graphics(Update: 18/9/9)
Every video game has to have them, graphics are very important otherwise you wouldn't be able to see what you was doing. Much time is spent on the graphics, the designing, the placement of objects within levels and animation.
If you are using Windows then DirectX is perhaps your best bet for getting objects to move about at speed and smoothly. DirectX is a pain to use but when you get used to it you can put the routines into a class so that you won't have to worry about the low-level stuff anymore and so that other games can use the class to access DirectX.
Regardless of whether the game uses 2D or 3D graphics for the levels and objects, most games have some sort of HUD (Heads-Up-Display) for showing the player's lives, health, and so on. While it's best to use symbols to represent these values where possible (e.g., a battery icon that indicates the remaining power in the player's torch), more than likely there will be a need to display text.
To be able to display text at high speed with fancy effects, your best bet is to use a texture that contains the individual characters (letters, numbers, symbols, etc). If each character is the same size and placed at a set interval, only simple calculations are required to select the right part of the texture to get the chosen character; repeat this process to form entire sentences. As 256x256 is supposedly an ideal size for a texture (in terms of speed), each character could be 16x16.
If the text is in ASCII form then it would be a good idea to place the characters in the texture in the ASCII order, starting with the space character and ending with the tilde (~) character. Then there should be room for any special symbols after, that you will need to display, such as icons. How you access those characters may require you to add escape sequences to the string (an escape sequence typically begins with a backslash \ followed by one or more characters such as \xhh to insert the hex value hh into the string).
As well as handling characters to be displayed you will also need to take care of certain control codes such as line feed (a.k.a. new line) which can be used to move onto the next line. And since a texture is used containing the characters, special effects can be used such as scaling, rotation and changing the colour.
You could also include an optional background behind the text (perhaps as a texture) to help the text show up over the game world. If you do use a background behind the text it will need to be sized so that the text fits within perhaps with a slight border around the text. You could place a texture behind each character to acts as the background but for performance reasons it would be better to use a single background that is the size of a one character and then scaled to take up the space of the text plus a border if you want that. However, if a small texture for the background is made many times bigger it may not look so good but it's best to use a more or less plain background behind the text anyway as to let the text stand out over it. By making the background texture at least a little bit transparent it will make for a slightly less plain looking background since the game world will show through somewhat from behind.
How you actually render the indivdual characters and background to the screen depends on what you are using to display the graphics. For Direct3D you can use a sprite which is a 2D object that can use a texture and be positioned and rotated as needed. You only need one actual sprite object that you can use multiple times for each frame and then tell Direct3D to actually draw them.
The main thing is the text can easily be read so a bold font that has a style any can read is best. You could use an existing font for the texture or make up your own, as long as each character is placed at a set distance in the texture and is no bigger than a maximum size.
It's likely that the function to display the textured text will need many input values so it would be a good idea to use a structure that has default values (perhaps using its constructor), which is passed to the text function.
Let's look at an example of where textured text may be needed to explain where it would be required to handle unusual control values in the text. Say you had a pause menu but you wanted to indicate to the player which option is selected by changing its colour. If every pause menu option was displayed using a single textured string (so that a single background is placed behind all the pause menu options) and of the same colour, how can the colour of just one option be changed? The answer is to insert a control value into the text string before the option we want to change the colour for (the function for displaying the text will use that different colour only for a single line of text, i.e., up to a line feed/new line control code):
0x01FF208034
The 0x01 is the control value which tells the text function to change the colour only for the next line of text. Following that is the AARRGGBB colour value to use (0xFF208034). To place those values into the text string you may need to use an escape sequence. Note that 0x01 in ASCII is traditionally a control value anyway so it's not normally used to display a character like 0 or F, so we can use it as we please.
Textured text has many advantages but it requires a lot more work from the programmer as well as putting together the characters in the texture. But it looks so much better than non-textured text and can really help to add a little something extra to your game.
2D Game Graphics
There are three main types of graphics that feature in most games and they are the moving objects (sprites), the background which may be scrolling and the score, etc which usually consist of text and icons. If you are any good at drawing then you might want to create the graphics 'by hand' to be used in your game or you could use existing art (if you have permission to do so). Originally I made the sprites and backgrounds using the art program Paint pixel-by-pixel but more recently I use the ray tracer Pov-Ray so that although the graphics are actually 2D when used in the game, they have a 3D look. Graphic priorities allow you to display some graphics behind or in front of others but this can come at a cost, namely CPU time. The simplest but not the best way is to assign a priority value to every object and then when it's time to draw the graphics, check every object and display it if it has the right priority value. To speed that up you could sort the objects based on their graphic priority and then they would be ready to draw in order. An alternate and more basic approach is to limit the game to only three priority levels which will probably be enough for most games. That way you could have, for example, the player appear in front or behind the other objects. There would be nothing stopping you from changing the graphic priority values as the game is played (provided they aren't const) as to simulate certain effects.
3D Game Graphics
To be added...
InputIf you are programming a game that is to be played on a computer then you will need to assume that the player has at least a keyboard, probably a mouse too. A controller is usually perfect for playing a game but support for it has to be offered as option, although there are a great deal of computer users who have a controller.
DirectInput can be used for interfacing to the keyboard, mouse and controller for fast input but there have been some recent changes to note. Usually, using Windows messages or keyboard events that encapsulate them, are fast enough for keyboard input, and the equivalent mouse events are fine for responding to the mouse. Although DirectInput can be used to communicate with all controllers, XInput was especially designed for Xbox 360 compatible controllers.
If you are going to support different forms of input such as keyboard, mouse and controller, then it would be best to program an input class that provides a universal form of input to the game while handling the different input sources. One way to do that is for the input class to update a virtual controller so that the game uses the same controls but are updated by the selected input device via the input class.
Level editing(Updated: 8/10/9)
When you first start work on your game you might be able to manage with a few objects set up in your coding. But there will come a time when you will need to be able to edit levels and save your changes.
You could either use a separate program to edit the levels or you could integrate a level editor into your game which has the advantage that any changes you make you will be able to see much quicker. If really necessary you could always remove the level editor when you have finished the game, but if you do leave the level editor in, if you were to let other people play your game they could make their own levels.
However you are able to edit the levels you will need to save the level information for each level, perhaps to separate level files.Values that you will need to save for each level include such things as the size of the level, the type of weather, and what music to play. You will also need to save the starting values for every object that is in the level when it first starts (the player is most likely one of those objects). For example, you will need to save the position, rotation, and size for each object. This can be handled in one of two main ways; either save a script (series of commands) or just the actual values for each object. A script has the advantage that it can be edited with a text editor and can contain complex commands but each instruction has to be processed by your game. Saving just values is a lot more limited but means loading a level is very simple (i.e. just load the values and pass them onto the objects).
You may decide later on that you want to add more values to the level files or remove some but you may be faced with the problem that you've already created some levels and don't want to lose them. The solution (although you need to do it from the start) is to save with each level file a version number. If the game finds that the level file is an older version than what the new version of the game uses it can skip certain values. For example, say that in version 2 of level saving you added a value that sets the weather type, but in version 1 the weather was always sunny. If the game detects that it is loading version 1 of a level file it can skip loading the value from the level file and set it to sunny. Note that if you are using level scripts to save the level information you may have a different problem, that is, having to deal with old commands.
My Kitty Wong Game Engine(Updated: 8/7/9)
My Kitty Wong game engine started off as part of several 2D scrolling platform games before I upgraded it to handle 3D games. First I will talk to you about the 2D version and then its offspring, which handles 3D games. I programmed them all using C++ as classes.
Kitty Wong 2D Game Engine
The Kitty Wong game engine gets its name from the game that I made that it was developed for but since then I have re-used and modified it for other games. This is a good point to make clear that you can put a lot of effort into making a game engine which can then be used for other games with changes and even updates so that it's even better than before.
Let me point out first that there are three classes that make the game possible, and they are the main class, the DirectDraw (it's a 2D game) class and the game class which is the Kitty Wong game engine. The main class has to initialize the two other classes and talks to them; the main class tells the DirectDraw class what to display and the main class provides timing for the game class. The game class can also give information to the main class such as for providing debug data about the objects since the main class does not handle the objects directly.
There is actually a fourth class, but it's part of DirectX, and that's the DirectInput class. As with the other classes, the main class sets up the DirectInput class, and gets the state of the keyboard and passes the data onto the game class so that it can move the player, etc as need be.
Now onto the objects in more detail, which are the sprites, the moving parts of the game that interact with each other. Every object will need to have information stored about them so that the game can remember what they are doing.
Some obvious but essential variables are the 2D coordinates (position) called x and y (how ever did I think of that?) and also, width and height sizes that define the rectangle that the sprite fits into.
struct object { That's all very well, but what about if we want the sprite to be invisible at times, or maybe some objects don't have graphics at all. Then we can include and check the state of the visible flag (it's always a good idea to label variables and functions that remind you of what they do and aren't too long, so thisVariableWhenSetDisplaysGraphics wouldn't be such a smart choice, for example). bool visible; Then there's the animated objects that use a number of pre-drawn frames to give the illusion of walking, jumping, etc. I store the frames that the sprites use in an offscreen buffer and align the frames horizontally for each object. When it comes to drawing a sprite, no matter if it's animated or not, it's a simple calculation to find the start of the frame to use from the sprite buffer. I use animFrame for each object to remember what frame is currently being draw, by multiplying it by the width of the sprite (assuming each frame is the same size) you get the start x coordinate of the current frame's position in the buffer for drawing. int animFrame; A game wouldn't be much if you could just walk through everything so collision testing is something you'll have to consider sooner or later. Sometimes we want a reaction regardless of the type of collision other times we need to know exactly what type of collision just occurred. I define four main varities of collisions which are simply named top, bottom, left and right. I deal with collisions between two objects so if, for example, object A landed onto object B, the bottom of object A would be touching object B's top. With respect to object A, this would be classed as a top collision but if you take it from object B's point of view, it would be a bottom collision. So, when an object falls because of gravity (if it is affected; if the Sonic and Mario games have taught us anything it's that platforms can float), we would want to check if that object had landed onto another object that acts like a platform or block. Once we have detected a top collision, we need to look at object B's data to see what type of object it is. bool isBlock; The isBlock flag, when set, tells us that it behaves like an average platform object and will stop objects affected by gravity from falling if landed onto it. If the flag is cleared, however, then objects will just fall past it as if it weren't there. Another use of the isBlock flag is for left and right collisions; if the player is walking or running along and hits (a collision occurs) an object with the isBlock flag set then the player shouldn't be able to walk through it. Also, if the player's jumping and hits the bottom of a block in the sky then the player should start falling. Of course you might want different reactions, these are just suggestions. While on the subject of collisions, a common theme found in countless video games is the attraction of picking up rewards or health items just laying there, ready to be taken. They may be rings, coins, rupees, stars or whatever, we need a way to determine what to do because of the collision. Perhaps there are rings to collect, rotating gold torus shaped items that were no way inspired by the Sonic the Hedgehog games and upon picking one up your score increases. One way to handle such a thing would be to use the isRing flag in the object structure: isRing; Then the coding could be something like this: if (leftHit || rightHit || topHit || botHit) { //Check for all possible collisions score++; //Increase score by one Then we have those nasty baddies that make Sonic loose rings or Link hearts; we use the isGood and isBad flags to determine whether health is lost or some other bad thing happens because of a collision between two objects. if (isGood && isBad) { //Object A is good and object B is bad To do simple collision testing you can calculate the collision box of an object by adding the width and height to its position but there will be times when you'll want the object's collision box to be a dfferent size to the actual sprite. To do this we need four more variables: int colOffX, colOffY; Now the collision box is calculated by adding colOffX to the object's x position and colOffY to its y position. Then colWidth is added to the result of x+colOffX just as colHeight is added to y+colOffY. This allows the collision box to be a different size to what the sprite appears to be, you could even have only half of the object, for example, involved in colision tests. These collision boxes are similar in concept to hot spots, i.e., an area of the sprite-smaller or larger-actually used for collision purposes. The mouse pointer that we're all familiar with has a hotspot; the top of the pointer is used for collision testing but not the bottom, otherwise it wouldn't seem right and could cause problems. Now, what game would be complete without platforms and other objects that move by themselves, helping the hero to cross dangerous lands and reach areas that only a twin-tailed fox could access? You may have platforms that move horizontally or vertically, that change direction after a certain amount of time. To do this, I give each object their own timer (I'm that kind), you could have more than one timer per object but that depends on how advanced the objects you want and how much memory and CPU time you have to spare. int timer; Notice the four variables added to the object structure, starting with the timer which is basically an integer that starts with the value zero and is increased so as long as the timerEn flag is set, otherwise the timer will stop counting as soon as timerEn is false. As for the timeLimit flag, when the timer reaches this value, it'll reset to zero and starting counting up again. The timerOneShot flag doesn't enable the timer to have one shot at getting things right, it's for deciding if the timer should count again once it has reset. If the flag is true then, when timer is the same value a timeLimit, the timer will return to zero but it won't start counting again because timerEn will be made false. The timer can be made to count again simply by setting timerEn to true once more. For the moving platforms you'll want to keep the timerOneShot flag as false but for other objects like special effects it would be best to have the flag set otherwise it may cause problems. Let's talk more about those magical platforms; we can use a number of flags to decide how they should move. bool moveHoriz; If the moveHoriz flag is true the object will move left/right, changing direction whenever its internal timer reaches the time limit. To remember the direction it is travelling and facing (for objects that look different when they face a particular way), the faceRight flag does that job; if true the object is moving to the right but if false then it's moving to the left. It should be no surprise that the moveVert flag causes the object to move up and down, changing direction just like with moving horizontal. The faceUp flag means that the object is moving upwards when true otherwise the object is moing down. What should happen if both flags moveHoriz and moveVert are set at the same time? If programmed right it won't destroy the universe, instad the object will move diagonally. This is a time (a pun?) when it would be a good idea to have two timers per object (if we can afford such a thing or maybe they'll have a special offer of buy one, get one free) so that the object can move horizontally and change direction independently of when it is also moving vertically.
Kitty Wong 3D Game Engine
As well as working with objects that can be transformed (moved, rotated, etc) in 3D, this version of the Kitty Wong game class improved in other ways. Object behaviours are handled better and I created a simple way for objects with different behaviours to 'talk' to each other. However, there were still big problems that needed sorting, especially as the level editor wasn't integrated well into the game class. This prompted me to work on version 2 which aims to do all the hard work, leaving as little as possible to the other classes when it comes to updating the game objects.
ObjectsThe objects are the entities in a game that provide some form of interaction, including the player, items, etc. and usually (but don't have to) have some visual form. I recommend using the STL vector containers since they allow easy adding and removal of objects as well as helpful functions. You can create a structure of the variables that each object has and use that as the vector type.
For spawning objects, you could have a set number of objects that are initialized as needed or actually allow the container to grow, perhaps up to a limit. Either way you will need to know if a particular object is currently being used so each object could have:
bool active;
If false the object is not being used and could be used again as perhaps a different type of object, but if true then the object cannot be used as anything else. To illustrate, imagine the player had found an item; this object's active flag will have been set to true. But the player then picks up the item which is then removed, so its active flag is cleared to false, meaning it can be used as another object.
How the objects are spawned and deleted is really just a concept that helps us understand what is happening. If an object is removed, for example, the memory doesn't actually go anywhere and the object remains, but by changing the values of an object's variables it can look and behave in a totally different way.
If more memory is allocated every time an object is added then memory requirements can be kept low until there are lots of objects. The other way is to have a fixed number of objects which at least means a certain amount of memory will be needed and no more, but then you can never have any more objects. Either way you will still need to check through every object to see if a now unused object (active flag is false) can be used for the object to be spawned.
To be more flexible, it is usual to create new objects as the game is being played but all objects could be spawned when the level was created and then activated as needed. For example, if you had some kind of item container the item to be spawned could already exist but then 'come alive' when the item container is activated by the player. However, that would be restrictive in that only one item could be spawned from the container or you would have to have every type of item ready for one of them to be used, but they could be reused for every item container provided only one was ever needed at a time. Also, you must consider how simple it would be to make a level using either of those methods when using a level designer.
It is important that an object has as few variables as possible, that is, each object uses as little memory as can be. If a value never changes, such as the strength of an object's attack or its maximum speed it can be a constant value shared amongst all objects that use that specific value. You can create sets of values used by certain objects which can be referenced by each object using an index value.
ScrollingGames like Tetris and Pac-Man managed well enough with a static screen but most games scroll horizontally, vertically or in both directions. Usually, the screen doesn't scroll until the playable character reaches the middle of the screen but in some levels of a game the scrolling may happen automatically as to hurry the hero on his or her way. There are two main ways to handle scrolling and which option you go for will pretty much be down to how much memory you have to spare and perhaps what is easier for you to program. If you have enough memory, you could draw all the graphics to a playfield bigger than the screen and then scroll the playfield. The other way is to draw the sprites and other graphics when a certain scroll value has been reached. Whenever the hero reaches the middle of the screen and moves right you increase the horizontal scroll variable until he or she reaches the end of the level. And when the playable character is moving to the left while at the middle of the screen you decrease the horizontal scroll value until the begining of the level is reached. However, an alternate method is simply to calculate the scroll values from the player's position so that he or she is always in the middle of the screen or window but you will need to adjust the values as to not show beyond the start or end of a level. By adding the width of the screen to the scroll value you get the range of x position values that are checked against the other objects'x positions to see if they should be diplayed. To do vertical scrolling you need another scroll variable, of course, but the principle is the same. You have the option to move and update the objects while on or off screen if you want which is more realistic but may cause slowdown. If you update the objects only when they are on screen then, although the game will run smoothly (or it should) objects may appear to suddenly come to life when they become visible. I have to confess that I found vertical scrolling harder to program than the horizontal scrolling and I'll explain why. The horizontal origin (zero) is at the left of the screen which I choose to be the start of the level, as the player moves right the horizontal scroll values increases. But the vertical origin is at the top of the screen yet I wanted the start of the level to be at the bottom so that as the player moves up the vertical scroll value increases. So that had to be reversed which means that both the width and height of the level are defined as positive values which is logical.
Weather EffectsRain or snow, for example, can really change the mood of a level and add to the graphics to create a more familiar look; it's not always sunny in the real wold (especially if you live in England). It's not difficult to create weather effects but you will need a fair amount of memory to remember the position of each rain or snow particle. At the very least you will need an array of positions for each particle; you could use a structure of the location variables. Whether it's rain or snow you're simulating the basic idea is to move each particle down until it reaches the bottom of the screen. This is when you randomly pick a new y value for that particle which by chance will be near the top of the screen; you could also generate a new horizontal position as well. If you don't randomly choose a new position for each particle the rain or snow just won't look right. The rain particles can be displayed simply as lines coloured blue or grey, and snow can be just circles coloured white, of course. To seem a bit more 3D you can alter the length of the rain particle lines or the size of the snow circle particles whenever 'new' particles are needed. So you'll need a size variable for every particle as new particles aren't all generated at the same time. Another extra is to change the speed of all particles after a predefined amount of time. One moment the rain could be falling slowly and the next-fast, giving the impression of a downfall. And even better would be to stop the rain or snow now and then, or change from rain to snow.
Since the weather particles will need fewer variables than the other game objects such as the player, it would be a good idea to keep them separate.
|
You can email me at james.boshikoopa@gmail.com
Why not have a look at my video game MyDream.
New: Update: Level editing
Update: Graphics.
Input.
Conversations.
Animation.
Objects.
Collision Testing |