Demo example: This is a somehow more thorough example but it is not yet a game. In this example various issues are demonstrated including: - the clustering grids - the extended bounding system - the priority lists - the use of the timer - the (efficient) object oriented approach to game programming ---------------------------------------------------------------------------- Lets start with a description of the classes which are new in this example. TOscroll class: This class tries to keep an object inside the view by scrolling the view window every time the object is about to get out of the view. Notice that TOscroll class is completely reusable. The only thing it needs is a pointer to the object that must be kept in view. Note also that you may have just one object of TOscroll in your program. There is no meaning in having more TOscroll objects. TOcloud class: This class represents a cloud that moves horizontally in a specific range and throws thunders. The thunders are created dynamically and move vertically until they hit a solid tile and die. TOguy and TOele class: TOguy implements the main character of the game. The little guy responds to key presses and can walk and jump. TOele represents a vertical elevator. The main character can jump on an elevator and in that case the elevator must carry the little guy until the latter jumps out of the elevator. ---------------------------------------------------------------------------- At the start of the main() function the bitmaps of our sprites are loaded from the disk and inserted to the world by using the functions loadraw() and TOworld::setbitmap(). Then the world data is loaded via a call to TOworld::loadlevel(). In this example the world is loaded from a WDD file instead of being created in execution time. Although a WDD file can carry information about the surface templates ,this particular file (level0.wdd) does not carry such information, so 2 bounding surfaces are inserted in the world through calls to TOworld::setsurface() in run time. Unfortunately a world data description file (WDD) cannot be created easily because of the lack of a level editor. Perhaps some time a level editor will be created and become available to the public. You may 'd like to create one :-) Next the sprite frame descriptors are created in 2 ways: - By using TOworld::make1bitmapdescr() - and by usign the more general TOworld::dprintf() After that the objects are initialized and inserted to the world and then the VESA 2.00 system is initialized and queried about the available modes. Finally the timer and the keyboard handler are initialized, a clustering grid is created and TOworld::animate() is called. ---------------------------------------------------------------------------- There are some interesting things concerning some of the objects. First we want the C1997 sprite to be displayed over all the rest sprites and have a fixed position on the screen even if the view scrolls. To achieve that we must take advantage of the useful characteristics of the priority lists. Notice that the scroll object (scrollobj) is put in priority list 0 and the C1997 object is put in priority list 9. The rest sprites are put in priorities lists 0..8 The C1997 has an action function that always moves the logo to position (TOworld::x+ (constant1), TOworld::y + (constant2)) So we can be sure that the C1997 logo will be drawn over all sprites, because it belongs to list 9, and have a fixed position on the screen, because scrolling is always performed before the action function of C1997 has been called and so (TOworld::x, TOworld::y) does not change in a particular frame. ---------------------------------------------------------------------------- Note the use of a sprite range for the cloud object number 0. A sprite range is set and activated via a call to TOobject::setrange(). A range is nothing more than a box. The active action function of cloud 0 will be called by the game engine only if the view window intersects that box. The sprite ranges can be used to decrease the amount of function calls and proccesing. In most cases the active action functions of the objects have to be called only when the view is near the object. In the other cases the function call would be just a waste of time. ---------------------------------------------------------------------------- In this a example we use a clustering grid to perform collision detection of the main character against the thunders. When the little guy intersects a thunder then the color of the thunders cycles. When we created the grid by calling TOworld::definegridlists() we defined that the grid will cluster objects from object list 4 only. If you take a look at cloud.cc, cloudgo() you will see that every new thunder is put in object list 4. The little guy in guyele.cc, generalaction() checks for collision by simply calling TOworld::gridcollision() ---------------------------------------------------------------------------- The movement of the guy is restricted by the bounding system. The use of the corresponding functions TOworld::howfarXX() is demonstrated in guyele.cc Every time the guy wants to move towards a direction first queries the bounding system by calling one of TOworld::howfarXX(). Each one of these functions returns how many pixels actually the guy can move without getting out of the bounds. Then the guy uses that value to arrange its movement. ---------------------------------------------------------------------------- BETATRON may work in a lot of different video modes. Not all video modes have the same retrace period. Some of the them have fast cycle while other have slow cycle. Because of that, in order to retain a steady animation speed you have to use the TOworld built-in timer. The built-in timer runs at a stable frequency and so it is ideal for time measurements. In this example the timer is used to adjust the animation of the main character and some other objects too. Generally there are 2 ways to use the timer in order to simulate physical movements: 1. Keep changing the position of an object in fixed steps for a fixed number of times. For example, keep increasing the x-coordinate of a bullet 10 times by 2 pixels each time. It must be obvious that the bullet will move 20 pixels. However in order to have the bullet accelerated or decelerated you must not increase it's x-coordinate at equal time spaces. Increase the time space if you want the bullet to slow down and decrease it if you want the bullet to speed up. This way should be always used, if you want to control exactly how far an object will move. For example when the little guy jumps, we would like it to move up a fixed number of pixels. However in this example i have used the second way to make the little guy jump. 2. Keep changing the position of an object every frame in some (non-fixed) steps for a fixed period of time. This method has the effect that the amount of the object's movement will vary depending on the (measured) time between two consecutive frames. For example, if TOworld::retraceticks becomes smaller then the object will move further. Try to set a smaller value for TOworld::retraceticks (after calling TOworld::inittimer() ) and watch the little guy jumping higher.