1958 lines
62 KiB
Plaintext
1958 lines
62 KiB
Plaintext
|
|
|||
|
|
|||
|
Animation Construction Kit 3D
|
|||
|
ACK-3D
|
|||
|
Lary Myers
|
|||
|
|
|||
|
|
|||
|
Contents:
|
|||
|
1 ........... Purpose
|
|||
|
2 ........... Using the ACK engine (DISCLAIMER)
|
|||
|
3 ........... Hardware Requirements
|
|||
|
4 ........... Development Environment
|
|||
|
5 ........... Multi-User
|
|||
|
6 ........... The Interface Structure
|
|||
|
7 ........... Important fields in the Interface Structure
|
|||
|
8 ........... Array Formats in the Interface Structure
|
|||
|
9 ........... Initializing the ACK-3D engine
|
|||
|
10 ........... ACK-3D Bitmaps
|
|||
|
11 ........... Loading Bitmaps
|
|||
|
12 ........... Creating Objects
|
|||
|
13 ........... Using Overlays
|
|||
|
14 ........... Moving around in ACK-3D
|
|||
|
15 ........... Moving objects
|
|||
|
16 ........... Normal and Secret Doors
|
|||
|
17 ........... Sound and ACK-3D
|
|||
|
18 ........... Function summary
|
|||
|
19 ........... Where to Begin (EXAMPLES)
|
|||
|
20 ........... Closing Comments
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
(1) Purpose:
|
|||
|
|
|||
|
ACK-3D began as an experiment to emulate the 3D effects seen in games like
|
|||
|
Wolfenstein-3D and Ken's Labyrinth and to make the technique available to the
|
|||
|
general public. So far things have worked out very well and in some areas have
|
|||
|
even exceeded the before mentioned games. With this latest installment ACK-3D
|
|||
|
gives the developer an easy to use library that can be linked into thier own
|
|||
|
applications without having to delve into the inner workings of the ray-casting
|
|||
|
engine. My hope is that others will use the ACK-3D engine to produce a new
|
|||
|
generation of 3D games that all of us can enjoy.
|
|||
|
|
|||
|
Please forgive any glaring mistakes in this documentation. I find that I can
|
|||
|
write code all day long without getting weary, but as soon as I have to write
|
|||
|
documentation, forget it, too much like work! I hope the text below gives you
|
|||
|
enough information to use the engine.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
(2) Using the ACK-3D engine: (DISCLAIMER)
|
|||
|
|
|||
|
The ACK-3D engine is being released as "PublicWare" and can be freely used
|
|||
|
in any private or commercial programs. The only restriction I would impose is
|
|||
|
that anything developed with the engine have its own disclaimer about liability
|
|||
|
and that under no circumstances will the author (me) be liable for any damages
|
|||
|
of any kind that may result from the use of the engine. "Basically the engine
|
|||
|
is being released "AS-IS" so it's left up to the developer to use it in a
|
|||
|
professional manner".
|
|||
|
|
|||
|
If you develope a wonderful new game using the engine, I'd appreciate a
|
|||
|
mention in the documentation or credits screen, but only if you feel the ACK
|
|||
|
engine helped the game. Thanks.
|
|||
|
|
|||
|
|
|||
|
(3) Hardware Requirements:
|
|||
|
|
|||
|
The current version of ACK-3D requires a minimum of a 386 33Mhz machine and
|
|||
|
will not run on a 286 or below because of 386 code being used. A coprocessor
|
|||
|
is NOT required or needed for the engine itself. Any other hardware requirements
|
|||
|
are left up to the applications that use the engine (ie. mouse, VGA, etc).
|
|||
|
A VGA is only required if the ACK display routines are used (which is up to the
|
|||
|
application). If the application chooses to use another display mode then it
|
|||
|
should issue the appropriate hardware requirements.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
(4) Development Environment:
|
|||
|
|
|||
|
ACK-3D was developed using BorlandC Version 3.1 in compact model. It is
|
|||
|
setup to also use large model (both may be supplied). The assembly routines
|
|||
|
were compiled using the Microsoft Assembler (MASM) version 5.00A and should
|
|||
|
work with later versions of MASM. The library was created using the Microsoft
|
|||
|
Library Manager (LIB) version 3.02 and should also work with later versions
|
|||
|
of LIB.
|
|||
|
|
|||
|
All of the ACK code is written in either normal C or assembler.
|
|||
|
|
|||
|
Compiler switches used with Borland;
|
|||
|
|
|||
|
-ml = Large model
|
|||
|
|
|||
|
-c = Compile only, don't link
|
|||
|
|
|||
|
-3 = Use 386 instructions
|
|||
|
|
|||
|
-O2 = Compile for size
|
|||
|
|
|||
|
-G = Compile for speed
|
|||
|
|
|||
|
-I = specifies the include path for header files
|
|||
|
|
|||
|
-o = specifies the object path and file name
|
|||
|
|
|||
|
|
|||
|
Assembler switches used with MASM;
|
|||
|
|
|||
|
/Ml = Case sensitive for all symbols and labels
|
|||
|
|
|||
|
/B63 = Size of buffer during assembly (does not affect OBJ file)
|
|||
|
|
|||
|
/D_ML
|
|||
|
/D_MC = Assembly directive for large (_ML) or compact (_MC) model.
|
|||
|
Used by ET.MAC for various macros.
|
|||
|
|
|||
|
|
|||
|
Directory structure;
|
|||
|
|
|||
|
I use the following subdirectories for the ACK engine and demo. This
|
|||
|
is provided for your information. It is what the .MAK files are setup for.
|
|||
|
|
|||
|
Engine source;
|
|||
|
|
|||
|
\borlandc\ack3d\eng <- .C .H .MAK files
|
|||
|
|
|||
|
\borlandc\ack3d\eng\cmobj <- Compact model .OBJ and .LIB files
|
|||
|
|
|||
|
\borlandc\ack3d\eng\lmobj <- Large model .OBJ and .LIB files
|
|||
|
|
|||
|
|
|||
|
Demo source;
|
|||
|
|
|||
|
\borlandc\ack3d\demo <- .C .H .BAT .MAK .EXE .DAT files
|
|||
|
|
|||
|
\borlandc\ack3d\demo\bitmaps <- .BBM and .LBM files
|
|||
|
|
|||
|
\borlandc\ack3d\demo\sound <- .VOC .CMF .PWM files
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
(5) Multi-User:
|
|||
|
|
|||
|
ACK-3D uses an interface structure to communicate between the application
|
|||
|
and the engine. This structure (which will be explained later) contains the
|
|||
|
pertinent data that the engine needs to draw the current Point of View (POV).
|
|||
|
It is possible to do some fancy things with this approach. Here are some of
|
|||
|
the things I've tried;
|
|||
|
|
|||
|
1. Displaying more than one view at the same time such as directly in
|
|||
|
front (the normal view) and directly behind (essentially eyes in the
|
|||
|
back of your head!).
|
|||
|
|
|||
|
2. Displaying more than one player on the map is also possible but has
|
|||
|
some restrictions that may make it unusable. These restrictions may
|
|||
|
be lifted in the near future.
|
|||
|
|
|||
|
|
|||
|
Some discussion has been going on for a network version of ACK-3D. While
|
|||
|
this is not built into the engine for this version, it does seem to be a
|
|||
|
straightforward progression from this point. Future versions of the engine
|
|||
|
may indeed support multiple players logging into a 3D world and interacting
|
|||
|
with each other in real-time!
|
|||
|
|
|||
|
|
|||
|
(6) The Interface Structure:
|
|||
|
|
|||
|
ACK-3D uses a structure from the application to get the necessary
|
|||
|
information for building a complete POV. It is the applications responsibility
|
|||
|
to correctly create and setup this structure before the ACK engine is called.
|
|||
|
Limited bounds checking is in the engine to prevent degradation of speed during
|
|||
|
the build process. UNPREDICTABLE RESULTS MAY OCCUR WITH INVALID DATA!
|
|||
|
|
|||
|
The structure is defined in the header file ACK3D.H and can be either in
|
|||
|
the data segment of the calling application or allocated in memory whichever
|
|||
|
the application desires. Within the structure are variables for things like the
|
|||
|
current location and angle of the player, the actual map arrays for the walls
|
|||
|
and objects, pointers to the screen buffer, overlay buffer and background
|
|||
|
buffer, as well as dimensions of the viewport to build the walls into. The
|
|||
|
term "build" is being used here because the ACK engine will not display to the
|
|||
|
screen unless told to do so in a separate function. This way the application
|
|||
|
is free to display the current POV anyway it wants to, or it can use the
|
|||
|
supplied function to display if it doesn't. This offers maximum flexibility in
|
|||
|
how the engine is used.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
(7) Important fields in the Interface Structure:
|
|||
|
|
|||
|
Before calling the ACK engine to initialize, some very important items
|
|||
|
need to be filled into the interface structure. These are described below;
|
|||
|
|
|||
|
WinStartX <- The leftmost pixel coordinate of the viewport
|
|||
|
WinEndX <- The rightmost pixel coordinate of the viewport
|
|||
|
WinStartY <- The upper pixel coordinate of the viewport
|
|||
|
WinEndY <- The lower pixel coordinate of the viewport
|
|||
|
|
|||
|
These four fields will allow the ACK engine to fill in some other fields
|
|||
|
within the interface structure, such as WinWidth, WinHeight, etc.
|
|||
|
|
|||
|
Before calling the function to construct the background, the following
|
|||
|
fields must be filled in;
|
|||
|
|
|||
|
TopColor <- Color value of the ceiling
|
|||
|
BottomColor <- Color value of the floor
|
|||
|
LightFlag <- Whether light shading is on or off
|
|||
|
|
|||
|
Before calling the function to actually build the POV, the following fields
|
|||
|
must be filled in;
|
|||
|
|
|||
|
xPlayer <- X coordinate for the POV
|
|||
|
yPlayer <- Y coordinate for the POV
|
|||
|
PlayerAngle <- POV angle in ACK units
|
|||
|
|
|||
|
|
|||
|
The ACK engine relies on the POV fields above to build the current scene.
|
|||
|
By changing these values it's possible to build a variety of scenes one after
|
|||
|
the other. For example, changing the PlayerAngle to be 180 degrees from the
|
|||
|
current angle and then calling the build routine, will create a scene that is
|
|||
|
directly behind the current POV.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
(8) Array Formats in the Interface Structure:
|
|||
|
|
|||
|
Several arrays are used to build the 3D POV scene. ACK-3D uses a map layed
|
|||
|
out in squares like graph paper to determine what the POV sees at any given
|
|||
|
time. This map is a 2 dimensional array of 64 columns by 64 rows. When the
|
|||
|
initialize routine reads in this map, it processes the data and builds the
|
|||
|
arrays in the interface structure. These arrays are xGrid and yGrid which are
|
|||
|
used for drawing the walls. The layout of these arrays is different from the
|
|||
|
map file and is described below;
|
|||
|
|
|||
|
|
|||
|
1,1,1,1,1,1,1,1 Example map of size 8x8, the real map
|
|||
|
1,0,0,0,0,0,0,1 would be the same except 64x64
|
|||
|
1,0,0,0,0,0,0,1
|
|||
|
1,0,1,1,1,0,0,1
|
|||
|
1,0,1,0,1,0,0,1
|
|||
|
1,0,1,1,1,0,0,1
|
|||
|
1,0,0,0,0,0,0,1
|
|||
|
1,1,1,1,1,1,1,1
|
|||
|
|
|||
|
|
|||
|
|
|||
|
This is an example of a map where 0 is a blank square and non-zero values
|
|||
|
represent wall bitmap numbers (from 1 to 255). The ACK engine requires this
|
|||
|
same information broken down into walls that fall on X planes and walls that
|
|||
|
fall on Y planes, so the array above is changed into;
|
|||
|
|
|||
|
1,1,1,1,1,1,1,1,1
|
|||
|
1,0,0,0,0,0,0,1,1 This would be a map of X walls, note the extra
|
|||
|
1,0,0,0,0,0,0,1,1 column on the right side of the map.
|
|||
|
1,0,1,1,1,0,0,1,1
|
|||
|
1,0,1,0,1,0,0,1,1
|
|||
|
1,0,1,1,1,0,0,1,1
|
|||
|
1,0,0,0,0,0,0,1,1
|
|||
|
1,1,1,1,1,1,1,1,1
|
|||
|
|
|||
|
|
|||
|
|
|||
|
1,1,1,1,1,1,1,1
|
|||
|
1,0,0,0,0,0,0,1 This would be a map of Y walls, note the extra
|
|||
|
1,0,0,0,0,0,0,1 row on the bottom of the map.
|
|||
|
1,0,1,1,1,0,0,1
|
|||
|
1,0,1,0,1,0,0,1
|
|||
|
1,0,1,1,1,0,0,1
|
|||
|
1,0,0,0,0,0,0,1
|
|||
|
1,1,1,1,1,1,1,1
|
|||
|
1,1,1,1,1,1,1,1
|
|||
|
|
|||
|
|
|||
|
This is basically what becomes of the original map in order for the engine
|
|||
|
to see walls in either the X or Y planes. What this means is that anytime the
|
|||
|
map needs to be looked at, the following applies;
|
|||
|
|
|||
|
|
|||
|
int MapPosn; /* Location to look at in map */
|
|||
|
|
|||
|
|
|||
|
xGrid[MapPosn]; /* This would be the left X walls */
|
|||
|
xGrid[MapPosn + 1]; /* This would be the right X wall */
|
|||
|
|
|||
|
yGrid[MapPosn]; /* This would be the top Y wall */
|
|||
|
yGrid[MapPosn + GRID_WIDTH]; /* This would be the bottom wall */
|
|||
|
|
|||
|
|
|||
|
If all four of these locations is 0 then there is nothing at this square in
|
|||
|
the map. What does this mean? One thing that would be possible (but not part of
|
|||
|
the ACK engine), is that walls would not have to be cubes, but could be single
|
|||
|
wall panels (might look alittle funny from the side since they wouldn't have
|
|||
|
any width to them). It could also mean that different bitmaps could be used
|
|||
|
for various sides of the cube.
|
|||
|
|
|||
|
|
|||
|
The next two arrays are bMaps and oMaps which, for now, are simple arrays
|
|||
|
that point to the wall and object bitmaps respectively. Each bitmap takes
|
|||
|
4096 bytes of memory for the 64x64 size bitmap. BUT, the important point is
|
|||
|
that ACK reads these bitmaps in normal row order, where there is a row of
|
|||
|
color bytes one after the other, like so;
|
|||
|
|
|||
|
1,2,3,... An arbitrary bitmap of 64x64
|
|||
|
1,1,1,...
|
|||
|
4,2,4,...
|
|||
|
....
|
|||
|
|
|||
|
This bitmap is then "rotated" 90 degrees so it is in column order, which
|
|||
|
makes it easier to use when drawing the bitmap, thus the above becomes;
|
|||
|
|
|||
|
1,1,4,.... Bitmap rotated 90 degrees
|
|||
|
2,1,2,....
|
|||
|
3,1,4,....
|
|||
|
|
|||
|
-----------------------------------------
|
|||
|
Another important array in the interface structure is the PalTable[] array
|
|||
|
which controls how the ACK engine will perform light shading. The array contains
|
|||
|
16 ranges of 256 colors each which are used to substitute for the actual colors
|
|||
|
of the bitmap based on the distance away from the POV.
|
|||
|
|
|||
|
The distance to the wall or object is first divided by 64 to get the zone
|
|||
|
to use for shading (zones greater than 15 are set at 15). This zone of 256 is
|
|||
|
then used as a lookup table indexed by the color of the bitmap. What all this
|
|||
|
allows is a gradual darkening of walls and objects as they become farther away
|
|||
|
from the POV while still allowing some colors to be used as constant lighting
|
|||
|
colors (they will never darken with distance).
|
|||
|
|
|||
|
|
|||
|
-----------------------------------------
|
|||
|
|
|||
|
Now we come to doors. Within the interface structure there is a sub-
|
|||
|
structure labeled Door[MAX_DOORS]. This structure array holds the current
|
|||
|
status of all doors that may be in the process of opening or closing. This
|
|||
|
array is NOT all the doors in the map, only those that are in motion. If the
|
|||
|
ColOffset field of the array is non-zero the door is active and either opening
|
|||
|
or closing. When an application makes a call to AckCheckDoorOpen() the engine
|
|||
|
will determine if the POV is close enough to a door and initialize it in the
|
|||
|
Door[] array. After that, every call to AckBuildView() will cause the door to
|
|||
|
be updated until it goes fully open then fully closed. During this time the
|
|||
|
application is free to alter the fields in the Door[] array (BEWARE that mPos
|
|||
|
and mPos1 are map positions and should NOT be altered during this time). If,
|
|||
|
for example, the application wants a door to remain open for a long period of
|
|||
|
time, it can check the value of ColOffset to see where a door currently is. If
|
|||
|
the value is less than 64 the door is partially open (or partially closed).
|
|||
|
Once the value becomes greater than 64 the door is no longer visible and is
|
|||
|
fully open. By keeping the value greater than 64 (setting the Speed field to
|
|||
|
zero) the door will no longer be moved. Another way might be to set the field
|
|||
|
ColOffset to zero and not allow the door to be seen anymore. This method would
|
|||
|
be the best if the application desires the door to remain permanently open.
|
|||
|
Here is a breakdown of the DOORS structure;
|
|||
|
|
|||
|
|
|||
|
mPos <- Map position for one side of cube
|
|||
|
|
|||
|
mPos1 <- Map position for other side of cube
|
|||
|
|
|||
|
mCode <- Bitmap code that represents the door
|
|||
|
|
|||
|
mCode1 <- Bitmap code for door on other side
|
|||
|
|
|||
|
ColOffset <- Current column offset of door (see above)
|
|||
|
|
|||
|
Speed <- Speed is added to ColOffset during open/close
|
|||
|
|
|||
|
Type <- Type of door (ie DOOR_XCODE or DOOR_YCODE)
|
|||
|
|
|||
|
Flags <- Current door action (DOOR_OPENING or DOOR_CLOSING)
|
|||
|
|
|||
|
The current version of the ACK engine uses the following wall bitmap values
|
|||
|
for doors;
|
|||
|
|
|||
|
60 <- Door lies on the X plane (vertical)
|
|||
|
|
|||
|
61 <- Side panel that appears on both sides of door
|
|||
|
|
|||
|
62 <- Door lies on the Y plane (horizontal)
|
|||
|
|
|||
|
|
|||
|
-----------------------------------------
|
|||
|
|
|||
|
Okay, now let's look at the OBJECT sub-structure. Again, this is an array
|
|||
|
stored within the interface structure and accessable to the application as well
|
|||
|
as the ACK engine. Every object in the map is represented by an entry in this
|
|||
|
array. The AckInitialize() function automatically fills in the initial X and Y
|
|||
|
coordinates of the object when it finds one in the map file. The rest of the
|
|||
|
object data must be filled in by the application before the objects can be
|
|||
|
used. The section below on Creating Objects describes what fields should be
|
|||
|
setup by the application.
|
|||
|
|
|||
|
Some of the other fields in the OBJECT array are described below;
|
|||
|
|
|||
|
Active <- 0 or 1 to indicate the object should be considered during
|
|||
|
the AckCheckObjectMovement() function.
|
|||
|
|
|||
|
bmNum <- Holds from 1 to MAX_VIEW bitmap indices to display for
|
|||
|
the object. If more than 1 bitmap the object will either
|
|||
|
animate in place or display multiple views as the POV
|
|||
|
walks around the object.
|
|||
|
|
|||
|
Sides <- Calculated by AckCreateObject() function dependent on
|
|||
|
the number of bitmaps in bmNum.
|
|||
|
|
|||
|
Dir <- Which direction the object will move.
|
|||
|
|
|||
|
Flags <- This field can contain OF_PASSABLE which allows the POV
|
|||
|
to walk right through the object. (Good for overhead
|
|||
|
lights and such). It can also contain OF_ANIMATE, which
|
|||
|
is used by the function AckCheckObjectMovement() to
|
|||
|
cycle between multiple bitmap images for the object.
|
|||
|
|
|||
|
CurNum <- Current index (base 0) of the bitmap to display out of
|
|||
|
bmNum. If the object only has 1 bitmap then CurNum will
|
|||
|
always be 0.
|
|||
|
|
|||
|
MaxNum <- Total number of bitmaps (base 0) contained in bmNum. If
|
|||
|
the object only has 1 bitmap then MaxNum will be 1.
|
|||
|
|
|||
|
Speed <- The speed at which the object will move. Normal values
|
|||
|
are from 2 to 40, higher than this causes alot of
|
|||
|
jmuping and may cause the object to pass through walls.
|
|||
|
|
|||
|
VidRow <- Reserved.
|
|||
|
|
|||
|
x and y <- Map coordinates of the object, from 0 to 4095.
|
|||
|
|
|||
|
mPos <- The actual map grid location of the object. If x and y
|
|||
|
are changed by the application then mPos should also
|
|||
|
be updated by using the equation;
|
|||
|
|
|||
|
mPos = (y & 0xFFC0) + (x >> 6);
|
|||
|
|
|||
|
|
|||
|
The Dir field is a left-over from the older version of the engine, where
|
|||
|
it was used for crude 8 direction movement, or rotating in place. With this
|
|||
|
latest version, the OF_ANIMATE flag is used to indicate cycling through the
|
|||
|
bitmaps for the object and the Dir field is ignored. The application can
|
|||
|
still use this field for storing an angle of movement for use with the
|
|||
|
function AckMoveObjectPOV().
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
(9) Initializing the ACK-3D engine:
|
|||
|
|
|||
|
Before anything can be done with the ACK engine, it must be initialized.
|
|||
|
This is done by creating the interface structure and passing it into the
|
|||
|
AckInitialize() function. The following example shows one method to accomplish
|
|||
|
this;
|
|||
|
|
|||
|
|
|||
|
ACKENG ae; /* Interface structure in global memory */
|
|||
|
|
|||
|
int main()
|
|||
|
{
|
|||
|
int result;
|
|||
|
|
|||
|
|
|||
|
ae.WinStartY = 0; /* Setup viewport coordinates */
|
|||
|
ae.WinEndY = 104;
|
|||
|
ae.WinStartX = 0;
|
|||
|
ae.WinEndX = 319;
|
|||
|
|
|||
|
result = AckInitialize(&ae); /* Initialize the engine */
|
|||
|
|
|||
|
if (result)
|
|||
|
{
|
|||
|
printf("Error initializing - Code: %d\n",result);
|
|||
|
exit(1);
|
|||
|
}
|
|||
|
|
|||
|
result = AckReadMapFile(&ae,"DEMOMAP.L01"); /* Read map file */
|
|||
|
|
|||
|
if (result)
|
|||
|
{
|
|||
|
printf("Error reading map - Code: %d\n",result);
|
|||
|
exit(1);
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
Example 1
|
|||
|
|
|||
|
|
|||
|
The function AckInitialize() will return an error code if there was a
|
|||
|
problem initializing the engine or a zero if successful. At this point the
|
|||
|
ACKENG interface structure is initialized, the file "TRIG.DAT" has been
|
|||
|
read into the various tables that ACK-3D requires.
|
|||
|
|
|||
|
No bitmaps have been dealt with yet so there is still more to do before
|
|||
|
actually drawing the first POV.
|
|||
|
|
|||
|
No objects have been dealt with yet. The application must handle these
|
|||
|
either on its own or by using one of the supplied ACK-3D functions.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
(10) ACK-3D Bitmaps:
|
|||
|
|
|||
|
All bitmaps used by ACK-3D are 64 pixels wide by 64 pixels tall and are
|
|||
|
stored in normal line by line fashion (ie. One row of 64 pixels then another
|
|||
|
row, and so forth). Each byte represents one pixel and may have a color from
|
|||
|
0 to 255. The engine itself requires the bitmaps to be in raw image form,
|
|||
|
however the function AckLoadBitmap() will accept either raw IMG format or
|
|||
|
Deluxe Paint II brush files (.BBM extensions).
|
|||
|
|
|||
|
|
|||
|
(11) Loading Bitmaps:
|
|||
|
|
|||
|
ACK-3D provides several functions to load in bitmaps. These functions don't
|
|||
|
have to be used and are only provided for convienence to the application. They
|
|||
|
are as follows;
|
|||
|
|
|||
|
NOTE: If the application chooses to load it's own bitmaps it MUST be sure
|
|||
|
to rotate the bitmaps 90 degrees into the column/row order needed
|
|||
|
by the engine. Also, the function below uses Extended memory (XMS)
|
|||
|
if available which the application may or may not decide to support.
|
|||
|
|
|||
|
|
|||
|
int AckLoadBitmap(ACKENG *ae,int BitmapNumber,int BitmapType,
|
|||
|
char *bmFileName);
|
|||
|
|
|||
|
where:
|
|||
|
ae <- Interface structure
|
|||
|
BitmapNumber <- A value from 1 to 255
|
|||
|
BitmapType <- Either TYPE_WALL or TYPE_OBJECT
|
|||
|
bmFileName <- Name of bitmap file to read
|
|||
|
|
|||
|
This is the general purpose bitmap load routine. It will read either
|
|||
|
raw image files (.IMG) or Deluxe Paint brush files (.BBM) and place the
|
|||
|
bitmap into the appropriate bitmap array (bMaps for walls and oMaps for
|
|||
|
objects).
|
|||
|
|
|||
|
|
|||
|
int AckLoadWall(ACKENG *ae,int WallNumber,char *bmFileName);
|
|||
|
|
|||
|
where:
|
|||
|
ae <- Interface structure
|
|||
|
WallNumber <- Value from 1 to 255
|
|||
|
bmFileName <- Name of bitmap file to read
|
|||
|
|
|||
|
This routine simply calls AckLoadBitmap() with TYPE_WALL set.
|
|||
|
|
|||
|
|
|||
|
int AckLoadObject(ACKENG *ae,int BmpNumber,char *bmFileName);
|
|||
|
|
|||
|
where:
|
|||
|
ae <- Interface structure
|
|||
|
BmpNumber <- Value from 1 to 255
|
|||
|
bmFileName <- Name of bitmap file to read
|
|||
|
|
|||
|
This routine simply calls AckLoadBitmap() with TYPE_OBJECT set.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
(12) Creating Objects:
|
|||
|
|
|||
|
Objects in ACK-3D can be either stationary or movable depending on the needs
|
|||
|
of the application. Both types of object are handled exactly the same, the
|
|||
|
stationary ones just never change location. Objects can share bitmaps if needed
|
|||
|
and can have more than one bitmap to accomplish different tasks. If the object
|
|||
|
has multiple bitmaps it can either stay in one spot and display the bitmaps
|
|||
|
in sequence (this animates the object) or can use the mulitple bitmaps to show
|
|||
|
different views of the object when the POV is walking around it.
|
|||
|
|
|||
|
The sequence for creating an object is as follows;
|
|||
|
|
|||
|
1) Load any bitmaps associated with the object.
|
|||
|
|
|||
|
2) Specify the object speed and any flags such as OF_PASSABLE or OF_ANIMATE.
|
|||
|
|
|||
|
3) Call the routine AckCreateObject() to fill-in other information in the
|
|||
|
object structure.
|
|||
|
|
|||
|
Example;
|
|||
|
|
|||
|
int main()
|
|||
|
{
|
|||
|
|
|||
|
/* Initialization done per Example 1 above */
|
|||
|
|
|||
|
AckLoadObject(&ae,1,"object1.bbm"); /* Load in a bitmap for object */
|
|||
|
|
|||
|
ae.ObjList[1].Flags |= OF_ANIMATE; /* Stay in place and animate */
|
|||
|
ae.ObjList[1].Speed = 1; /* Speed is non-zero to activate */
|
|||
|
AckCreateObject(&ae,1,1,nums);
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
Example 2
|
|||
|
|
|||
|
|
|||
|
The example above uses an array of unsigned chars called nums[] to specify
|
|||
|
the different views of the object. In this case there is only one so the number
|
|||
|
of views is also passed as 1. There can be as many as MAX_VIEWS (ack3d.h)
|
|||
|
different bitmaps assigned to one object for use as animation, etc.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
(13) Using Overlays:
|
|||
|
|
|||
|
If the application so desires, an overlay screen can be used with ACK-3D.
|
|||
|
This overlay will only be effective if the AckDisplayScreen() function is used,
|
|||
|
unless the application manipulates the overlay on its own.
|
|||
|
|
|||
|
What an overlay does is allow a full screen picture to be used which may
|
|||
|
overlay some of the area where walls are displayed. There could be ancient
|
|||
|
pillars, or merely a sign that says "Demo in progress" or whatever, the whole
|
|||
|
point to the overlay is to provide a means of displaying graphics over the top
|
|||
|
of the viewport when displayed on the screen.
|
|||
|
|
|||
|
The overlay is read in like a normal bitmap, except it is full-screen in
|
|||
|
size (320x200). It is then compiled into drawing commands that are placed in
|
|||
|
the interface structure pointed to by OverlayBuffer. Everytime the function
|
|||
|
AckDisplayScreen() is called, this pointer is used to draw the overlay on top
|
|||
|
of the last POV that was built.
|
|||
|
|
|||
|
Overlays are optional and do not need to be used unless desired by the
|
|||
|
application. For those who wish to process the compiled overlay themselves,
|
|||
|
here is the format for the OverlayBuffer;
|
|||
|
|
|||
|
|
|||
|
Length 2 bytes <- Length of data below (does not include offset)
|
|||
|
Offset 2 bytes <- Offset into screen to show data
|
|||
|
Data n bytes <- Actual data of size Length
|
|||
|
|
|||
|
Length,Offset,Data combinations will continue until a Length of zero
|
|||
|
is reached, meaning no more data.
|
|||
|
|
|||
|
The function AckDrawOverlay(ScreenBuffer,OverlayBuffer) can be used to
|
|||
|
place the overlay on top of any walls that were drawn. This function processes
|
|||
|
the overlay commands described above and draws into the screen buffer. The
|
|||
|
application can then do any additional drawing it desires before actually
|
|||
|
displaying to the video.
|
|||
|
|
|||
|
|
|||
|
(14) Moving around in ACK-3D:
|
|||
|
|
|||
|
Once things are initialized and the current POV is built and displayed, it
|
|||
|
becomes time to move around in the map. This is accomplished by using the
|
|||
|
function:
|
|||
|
|
|||
|
int AckMovePOV(ACKENG *ae,int Angle,int Amount);
|
|||
|
|
|||
|
where:
|
|||
|
ae <- Interface structure
|
|||
|
Angle <- Direction to move
|
|||
|
Amount <- Amount to move
|
|||
|
|
|||
|
|
|||
|
In its simplest form the function can just be called with the current angle
|
|||
|
the POV is facing and some amount to move, such as;
|
|||
|
|
|||
|
AckMovePOV(&ae,ae.PlayerAngle,16);
|
|||
|
|
|||
|
But it can also be used to backup, with the following;
|
|||
|
|
|||
|
NewAngle = ae.PlayerAngle + INT_ANGLE_180;
|
|||
|
|
|||
|
if (NewAngle >= INT_ANGLE_360)
|
|||
|
NewAngle -= INT_ANGLE_360;
|
|||
|
|
|||
|
AckMovePOV(&ae,NewAngle,16);
|
|||
|
|
|||
|
This function does the necessary collision detection and returns 0 if the
|
|||
|
POV actually moved, in which case the values ae.xPlayer and ae.yPlayer have
|
|||
|
been updated with the new coordinates of the POV.
|
|||
|
|
|||
|
If the application wishes to do the moving itself, it can make a call to
|
|||
|
the function AckCheckHit() which returns 0 if no walls are in the way. Any
|
|||
|
collisions with objects will not be returned from AckCheckHit.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
(15) Moving Objects:
|
|||
|
|
|||
|
ACK-3D contains two functions for handling object animation and movement.
|
|||
|
Animation is performed by switching the displayed bitmap for the object
|
|||
|
whenever the function is called. The application is responsible for setting
|
|||
|
up the object structure to provide the engine with the necessary information
|
|||
|
to animate or move the object.
|
|||
|
|
|||
|
void AckCheckObjectMovement(ACKENG *ae);
|
|||
|
|
|||
|
This function will check all active objects and determine if any need
|
|||
|
to have thier bitmaps changed for animation. The application can perform this
|
|||
|
check itself if so desired. This function is provided for convienence.
|
|||
|
|
|||
|
|
|||
|
int AckMoveObjectPOV(ACKENG *ae,int ObjIndex,int Angle,int Amount);
|
|||
|
|
|||
|
This function actually moves the object at the specified Angle for the
|
|||
|
specified Amount. The return value can be processed by the application to
|
|||
|
determine if the object has struck a wall, another object, or the player.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
(16) Normal and Secret Doors:
|
|||
|
|
|||
|
Once ACK-3D reads in the map file, it is processed for a variety of things,
|
|||
|
one of these being where doors will appear in the map. The application should
|
|||
|
establish where doors will appear within the map file BEFORE calling the
|
|||
|
function AckInitialize(). Doors are treated as special walls in the ACK engine
|
|||
|
and will not automatically be checked to see if they should open. This is up
|
|||
|
to the application and can be accomplished with the function;
|
|||
|
|
|||
|
|
|||
|
void AckCheckDoorOpen(int xPlayer,int yPlayer,int PlayerAngle,ACKENG *ae);
|
|||
|
|
|||
|
where:
|
|||
|
xPlayer <- Current x coordinate of the POV
|
|||
|
yPlayer <- Current y coordinate of the POV
|
|||
|
PlayerAngle <- Current angle the POV is facing
|
|||
|
ae <- Pointer to the interface structure
|
|||
|
|
|||
|
What this function does is determine if the POV is close enough to either
|
|||
|
a normal or secret door and set the appropriate information to begin opening
|
|||
|
the door. This information is kept in the Door[] array of the interface
|
|||
|
structure and is then used by the ACK engine during subsequent builds. After
|
|||
|
the door has been triggered to open, the process is automatic (unless the
|
|||
|
application manipulates the Door array, which it can if need be) until the
|
|||
|
door goes back to fully closed.
|
|||
|
|
|||
|
|
|||
|
(17) Sound and ACK-3D:
|
|||
|
|
|||
|
The ACK engine is being released with a public domain program from Mystic
|
|||
|
software that allows playing of SoundBlaster .CMF and .VOC files. In addition
|
|||
|
the files with .PWM extensions are capable of playing through the PC speaker.
|
|||
|
|
|||
|
Since the sound features are public domain there are some limitations that
|
|||
|
are imposed by Mystic Software. The first is that a Terminate and Stay Resident
|
|||
|
program (TSR) must be loaded before using the sound routines. The program is
|
|||
|
included with the ACK demo and is called WORXLITE.EXE. This TSR need be loaded
|
|||
|
only once when first running the demo program.
|
|||
|
|
|||
|
The second limitation is how the sound is used. Sound files consume alot
|
|||
|
of memory so don't try to load several of them or large ones, they won't fit.
|
|||
|
There also appears to be some problems with the sound routines that need to
|
|||
|
be watched out for. On some machines it appears that playing background music
|
|||
|
with the .CMF files will occasionally lock up the machine and require a hard
|
|||
|
reboot to clear it. Playing the sound effect (.VOC files) alone seems to work
|
|||
|
fine. The other problem is that .VOC files cannot be loaded after background
|
|||
|
music is started playing, this forces all VOC files to be loaded up front which
|
|||
|
contributes to the memory overhead mentioned earlier.
|
|||
|
|
|||
|
But, the sound routines are provided to allow you to experiment with them
|
|||
|
in the 3D engine without having to write your own. The full fledged WORX
|
|||
|
library can be purchased from Mystic Software if you wish to use it in a full
|
|||
|
fledged shareware or commercial application.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
(18) Function Summary:
|
|||
|
|
|||
|
Here is a list of the available ACK-3D functions and what they do.
|
|||
|
|
|||
|
------------------------------------------------------------------------------
|
|||
|
int AckInitialize(ACKENG *ae);
|
|||
|
where:
|
|||
|
ae <- Pointer to interface structure
|
|||
|
|
|||
|
Purpose:
|
|||
|
Initializes the various arrays used by the ACK engine.
|
|||
|
Reads and processes the file "TRIG.DAT".
|
|||
|
Reads and processes the Map file supplied in the call.
|
|||
|
Allocates 64000 bytes for the ScreenBuffer.
|
|||
|
Calculates viewport items based on initial dimensions setup by the
|
|||
|
application.
|
|||
|
|
|||
|
Returns:
|
|||
|
0 if successful
|
|||
|
One of the error codes listed in ACK3D.H
|
|||
|
|
|||
|
Notes:
|
|||
|
This function must be called before any other ACK function.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
------------------------------------------------------------------------------
|
|||
|
int AckReadMapFile(ACKENG *ae,char *MapFileName);
|
|||
|
where:
|
|||
|
ae <- Pointer to interface structure
|
|||
|
MapFileName <- Name of map/object file to read
|
|||
|
|
|||
|
Purpose:
|
|||
|
Reads and processes the ACK binary map file.
|
|||
|
|
|||
|
Returns:
|
|||
|
0 if successful
|
|||
|
One of the error codes listed in ACK3D.H
|
|||
|
|
|||
|
Notes:
|
|||
|
Upon return the xGrid and yGrid arrays will be filled in with the
|
|||
|
wall bitmap numbers. The application is then free to change these if it
|
|||
|
wishes some walls to be different.
|
|||
|
|
|||
|
|
|||
|
------------------------------------------------------------------------------
|
|||
|
int AckLoadBitmap(ACKENG *ae,int BitmapNumber,int BitmapType,char *bmFileName);
|
|||
|
where:
|
|||
|
ae <- Pointer to interface structure
|
|||
|
BitmapNumber <- Index number into bitmap array
|
|||
|
BitmapType <- Either TYPE_WALL or TYPE_OBJECT
|
|||
|
bmFileName <- Name of bitmap file to read
|
|||
|
|
|||
|
Purpose:
|
|||
|
Reads in either raw (IMG) or DPII (BBM) bitmap file and processes it
|
|||
|
into the form that ACK-3D requires.
|
|||
|
Allocates the 4K of memory for the bitmap and stores the pointer in
|
|||
|
either bMaps[] or oMaps[] array based on BitmapType.
|
|||
|
|
|||
|
Returns:
|
|||
|
0 if successful
|
|||
|
One of the error codes listed in ACK3D.H
|
|||
|
|
|||
|
Notes:
|
|||
|
This function now uses XMS (Extended Memory) when possible to load
|
|||
|
bitmaps.
|
|||
|
|
|||
|
|
|||
|
------------------------------------------------------------------------------
|
|||
|
int AckLoadWall(ACKENG *ae,int WallNumber,char *bmFileName);
|
|||
|
where:
|
|||
|
ae <- Pointer to interface structure
|
|||
|
WallNumber <- Index number into bitmap array
|
|||
|
bmFileName <- Name of bitmap file to read
|
|||
|
|
|||
|
Purpose:
|
|||
|
Calls AckLoadBitmap() function with TYPE_WALL BitmapType set.
|
|||
|
|
|||
|
Returns:
|
|||
|
0 if successful
|
|||
|
One of the error codes listed in ACK3D.H
|
|||
|
|
|||
|
Notes:
|
|||
|
This function now uses XMS (Extended Memory) when possible to load
|
|||
|
bitmaps.
|
|||
|
|
|||
|
|
|||
|
------------------------------------------------------------------------------
|
|||
|
int AckLoadObject(ACKENG *ae,int BmpNumber,char *bmFileName);
|
|||
|
where:
|
|||
|
ae <- Pointer to interface structure
|
|||
|
BmpNumber <- Index number into bitmap array
|
|||
|
bmFileName <- Name of bitmap file to read
|
|||
|
|
|||
|
Purpose:
|
|||
|
Calls AckLoadBitmap() function with TYPE_OBJECT BitmapType set.
|
|||
|
|
|||
|
Returns:
|
|||
|
0 if successful
|
|||
|
One of the error codes listed in ACK3D.H
|
|||
|
|
|||
|
Notes:
|
|||
|
This function now uses XMS (Extended Memory) when possible to load
|
|||
|
bitmaps.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
------------------------------------------------------------------------------
|
|||
|
int AckCreateObject(ACKENG *ae,int ObjNumber,int NumBitmaps,UCHAR *bmNums);
|
|||
|
where:
|
|||
|
ae <- Pointer to interface structure
|
|||
|
ObjNumber <- Index number into ObjList array (Different than bitmap
|
|||
|
number!).
|
|||
|
NumBitmaps <- Number of bitmap indexes contained in bmNums.
|
|||
|
bmNums <- List of bitmap numbers associated with this object.
|
|||
|
|
|||
|
Purpose:
|
|||
|
Sets up ObjList structure with information regarding the object.
|
|||
|
Calculates the number of sides to the object if multiple bitmaps are
|
|||
|
specified.
|
|||
|
|
|||
|
Returns:
|
|||
|
0 if successful
|
|||
|
One of the error codes listed in ACK3D.H
|
|||
|
|
|||
|
Notes:
|
|||
|
None
|
|||
|
|
|||
|
|
|||
|
------------------------------------------------------------------------------
|
|||
|
int AckCreateOverlay(ACKENG *ae, UCHAR far *OverlayScreen);
|
|||
|
where:
|
|||
|
ae <- Pointer to interface structure
|
|||
|
OverlayScreen <- Pointer to 64k screen image to use.
|
|||
|
|
|||
|
Purpose:
|
|||
|
Determines which part of the screen is within the viewport and compiles
|
|||
|
this area for use by the AckDisplayScreen() function. The compiled
|
|||
|
commands are placed in the pointer OverlayBuffer in the interface
|
|||
|
structure.
|
|||
|
|
|||
|
Returns:
|
|||
|
0 if successful
|
|||
|
One of the error codes listed in ACK3D.H
|
|||
|
|
|||
|
Notes:
|
|||
|
This function process the overlay screen passed and builds a compiled
|
|||
|
overlay buffer (which is allocated) and returned in ae->OverlayBuffer.
|
|||
|
|
|||
|
|
|||
|
------------------------------------------------------------------------------
|
|||
|
int AckBuildBackground(ACKENG *ae);
|
|||
|
where:
|
|||
|
ae <- Pointer to interface structure
|
|||
|
|
|||
|
Purpose:
|
|||
|
Builds a static floor and ceiling background image based on the values
|
|||
|
of TopColor, BottomColor and LightFlag in the interface structure. The
|
|||
|
resulting image is pointed to by BkgdBuffer in the interface structure.
|
|||
|
|
|||
|
Returns:
|
|||
|
0 always
|
|||
|
|
|||
|
Notes:
|
|||
|
This function builds a psuedo-shaded ceiling and floor picture that is
|
|||
|
used for the background of the screen. The application can override this
|
|||
|
and have its own background if desired.
|
|||
|
|
|||
|
|
|||
|
------------------------------------------------------------------------------
|
|||
|
int AckBuildView(ACKENG *ae);
|
|||
|
where:
|
|||
|
ae <- Pointer to interface structure
|
|||
|
|
|||
|
Purpose:
|
|||
|
Constructs the current POV and places the result into ScreenBuffer
|
|||
|
pointer in the interface structure. No displaying is done to the
|
|||
|
screen at this time, nor has the optional overlay buffer been processed
|
|||
|
by this function.
|
|||
|
|
|||
|
Returns:
|
|||
|
0 always
|
|||
|
|
|||
|
Notes:
|
|||
|
The best place to put this function is in the applications main loop
|
|||
|
so it is repeatedly called whenever the POV moves or objects move or
|
|||
|
animate.
|
|||
|
|
|||
|
|
|||
|
------------------------------------------------------------------------------
|
|||
|
void AckDrawOverlay(UCHAR far *Screen,UCHAR far *Overlay);
|
|||
|
where:
|
|||
|
Screen <- Buffer to draw overlay buffer into
|
|||
|
Overlay <- Pointer to compiled overlay image
|
|||
|
|
|||
|
Purpose:
|
|||
|
Processes the compiled overlay image and draws into the screen buffer.
|
|||
|
|
|||
|
Notes:
|
|||
|
This function should be called before AckDisplayScreen (or the
|
|||
|
applications own display function) if an overlay is used.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
------------------------------------------------------------------------------
|
|||
|
int AckDisplayScreen(ACKENG *ae);
|
|||
|
where:
|
|||
|
ae <- Pointer to interface structure
|
|||
|
|
|||
|
Purpose:
|
|||
|
Display the last built POV onto the screen in normal VGA mode 13h.
|
|||
|
|
|||
|
|
|||
|
Returns:
|
|||
|
0 always
|
|||
|
|
|||
|
Notes:
|
|||
|
If the application wishes to use an overlay it must call the function,
|
|||
|
AckDrawOverlay() before calling AckDisplayScreen().
|
|||
|
|
|||
|
|
|||
|
------------------------------------------------------------------------------
|
|||
|
void AckCheckObjectMovement(ACKENG *ae);
|
|||
|
where:
|
|||
|
ae <- Pointer to interface structure
|
|||
|
|
|||
|
Purpose:
|
|||
|
Scans the ObjList sub-structure of the interface structure to determine
|
|||
|
if any objects need to be updated. This function should be called
|
|||
|
before the AckBuildView() function.
|
|||
|
|
|||
|
Returns:
|
|||
|
Nothing
|
|||
|
|
|||
|
Notes:
|
|||
|
This routine is mainly good for animating objects that have multiple
|
|||
|
bitmaps. The application should devise its own movement algorithms and
|
|||
|
call AckMoveObjectPOV() to carry them out.
|
|||
|
|
|||
|
|
|||
|
------------------------------------------------------------------------------
|
|||
|
int AckMovePOV(ACKENG *ae,int Angle,int Amount);
|
|||
|
where:
|
|||
|
ae <- Pointer to interface structure
|
|||
|
Angle <- Angle to move POV
|
|||
|
Amount <- Amount to move POV
|
|||
|
|
|||
|
Purpose:
|
|||
|
Determines if the move is valid and then sets the new coordinates into
|
|||
|
xPlayer and yPlayer of the interface structure.
|
|||
|
|
|||
|
Returns:
|
|||
|
0 if successful
|
|||
|
1 if X wall was hit
|
|||
|
2 if Y wall was hit
|
|||
|
3 if an object was hit
|
|||
|
|
|||
|
Notes:
|
|||
|
None
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
------------------------------------------------------------------------------
|
|||
|
int AckCheckDoorOpen(int xPlayer,int yPlayer,int PlayerAngle,ACKENG *ae);
|
|||
|
where:
|
|||
|
xPlayer <- Current x coordinate of the POV
|
|||
|
yPlayer <- Current y coordinate of the POV
|
|||
|
PlayerAngle <- Current angle POV is facing
|
|||
|
ae <- Pointer to interface structure
|
|||
|
|
|||
|
Purpose:
|
|||
|
Determines if the POV is close enough to trigger a door open. If so,
|
|||
|
the door is placed in the Door sub-structure of the interface structure
|
|||
|
and the opening process is begun. Subsequent calls to AckBuildView()
|
|||
|
will automatically continue the open and close process.
|
|||
|
|
|||
|
Returns:
|
|||
|
0 if no door was opened
|
|||
|
1 if X door was opened
|
|||
|
2 if Y door was opened
|
|||
|
3 if X secret door was opened
|
|||
|
4 if Y secret door was opened
|
|||
|
|
|||
|
Notes:
|
|||
|
None
|
|||
|
|
|||
|
|
|||
|
|
|||
|
------------------------------------------------------------------------------
|
|||
|
int AckCheckHit(int xPlayer,int yPlayer,int ViewAngle,ACKENG *ae);
|
|||
|
where:
|
|||
|
xPlayer <- Current x coordinate of the POV
|
|||
|
yPlayer <- Current y coordinate of the POV
|
|||
|
ViewAngle <- Current angle POV is facing
|
|||
|
ae <- Pointer to interface structure
|
|||
|
|
|||
|
Purpose:
|
|||
|
Allows the application to determine if an obstacle is close to the POV.
|
|||
|
|
|||
|
Returns:
|
|||
|
0 if nothing nearby
|
|||
|
1 if X wall is close
|
|||
|
2 if Y wall is close
|
|||
|
|
|||
|
Notes:
|
|||
|
This function is called by AckMovePOV() and checks for collisions with
|
|||
|
walls (not objects).
|
|||
|
|
|||
|
------------------------------------------------------------------------------
|
|||
|
int AckGetObjectHit(void);
|
|||
|
|
|||
|
Purpose:
|
|||
|
Allows the application to determine the object the POV last came in
|
|||
|
contact with.
|
|||
|
|
|||
|
Returns:
|
|||
|
Returns the object index of the last object the POV hit.
|
|||
|
|
|||
|
Notes:
|
|||
|
None
|
|||
|
|
|||
|
|
|||
|
|
|||
|
------------------------------------------------------------------------------
|
|||
|
int AckDeleteObject(ACKENG *ae,int ObjectIndex);
|
|||
|
where:
|
|||
|
ae <- Pointer to interface structure
|
|||
|
ObjectIndex <- Index number of object to delete
|
|||
|
|
|||
|
Purpose:
|
|||
|
Essentially sets the objects Active flag to 0 so the object is no longer
|
|||
|
checked by the engine. Good idea to call this function for future
|
|||
|
versions which may need to do more processing.
|
|||
|
|
|||
|
Returns:
|
|||
|
-1 if object already inactive
|
|||
|
0 if object was deleted from map
|
|||
|
|
|||
|
Notes:
|
|||
|
None
|
|||
|
|
|||
|
|
|||
|
------------------------------------------------------------------------------
|
|||
|
void AckSetVGAmode(void);
|
|||
|
|
|||
|
Purpose:
|
|||
|
Places the screen into standard 320x200 VGA mode 13h.
|
|||
|
|
|||
|
Returns:
|
|||
|
Nothing
|
|||
|
|
|||
|
Notes:
|
|||
|
This is a utility function. The application can set the screen to
|
|||
|
graphics using its own routines if it so desires, the ACK engine does not
|
|||
|
depend on mode 13h unless the AckDisplayScreen() function is called.
|
|||
|
|
|||
|
|
|||
|
------------------------------------------------------------------------------
|
|||
|
int AckLoadAndSetPalette(char *FileName);
|
|||
|
where:
|
|||
|
FileName <- Name of palette file to load
|
|||
|
|
|||
|
Purpose:
|
|||
|
Reads the specified palette file then sets the palette of the VGA.
|
|||
|
|
|||
|
Returns:
|
|||
|
0 if successful
|
|||
|
One of the error codes listed in ACK3D.H
|
|||
|
|
|||
|
Notes:
|
|||
|
This is a utility function to read a palette file (768 bytes) and
|
|||
|
set the video palette to the contents of the file. The application can
|
|||
|
use it's own function if desired or can also use AckSetPalette() with
|
|||
|
a buffer if needed.
|
|||
|
|
|||
|
|
|||
|
------------------------------------------------------------------------------
|
|||
|
void AckSetPalette(UCHAR far *PalBuffer);
|
|||
|
where:
|
|||
|
PalBuffer <- 768 byte buffer containing palette information
|
|||
|
|
|||
|
Purpose:
|
|||
|
Sets the contents of the PalBuffer into the video palette.
|
|||
|
|
|||
|
Returns:
|
|||
|
Nothing
|
|||
|
|
|||
|
Notes:
|
|||
|
Use this function to set a palette that has already been read into
|
|||
|
a buffer. Use AckLoadAndSetPalette() to read AND set a palette from a
|
|||
|
file. This is a utility function.
|
|||
|
|
|||
|
|
|||
|
------------------------------------------------------------------------------
|
|||
|
void AckFadeIn(int Begin,int Count,UCHAR far *Palette);
|
|||
|
where:
|
|||
|
Begin <- Starting color index to fade in
|
|||
|
Count <- Number of sequential color indexes to fade
|
|||
|
Palette <- Buffer containing palette information to use
|
|||
|
|
|||
|
Purpose:
|
|||
|
Slowly fades the screen from black to the contents of the Palette
|
|||
|
buffer passed.
|
|||
|
|
|||
|
Notes:
|
|||
|
This is a utility function.
|
|||
|
|
|||
|
|
|||
|
------------------------------------------------------------------------------
|
|||
|
void AckFadeOut(int Begin,int Count);
|
|||
|
where:
|
|||
|
Begin <- Starting color index to fade in
|
|||
|
Count <- Number of sequential color indexes to fade
|
|||
|
|
|||
|
Purpose:
|
|||
|
This function slowly fades the screen from the current palette to black.
|
|||
|
|
|||
|
Notes:
|
|||
|
This is a utility function.
|
|||
|
|
|||
|
|
|||
|
------------------------------------------------------------------------------
|
|||
|
void AckSetTextmode(void);
|
|||
|
|
|||
|
Purpose:
|
|||
|
Places the screen into normal 80x25 text color mode 3.
|
|||
|
|
|||
|
Returns:
|
|||
|
Nothing
|
|||
|
|
|||
|
Notes:
|
|||
|
This is a utility function.
|
|||
|
|
|||
|
|
|||
|
------------------------------------------------------------------------------
|
|||
|
UCHAR far *AckReadiff(char *FileName);
|
|||
|
where:
|
|||
|
FileName <- Name of .LBM or .BBM file to read
|
|||
|
|
|||
|
Purpose:
|
|||
|
Reads in a Deluxe Paint picture (.LBM) or brush (.BBM) file, allocates
|
|||
|
a buffer for the images and returns the buffer pointer to the caller.
|
|||
|
The image in the buffer will contain 4 bytes at the beginning with the
|
|||
|
width and height of the image in integer format.
|
|||
|
This function is provided if the application wishes to read its own
|
|||
|
images and is also needed if the overlay image is Deluxe Paint format.
|
|||
|
|
|||
|
Returns:
|
|||
|
Pointer to buffer if successful
|
|||
|
NULL if an error reading the image
|
|||
|
|
|||
|
Notes:
|
|||
|
None
|
|||
|
|
|||
|
|
|||
|
------------------------------------------------------------------------------
|
|||
|
int AckWrapUp(ACKENG *ae);
|
|||
|
where:
|
|||
|
ae <- Pointer to interface structure
|
|||
|
|
|||
|
Purpose:
|
|||
|
Frees up memory buffers used by the ACK engine.
|
|||
|
|
|||
|
Returns:
|
|||
|
0 always
|
|||
|
|
|||
|
Notes:
|
|||
|
This function MUST be called before exiting the application so that
|
|||
|
XMS memory is returned to the system.
|
|||
|
|
|||
|
|
|||
|
------------------------------------------------------------------------------
|
|||
|
int AckSoundInitialize(int DefaultSoundDevice);
|
|||
|
where:
|
|||
|
DefaultSoundDevice <- One of the DEV_ values in ACKSND.H
|
|||
|
|
|||
|
Purpose:
|
|||
|
Starts up WORX routines and determines hardware
|
|||
|
|
|||
|
Returns:
|
|||
|
0 if okay
|
|||
|
-1 if error initializing
|
|||
|
|
|||
|
Notes:
|
|||
|
This function MUST be called before the other sound routines are used.
|
|||
|
Calling with DEV_NOSOUND will force all sound off.
|
|||
|
Calling with DEV_PCSPEAKER will force sound through the speaker even
|
|||
|
if a sound card is present.
|
|||
|
|
|||
|
|
|||
|
------------------------------------------------------------------------------
|
|||
|
int AckPlayBackground(char *MusicFileName);
|
|||
|
where:
|
|||
|
MusicFileName <- Name of .CMF file to begin playing in background.
|
|||
|
|
|||
|
Purpose:
|
|||
|
Continuously plays the music file in the background.
|
|||
|
|
|||
|
Returns:
|
|||
|
0 if okay
|
|||
|
-1 if error loading file
|
|||
|
|
|||
|
Notes:
|
|||
|
See text in section Sound and ACK-3D for limitations with music.
|
|||
|
|
|||
|
|
|||
|
------------------------------------------------------------------------------
|
|||
|
int AckLoadSound(int VocIndex,char *VocFileName);
|
|||
|
where:
|
|||
|
VocIndex <- One of the SOUND_ defines in ACKSND.H
|
|||
|
VocFileName <- Name of .VOC file to load.
|
|||
|
|
|||
|
Purpose:
|
|||
|
Loads up a sound file for later playing.
|
|||
|
|
|||
|
Returns:
|
|||
|
0 if okay
|
|||
|
-1 if error loading sound
|
|||
|
|
|||
|
Notes:
|
|||
|
This function will load the .VOC file if a SoundBlaster or Adlib card
|
|||
|
is present. If the PC speaker is specified then the routine will look for
|
|||
|
.PWM files (created with the VOC2PWM.EXE program). The application should
|
|||
|
always call with the .VOC extent.
|
|||
|
|
|||
|
|
|||
|
------------------------------------------------------------------------------
|
|||
|
void AckPlaySound(int SoundIndex);
|
|||
|
where:
|
|||
|
SoundIndex <- One of SOUND_ indexes from ACKSND.H
|
|||
|
|
|||
|
Purpose:
|
|||
|
Plays the specified sound effect (.VOC file)
|
|||
|
|
|||
|
Returns:
|
|||
|
Nothing
|
|||
|
|
|||
|
Notes:
|
|||
|
Sound file must have been previously loaded with AckLoadSound().
|
|||
|
|
|||
|
|
|||
|
------------------------------------------------------------------------------
|
|||
|
void AckStopBackground(void);
|
|||
|
where:
|
|||
|
Nothing
|
|||
|
|
|||
|
Purpose:
|
|||
|
Stops the current background music file (.CMF)
|
|||
|
|
|||
|
Returns:
|
|||
|
Nothing
|
|||
|
|
|||
|
Notes:
|
|||
|
None
|
|||
|
|
|||
|
|
|||
|
------------------------------------------------------------------------------
|
|||
|
void AckSoundShutdown(void);
|
|||
|
where:
|
|||
|
Nothing
|
|||
|
|
|||
|
Purpose:
|
|||
|
Closes down the WORX routines, freeing memory, etc.
|
|||
|
|
|||
|
Returns:
|
|||
|
Nothing
|
|||
|
|
|||
|
Notes:
|
|||
|
This routine MUST be called before exiting the application if sound is
|
|||
|
being used in the ACK engine.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
(19) Where to Begin: (EXAMPLES)
|
|||
|
|
|||
|
Okay, now that we've covered some of the details, let's look at how an
|
|||
|
application begins to use the ACK engine. The first thing to do is to make
|
|||
|
some decisions, such as (and these may not be related to each other);
|
|||
|
|
|||
|
1. Will the interface structure be in global memory, or allocated memory?
|
|||
|
|
|||
|
2. What size of viewport will be used?
|
|||
|
|
|||
|
3. Will light shading be used? Will the ceiling and floor be shaded?
|
|||
|
|
|||
|
4. Are objects going to be manipulated by the ACK engine, or the app?
|
|||
|
|
|||
|
5. Will there be an overlay screen?
|
|||
|
|
|||
|
|
|||
|
Why is question 1 important? The interface structure is not small, just to
|
|||
|
begin with there are four 8k arrays within it to hold the map data, as well as
|
|||
|
object and door arrays, so if you plan on having alot of global data in the
|
|||
|
application then the interface structure should be allocated from the far heap.
|
|||
|
It doesn't really matter to the engine since a pointer to the structure is
|
|||
|
passed in the library calls as far data.
|
|||
|
|
|||
|
Let's say for our example that we'll allocate the structure and hold it in
|
|||
|
a global data pointer within the application. We could begin with something
|
|||
|
like;
|
|||
|
|
|||
|
#include "ack3d.h"
|
|||
|
|
|||
|
ACKENG *ae; /* This is our global pointer */
|
|||
|
|
|||
|
/* This could be a routine that the application uses to initialize things */
|
|||
|
int AppInitialize(void)
|
|||
|
{
|
|||
|
int result = 0;
|
|||
|
|
|||
|
ae = malloc(sizeof(ACKENG)); /* We first get memory for the structure */
|
|||
|
|
|||
|
if (ae == NULL) /* Whoops, we didn't get the memory */
|
|||
|
return(-1); /* So return an error */
|
|||
|
|
|||
|
memset(ae,0,sizeof(ACKENG)); /* Now we clear out the entire structure */
|
|||
|
|
|||
|
/* Perform other initialization here */
|
|||
|
|
|||
|
return(result);
|
|||
|
}
|
|||
|
|
|||
|
Snippet 1
|
|||
|
|
|||
|
|
|||
|
This code snippet does nothing more than allocate and clear the interface
|
|||
|
structure that will be used by the application and the ACK engine. Now we need
|
|||
|
to setup the size of the viewport and initialize the engine;
|
|||
|
|
|||
|
#include "ack3d.h"
|
|||
|
|
|||
|
#define VIEW_X 80 /* Size of the 3D viewport */
|
|||
|
#define VIEW_X1 240
|
|||
|
#define VIEW_Y 40
|
|||
|
#define VIEW_Y1 160
|
|||
|
|
|||
|
|
|||
|
char *MapFileName = "LEVEL1.MAP";
|
|||
|
|
|||
|
|
|||
|
int AppSetupEngine(void)
|
|||
|
{
|
|||
|
int result;
|
|||
|
|
|||
|
ae->WinStartX = VIEW_X;
|
|||
|
ae->WinStartY = VIEW_Y; /* Plug in the size we want */
|
|||
|
ae->WinEndX = VIEW_X1; /* for our viewport */
|
|||
|
ae->WinEndY = VIEW_Y1;
|
|||
|
|
|||
|
result = AckInitialize(ae); /* Then initialize the engine! */
|
|||
|
|
|||
|
return(result); /* 0 if no error, else errorcode */
|
|||
|
}
|
|||
|
|
|||
|
Snippet 2
|
|||
|
|
|||
|
|
|||
|
Code snippet 2 sets up an arbitrary viewport size (I usually load a picture
|
|||
|
into Deluxe Paint and write down the coordinates of where I want the 3D walls
|
|||
|
to be), and then calls the engine to intialize. Upon return the value of result
|
|||
|
will either be zero, meaning no problems, or one of the ERR_ codes listed in
|
|||
|
the ACK3D.H header file. If an error occurs the application should NOT continue,
|
|||
|
since unpredictable results WILL occur.
|
|||
|
|
|||
|
At this point the engine has filled in quite a bit of the ACKENG structure
|
|||
|
with information about the viewport and the map file that was read in and
|
|||
|
processed. A buffer is automatically allocated for the ScreenBuffer pointer in
|
|||
|
the structure and the map arrays now contain the necessary wall and object
|
|||
|
information to build the POV. But, we can't do that just yet. First we have
|
|||
|
to decide on a couple more things. Lets take code snippet 2 and expand it;
|
|||
|
|
|||
|
|
|||
|
#include "ack3d.h"
|
|||
|
|
|||
|
#define VIEW_X 80 /* Size of the 3D viewport */
|
|||
|
#define VIEW_X1 240
|
|||
|
#define VIEW_Y 40
|
|||
|
#define VIEW_Y1 160
|
|||
|
|
|||
|
#define CEILING_COLOR 23
|
|||
|
#define FLOOR_COLOR 27
|
|||
|
|
|||
|
#define DOORSPEED 4 /* Doors need a speed to open/close */
|
|||
|
|
|||
|
#define PLAYER_X 390 /* Initial X coordinate */
|
|||
|
#define PLAYER_Y 260 /* Initial Y coordinate */
|
|||
|
#define PLAYER_ANGLE 480 /* Initial POV angle */
|
|||
|
|
|||
|
|
|||
|
char *MapFileName = "LEVEL1.MAP";
|
|||
|
|
|||
|
|
|||
|
int AppSetupEngine(void)
|
|||
|
{
|
|||
|
int result;
|
|||
|
|
|||
|
ae->WinStartX = VIEW_X;
|
|||
|
ae->WinStartY = VIEW_Y; /* Plug in the size we want */
|
|||
|
ae->WinEndX = VIEW_X1; /* for our viewport */
|
|||
|
ae->WinEndY = VIEW_Y1;
|
|||
|
|
|||
|
|
|||
|
ae->xPlayer = PLAYER_X; /* Setup intial coordinates */
|
|||
|
ae->yPlayer = PLAYER_Y; /* for the POV */
|
|||
|
ae->PlayerAngle = PLAYER_ANGLE;
|
|||
|
ae->DoorSpeed = DOORSPEED; /* Set a default door speed */
|
|||
|
|
|||
|
result = AckInitialize(ae); /* Then initialize the engine! */
|
|||
|
|
|||
|
if (result)
|
|||
|
return(result); /* Error, so get out now */
|
|||
|
|
|||
|
result = AckReadMapFile(ae,MapFileName);
|
|||
|
if (result)
|
|||
|
return(result);
|
|||
|
|
|||
|
ae->TopColor = CEILING_COLOR; /* Setup our colors for the */
|
|||
|
ae->BottomColor = FLOOR_COLOR; /* background.... */
|
|||
|
|
|||
|
ae->LightFlag = SHADING_ON; /* Yes, we want light shading */
|
|||
|
result = AckBuildBackground(ae); /* Build the ceiling, floor */
|
|||
|
|
|||
|
if (result)
|
|||
|
return(result); /* Error, so get out now */
|
|||
|
|
|||
|
return(result); /* 0 if no error, else errorcode */
|
|||
|
}
|
|||
|
|
|||
|
Snippet 3
|
|||
|
|
|||
|
|
|||
|
|
|||
|
Code snippet 3 adds to snippet 2 by setting up the background ceiling and
|
|||
|
floor that the ACK engine requires. In the example above we decided to shade
|
|||
|
the background. By using SHADING_OFF (both found in ACK3D.H) we could also
|
|||
|
decide to have a solid background. Note the two defines for ceiling and floor
|
|||
|
color, these can be any color from 0 to 255, whatever is appropriate for the
|
|||
|
applications.
|
|||
|
|
|||
|
Also note that the initial coordinates and angle of the POV are setup here
|
|||
|
but do not need to be. These can be deferred until the call to AckBuildView()
|
|||
|
is made. They are done here merely for convienence.
|
|||
|
|
|||
|
|
|||
|
Okay, things are coming along nicely. Now we need to decide if an overlay
|
|||
|
is needed or not. Remember an overlay is only need if there will be some part
|
|||
|
of the full screen that will always display over the 3D walls. An example
|
|||
|
would be gothic pillars, or perhaps magic staffs that border the full screen
|
|||
|
display. Whatever the application needs to give the visual effect.
|
|||
|
|
|||
|
An overlay is not mandatory and does induce a slight speed degradation when
|
|||
|
displaying the engine. It also means the function AckDisplayScreen() must be
|
|||
|
called to actually use the overlay (unless the application has it's own
|
|||
|
routine to handle it).
|
|||
|
|
|||
|
Let's say we want an overlay, this is what we would do;
|
|||
|
|
|||
|
|
|||
|
#include "ack3d.h"
|
|||
|
|
|||
|
|
|||
|
char *PictureFile = "MYPIC.LBM";
|
|||
|
|
|||
|
|
|||
|
int AppSetupOverlay(void)
|
|||
|
{
|
|||
|
int result = 0;
|
|||
|
UCHAR far *OverlayPic;
|
|||
|
|
|||
|
OverlayPic = AckReadiff(PictureFile); /* Load a Deluxe Paint picture */
|
|||
|
|
|||
|
if (OverlayPic == NULL) /* Whoops, got a problem */
|
|||
|
return(-1); /* So return with an error */
|
|||
|
|
|||
|
|
|||
|
result = AckCreateOverlay(ae,&OverlayPic[4]); /* Compile the overlay */
|
|||
|
|
|||
|
free(OverlayPic); /* Free up the picture unless */
|
|||
|
/* we want to use it later */
|
|||
|
|
|||
|
return(result);
|
|||
|
}
|
|||
|
|
|||
|
Snippet 4
|
|||
|
|
|||
|
|
|||
|
This example reads in a Deluxe Paint LBM file and calls the ACK engine to
|
|||
|
compile it. The resulting overlay sequence will be in the interface structure
|
|||
|
pointed to by OverlayBuffer (unless an error occurs).
|
|||
|
|
|||
|
One change to this code snippet would be to keep the picture buffer around
|
|||
|
so it can be displayed on the screen. Note, only the portion which will cover
|
|||
|
the 3D viewport will be compiled, NOT the entire picture.
|
|||
|
|
|||
|
The application can use the supplied LBM read routine if it wishes, or read
|
|||
|
in the picture on its own, as long as the picture buffer passed to the routine
|
|||
|
is a flat 320 by 200 (64000 byte) image that the overlay section can be snipped
|
|||
|
out of.
|
|||
|
|
|||
|
The AckReadiff() routine places the width and height of the image in the
|
|||
|
first four bytes of the buffer, which is why &OverlayPic[4] was passed to
|
|||
|
the engine.
|
|||
|
|
|||
|
Again remember, the overlay is optional and only needs to be used if part
|
|||
|
of the screen is going to appear over the walls.
|
|||
|
|
|||
|
|
|||
|
By now we've setup quite a bit of the information we'll need to actually
|
|||
|
draw a 3D view of our map, but things will look pretty bad if we try to draw
|
|||
|
at this point, we don't have any bitmaps yet! Let's proceed with what is needed
|
|||
|
to get some walls into our application;
|
|||
|
|
|||
|
|
|||
|
#include "ack3d.h"
|
|||
|
|
|||
|
|
|||
|
typedef struct {
|
|||
|
int Number;
|
|||
|
int Type;
|
|||
|
char *Name;
|
|||
|
} BMTABLE;
|
|||
|
|
|||
|
|
|||
|
BMTABLE bmTable[] = {
|
|||
|
1 ,TYPE_WALL ,"swall1.bbm",
|
|||
|
2 ,TYPE_WALL ,"swall2.bbm",
|
|||
|
3 ,TYPE_WALL ,"swall3.bbm",
|
|||
|
4 ,TYPE_WALL ,"swall4.bbm",
|
|||
|
5 ,TYPE_WALL ,"swall5.bbm",
|
|||
|
58 ,TYPE_WALL ,"secret.bbm",
|
|||
|
59 ,TYPE_WALL ,"secret.bbm",
|
|||
|
60 ,TYPE_WALL ,"sdoor.bbm",
|
|||
|
61 ,TYPE_WALL ,"sside.bbm",
|
|||
|
62 ,TYPE_WALL ,"sdoor.bbm",
|
|||
|
1 ,TYPE_OBJECT ,"eyeball.bbm",
|
|||
|
2 ,TYPE_OBJECT ,"treasure.bbm",
|
|||
|
-1 ,-1 ,"" /* End of table */
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
int AppLoadBitmaps(void)
|
|||
|
{
|
|||
|
int result;
|
|||
|
int i = 0;
|
|||
|
|
|||
|
while (bmTable[i].Number != -1)
|
|||
|
{
|
|||
|
result = AckLoadBitmap(ae,
|
|||
|
bmTable[i].Number,
|
|||
|
bmTable[i].Type,
|
|||
|
bmTable[i].Name);
|
|||
|
|
|||
|
if (result) /* Error during load */
|
|||
|
break; /* so get out now */
|
|||
|
|
|||
|
i++; /* Next index in table */
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
return(result);
|
|||
|
}
|
|||
|
|
|||
|
Snippet 5
|
|||
|
|
|||
|
|
|||
|
What code snippet 5 does is loop through a table and load all the bitmaps
|
|||
|
for this example application (the bitmap names are arbitrary). Should an error
|
|||
|
occur the routine exits immediately and returns the error to the caller. Note
|
|||
|
also that this same routine can be used to load objects as well as walls, just
|
|||
|
use TYPE_OBJECT instead of TYPE_WALL (defined in ACK3D.H) for the objects.
|
|||
|
|
|||
|
Upon return from code snippet 5 we'll have all the bitmaps loaded that are
|
|||
|
needed to begin. If no objects are going to be used then we can proceed, but
|
|||
|
for example purposes let's say we have the two objects loaded in snippet 5 to
|
|||
|
setup. We do this by calling the function AckCreateObject() as in the following
|
|||
|
example;
|
|||
|
|
|||
|
|
|||
|
#include "ack3d.h"
|
|||
|
|
|||
|
|
|||
|
int AppSetupObjects(void)
|
|||
|
{
|
|||
|
int result;
|
|||
|
UCHAR BitmapNumbers[2];
|
|||
|
|
|||
|
ae->ObjList[1].Dir = 0; /* Direction doesn't matter */
|
|||
|
ae->ObjList[1].Speed = 0; /* is a stationary object */
|
|||
|
|
|||
|
BitmapNumbers[0] = 1; /* Bitmap to use with object (eyeball) */
|
|||
|
|
|||
|
result = AckCreateObject(ae,1,1,BitmapNumbers);
|
|||
|
|
|||
|
if (result) /* An error occurred */
|
|||
|
return(result); /* so get out now */
|
|||
|
|
|||
|
ae->ObjList[2].Dir = 0; /* Again a direction is irrelavent */
|
|||
|
ae->ObjList[2].Speed = 0; /* Because speed 0 is stationary */
|
|||
|
|
|||
|
BitmapNumbers[0] = 2; /* Bitmap to use (treasure) */
|
|||
|
|
|||
|
result = AckCreateObject(ae,2,1,BitmapNumbers);
|
|||
|
|
|||
|
return(result);
|
|||
|
}
|
|||
|
|
|||
|
Snippet 6
|
|||
|
|
|||
|
|
|||
|
Snippet 6 is a brute force method of creating the objects. A more elegant
|
|||
|
method would be to setup a table to create the objects, similiar to what we
|
|||
|
did in snippet 5 with the bitmaps.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
At this point we've got the engine initialized, a background buffer built,
|
|||
|
bitmaps loaded, and objects created. We're not even in graphics mode yet! The
|
|||
|
ACK engine provides some support functions that may be used (unless the
|
|||
|
application has its own), to setup graphics mode and later go back to text
|
|||
|
mode. These functions are;
|
|||
|
|
|||
|
|
|||
|
AckSetVGAmode(); <- Sets video into mode 13h (320x200 w/ 256 colors)
|
|||
|
|
|||
|
AckSetTextmode(); <- Sets video into mode 3 (80x25 16 color text)
|
|||
|
|
|||
|
|
|||
|
Use these if desired, they are just thrown in as support routines. Another
|
|||
|
routine can also be used, this one reads in a palette file and sets up the
|
|||
|
VGA palette registers;
|
|||
|
|
|||
|
|
|||
|
AckLoadAndSetPalette( FileName ); <- Reads a palette file and sets regs.
|
|||
|
|
|||
|
|
|||
|
This routine may be used AFTER the video is placed in graphics mode. Pass
|
|||
|
the name of a 768 byte palette file to use. Upon return the new palette will
|
|||
|
be set. Let's put these routines into a code snippet;
|
|||
|
|
|||
|
|
|||
|
#include "ack3d.h"
|
|||
|
|
|||
|
|
|||
|
char *PalFile = "DEMO.PAL";
|
|||
|
|
|||
|
|
|||
|
|
|||
|
int AppSetGraphics(void)
|
|||
|
{
|
|||
|
int result;
|
|||
|
|
|||
|
|
|||
|
AckSetVGAmode(); /* Go into graphics */
|
|||
|
|
|||
|
result = AckLoadAndSetPalette(PalFile);
|
|||
|
|
|||
|
return(result);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
Snippet 7
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
Okay, we've got the beginnings of a 3D game! As part of this introduction
|
|||
|
we need to be concerned with only a couple more things. Beyond that it becomes
|
|||
|
the applications responsibility to handle user interaction with the engine.
|
|||
|
|
|||
|
Once we're ready to begin displaying the POV on the screen we need to make
|
|||
|
one more mandatory call to the engine to tell it to build the current scene. The
|
|||
|
code snippet below shows this process (for now we'll assume the application is
|
|||
|
going to let the ACK engine perform the actual display);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
#include "ack3d.h"
|
|||
|
|
|||
|
|
|||
|
void AppShow3D(void)
|
|||
|
{
|
|||
|
|
|||
|
/* Any preprocessing the application wishes to do can go here */
|
|||
|
|
|||
|
AckBuildView(ae); /* Tell the ACK engine to construct the POV */
|
|||
|
|
|||
|
AckDisplayScreen(ae); /* Display the POV on the video screen */
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
Snippet 8
|
|||
|
|
|||
|
|
|||
|
|
|||
|
Remember that the initial coordinates of the POV were setup in code snippet
|
|||
|
number 3 above. They can easily be setup anytime before calling AckBuildView()
|
|||
|
if the application so decides.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
It's high time we put all these code snippets together into a running
|
|||
|
example program. This is shown below;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
#include "ack3d.h"
|
|||
|
|
|||
|
#define VIEW_X 80 /* Size of the 3D viewport */
|
|||
|
#define VIEW_X1 240
|
|||
|
#define VIEW_Y 40
|
|||
|
#define VIEW_Y1 160
|
|||
|
|
|||
|
#define CEILING_COLOR 23
|
|||
|
#define FLOOR_COLOR 27
|
|||
|
|
|||
|
|
|||
|
#define PLAYER_X 390 /* Initial X coordinate */
|
|||
|
#define PLAYER_Y 260 /* Initial Y coordinate */
|
|||
|
#define PLAYER_ANGLE 480 /* Initial POV angle */
|
|||
|
|
|||
|
|
|||
|
char *MapFileName = "LEVEL1.MAP";
|
|||
|
char *PictureFile = "MYPIC.LBM";
|
|||
|
char *PalFile = "DEMO.PAL";
|
|||
|
|
|||
|
typedef struct {
|
|||
|
int Number;
|
|||
|
int Type;
|
|||
|
char *Name;
|
|||
|
} BMTABLE;
|
|||
|
|
|||
|
|
|||
|
BMTABLE bmTable[] = {
|
|||
|
1 ,TYPE_WALL ,"swall1.bbm",
|
|||
|
2 ,TYPE_WALL ,"swall2.bbm",
|
|||
|
3 ,TYPE_WALL ,"swall3.bbm",
|
|||
|
4 ,TYPE_WALL ,"swall4.bbm",
|
|||
|
5 ,TYPE_WALL ,"swall5.bbm",
|
|||
|
58 ,TYPE_WALL ,"secret.bbm",
|
|||
|
59 ,TYPE_WALL ,"secret.bbm",
|
|||
|
60 ,TYPE_WALL ,"sdoor.bbm",
|
|||
|
61 ,TYPE_WALL ,"sside.bbm",
|
|||
|
62 ,TYPE_WALL ,"sdoor.bbm",
|
|||
|
1 ,TYPE_OBJECT ,"eyeball.bbm",
|
|||
|
2 ,TYPE_OBJECT ,"treasure.bbm",
|
|||
|
-1 ,-1 ,"" /* End of table */
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
/* Prototypes */
|
|||
|
int AppInitialize(void);
|
|||
|
int AppSetupEngine(void);
|
|||
|
int AppSetupOverlay(void);
|
|||
|
int AppLoadBitmaps(void);
|
|||
|
int AppSetupObjects(void);
|
|||
|
int AppSetGraphics(void);
|
|||
|
void AppShow3D(void);
|
|||
|
|
|||
|
|
|||
|
/* Entry point for application */
|
|||
|
|
|||
|
int main(void)
|
|||
|
{
|
|||
|
int result,done = 0;
|
|||
|
|
|||
|
result = AppInitialize();
|
|||
|
|
|||
|
if (result)
|
|||
|
{
|
|||
|
printf("Error initializing: ErrorCode = %d\n",result);
|
|||
|
return(1);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
result = AppSetupEngine();
|
|||
|
|
|||
|
if (result)
|
|||
|
{
|
|||
|
printf("Error setting up ACK engine: ErrorCode = %d\n",result);
|
|||
|
return(1);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
result = AppSetupOverlay();
|
|||
|
|
|||
|
if (result)
|
|||
|
{
|
|||
|
printf("Error loading overlay: ErrorCode = %d\n",result);
|
|||
|
return(1);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
result = AppLoadBitmaps();
|
|||
|
|
|||
|
if (result)
|
|||
|
{
|
|||
|
printf("Error loading bitmaps: ErrorCode = %d\n",result);
|
|||
|
return(1);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
result = AppSetupObjects();
|
|||
|
|
|||
|
if (result)
|
|||
|
{
|
|||
|
printf("Error creating objects: ErrorCode = %d\n",result);
|
|||
|
return(1);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
result = AppSetGraphics();
|
|||
|
|
|||
|
if (result)
|
|||
|
{
|
|||
|
AckSetTextmode();
|
|||
|
printf("Error loading palette: ErrorCode = %d\n",result);
|
|||
|
return(1);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
while (!done)
|
|||
|
{
|
|||
|
AppShow3D();
|
|||
|
|
|||
|
|
|||
|
if (getch() == 27) /* Check for the escape key */
|
|||
|
break;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
AckSetTextmode();
|
|||
|
|
|||
|
return(0);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
Example 3
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
(20) Closing comments:
|
|||
|
|
|||
|
And here I said at the start of this document that writing docs is too
|
|||
|
much like work! Anyway, I hope the information provided above, as well as the
|
|||
|
actual source to the ACK engine, is enough to get you started on your own
|
|||
|
3D adventure. It's been a very exciting project and I've met alot of nice folks
|
|||
|
who have pitched in and helped in many ways. I wish to express thanks to all
|
|||
|
of you who made this possible and advanced thanks to all of you who may use the
|
|||
|
engine to produce more games for all of us to enjoy.
|
|||
|
|
|||
|
Those who have been of GREAT help are;
|
|||
|
|
|||
|
Jaimi McEntire who helped a great deal with programming and graphics.
|
|||
|
Jaimi also did the neat space station picture in the title!
|
|||
|
|
|||
|
Ken Lemieux who provided the space dudes and shuttle!
|
|||
|
|
|||
|
Steve Salter who did ALOT of the wall and object graphics!
|
|||
|
|
|||
|
Frank Sachse who provided the sound routine interface and music!
|
|||
|
|
|||
|
Ron Sachse who provided some of the wall bitmaps!
|
|||
|
|
|||
|
Mark Betz who allowed me to use his fading routines!
|
|||
|
|
|||
|
Bart Stewart who built an image editor for this thing!
|
|||
|
|
|||
|
Michael Wilson who's XMS code was readily available and is working
|
|||
|
great!
|
|||
|
|
|||
|
Thanks guys! Without you the ACK engine and demo would not have been
|
|||
|
possible!
|
|||
|
|
|||
|
Now some bad news: There is currently a problem with displaying objects that
|
|||
|
sometimes causes "ghosts" images to be displayed. While this does not cause any
|
|||
|
damage to anything it is very annoying and I haven't been able to track down the
|
|||
|
cause. If anyone has any suggestions or ideas I'd appreciate hearing them and
|
|||
|
building an update for others. Thanks!
|
|||
|
|
|||
|
One last note: This will be my final installment of ACK3D for awhile. I'm
|
|||
|
not going to drop out of the picture totally but I've got some new things that
|
|||
|
I'm eager to try out! Maybe some more goodies will come out of it, one can
|
|||
|
never tell.
|
|||
|
|
|||
|
If you wish to reach me on CompuServe I'm usually hanging around the Game
|
|||
|
Design library of The Gamers forum, or you can email me direct. My CompuServe
|
|||
|
account is 72355,655.
|
|||
|
|
|||
|
Sincerely,
|
|||
|
Lary Myers
|
|||
|
|
|||
|
|
|||
|
|