Scorched Asteroids Project Summary


Game Overview

We are taking pieces of Scorched Earth and the most addictive part of Asteriods and merging them into a Win32 application. With real time play, multiple weapons, and networked opponents, we are hoping to create the most addictive final project with the smoothest sprite based graphics.

Scorched Earth is a side view, turn based game. Each 'tank' is allowed to take one shot at the other players on the team. After all the tanks are dead, each player gets an amount of money based on what kills they made. And in between each battle is a buying round with dozens of weapons and special items to buy.

Asteriods, the classic, is viewed from above and played in real time. The player's ship continuously moves, with a single booster and a single weapon. The goal of the game is to destroy the asteroids floating around. The only physics done is the acceleration from the booster.

Merging Scorched Earth and Asteroids: Play Modes

Scorched Asteroids will have a side view (Scorched Earth) and play in real time (like Asteroids). There will be multiple weapons (Scorched Earth) and will be played with momentum (Asteroids) and gravity (kind of like Scorched Earth) between objects in mind. To include the best elements of both games, we have broken the battles up into three different modes.
Earth Mode
Much like Scorched Earth, the earth mode will take place on the surface of a planet. Each ship will have an amiable turret and will be able to move horizontally. As with all the modes, multiple weapons and special items are allowed.
Moon Mode
This mode will be more like asteroids. It will take place in space, with a moon in the middle of the screen. This is the first and simplest of the 'space' modes. The direction of fire will be the direction of the ship (fixed turret). Maybe with multiple direction 'jets' for moving in different directions. Gravity will be in effect and will be toward the center of the moon.
Free Space Mode
This mode will be asteroids. Gravity will be in effect for only the largest asteroids. This is the most complex of the 'Space' modes.

Note On Summary

All of the procedures that have a basis in C are described using C prototypes. Since we are using MASM's PROTO and INVOKE to simplify C calls, we believe that it is more intuitive to describe functions this way than it is to use ASM declarations.

Data Types

HRESULT
A 32 bit number. Usually in index into a table of results specific for a function or group of functions. Always returned from a DD API call.
LPDDSurface
A pointer to a structure the OS provides as a kind of 'handle' to a buffer in video memory. Usually used to represent one screen full of information. Also commonly used to represent one buffered bitmap in video memory. If the system doesn't have enough video memory, then the buffer is placed in system memory. This structure is never directly accessed by programers. It is used as an entry point for all DD calls.
LPSysSurface
A pointer to a SysSurface.
MY_RGB
A device dependent type of color. Currently we are running in 16bit color (because that's all my computer at home can handle in 1024x768). So MY_RGB would be equivalent to an unsigned short (WORD). If we where in 8bit color, MY_RGB would be equivalent to an unsigned byte (BYTE). Our target color depth is 24bit color, so the final MY_RGB will be a 3-byte structure.

#if COLORDEPTH == 8
typdef unsigned char MY_RGB;
#elseif COLORDEPTH == 16
typedef unsigned short MY_RGB;
#elseif COLORDEPTH == 24
struct MY_RGB {
   unsigned char red;
   unsigned char green;
   unsigned char blue;
};

RECT
Win32 Structure of 4 32bit integers.

typedef struct _RECT {   // rc
   LONG left;
   LONG top;
   LONG right;
   LONG bottom;
} RECT;

SysSurface
This structure is not an OS or DD structure -- it is our own structure used as a pointer to an image buffer in system memory. We were experimenting to see if there were any speed advantages if we rendered graphics in system memory instead of video memory (using triple buffering).

struct SysSurface {
   LPVOID pSurface;
   LONG   width;
   LONG   height;
};

Point
This structure represents a point on the screen.

struct Point {
   int x;
   int y;
};

MenuItem
This structure is used in the menu routines. It represents a text item in a menu that can be selected.

struct MenuItem {
   char* itemName;
   int stringLen;
   Point;
};

MenuItem netMenuStrings[]
contains all the strings and their locations required to draw the Network Game menu. This is for the UI.
Object
A structure that encapsulates the various attributes of any game object, such as a ship or a weapon.

struct Object {
   objectType;
   int status;
   double
        xPos,
        yPos,
        yPos,
        xVel,
        yVel,
        dir,
        mass;
        int damage;
};

Object MasterTable[]
contains information about each object that is to be drawn to the screen.
CSocket Connections[]
This array holds all incoming connections to the server (i.e., clients). This array will be used to identify each of the clients and to send data to them.
int MAXCONNECTIONS
This variable simply limits the maximum number of client connections.

Introduction (Opening Sequence)

The introduction sequence for Scorched Asteroids will serve two important functions: (a) it will be the main entrypoint into the game and (b) it will acquaint the user with the various features of the game. The intro must also be attractive to make the game appeal to users. The introduction sequence will fade into a screen that displays our project group name. Then, we will display a visual "water" effect, and finally bring up the Main Menu.

Functions

void Fade(BOOL fadeIn)
Depending on the boolean flag fadeIn, this routine will fade into (increase the brightness of the screen) or out of (decrease the brightness of the screen) the screen.
void RenderWater()
The RenderWater routine will render a shimmering water surface onto the screen. This function will be used to provide an appealing visual effect.

Menuing (User Interface)

After the game's introduction is complete, the game will enter menu mode. Here, the players will be presented with various options including game mode, volume levels, and network play. The menu page will be implemented dynamically, so that additional options can be added easily if time permits.

Functions

void buildMenu(MenuItem menuContents[MAXMENUITEMS])
void drawString(char* string, Point p)
void drawSelectedString(char* string, Point p)
menuMode WinMain()

Video and Graphics

There are three main challenges to doing video in Win32. Initialization, acquiring and releasing the hardware, and lastly, learning to utilize the hardware.

The first part of initialization includes creating our 'window', a full screen exclusive mode Win32 application. This means that when we are active, we require the whole screen for ourselves. Next we need to claim the screen and an off screen buffer as our own, so we can use them to draw to. Lastly we need to load the images that are going to be needed for the play mode we are in. But our work is not done, we can lose our claim to the screen any time the user switches to another app. We will discuss in detail in the next section.

Because this is in Win32, which is a 32bit preemptive multitasking environment, we don't have exclusive access the video hardware. As a result, the graphics becomes much more complicated than it did in Dos-16 Assembly. Every API call can fail: imagine having to check to see if a write operation to the video buffer had failed, or a write operation to a port to change the palette had failed.

Any time the user switches to another application, the current video buffer is immediately overwritten. So there is a continuous process of testing and reacquiring any time a resource is lost.

Acquisition and release happens every time a write to the video buffer is necessary. Because almost every process on the computer shares the video screen, the OS is very careful about exclusive access. The Lock/Unlock pair that wraps every write to the video buffer is the basis for the screenLock() and screenUnlock() procedures.

Lastly, and most complicated is what every project needs: the graphics rendering code. Our 2D overhead view is simple. Because we are in a device independent environment, we do have a few quirks to worry about, but most of it is done just like it was in MP4. Writes to a buffer are followed by a call to flip the back buffer with the primary buffer. If we have time, we might add the ability to run in a window, which requires adding another buffer and a clipping rectangle (triple buffering).

HRESULT restoreAll()
Called during initialization and when a surface is dropped to restore the surface. This loads all the bitmaps used in the current play mode into the video buffer.
BOOL clearBackSurface()
BOOL clearBackSurfaceAsm()
These two functions clear the drawing surface.
HRESULT updateFrame()
Renders the current scene and flips the video buffers.
LPDDSurface loadSurface(const char * filename)
Creates a DD Surface, loads the image file pointed to by filename, and returns a pointer to the created surface.
HRESULT destroySurface(LPDDSurface surf)
Destroys a surface created by loadSurface. This is used to release video memory between switching of play modes.
HRESULT fillRect(RECT* pDrawRect, MY_RGB color)
Fills a rectangular piece of the draw buffer with the specified color.
HRESULT drawRect(RECT* pDrawRect, POINT* start, LPDDSurface source)
HRESULT drawRectSys(RECT* pDrawRect, POINT* start, LPSysSurface source)
Copies the rectangle DrawRect from the source surface to the back surface.

Networking Overview

The networking for the project is based on a simple client/server model. Networking will be implemented using TCP/IP sockets through the Win32 Winsock API. Initially, we plan to have a dedicated server that will maintain and update a "master copy" of the object table, MasterTable. This table will then periodically be broadcast to all the clients. If time permits, we will attempt to embed the server portion of the project into the client, so that there is only one binary a user needs to run.

There are two approaches we can take to implement networking. One method is to make the server responsible for not only updating the MasterTable, but also for calculating a sequence of frames to broadcast to each client. Using this approach, the client is simply responsible for drawing the screen based on the sequence of positions the server has told it to draw. However, there is a significant drawback with this approach: the network traffic will become intense, and the server would slow down immensely, causing jumpy and lagged gameplay.

The other approach is to have the server simply update positions based on the change in a client's velocity. This method would force the physics modeling to be done by the client of this. While this approach would be compute-intensive on the client, it would decrease network traffic and lallow for smoother gameplay.

Since, at this point, we cannot effectively calculate which approach is better, we propose to try both methods and choose the better of the two for the final release.

There are also multiple approaches to take when using the Winsock API. We can either use the C++-based Microsoft Foundation Classes to abstract low-level threading issues and Winsock calls, or we can use the straight, C-based Winsock calls. In order to first obtain a working version of the networking, we will use the MFCs. If time permits, we will port the code to work using the C-based calls.

Client Functions

void connectToServer(char* pszServName)
This function will connect to the server whose name is pszServName. If the host resolution succeeded, the player will be connected. Otherwise, the player will be notified that the connection failed and will be returned to the main menu.
void onReceivePacket()
This function will handle the updating of the MasterTable once the client has received a new one from the server.
void sendPacket()
This function will send a packet out over the network to the server and wait for the server's response.

Server Functions

void serverInit()
This function will initialize the Network Server.
void onReceiveConnection()
This function will handle an incoming connection. If there are already MAXCONNECTIONS clients connected, then the server will simply ignore any incoming connections; it will be the responsibility of the client to handle a server timeout.
void onReceivePacket()
This function will receive a packet from a client. It will then update the master copy of the MasterTable, and will broadcast this back to all connected clients.

Physics Modeling Overview

The kinematics of the objects on the screen is dependent on the level that the user selects. There are two separate levels that the user can choose from and below is a brief description of each:

Free Space

The setting for this level is such that the motion of the objects on the screen is not influenced by the gravity of stars or planets. Although the gravity of some objects does play a role in determining the trajectories, it is not as profound as in the terrestrial level (see below). Only objects with a certain minimum mass can affect the motion of the other objects on the screen. The positions and velocities of the active objects are updated using the following sequence of calculations:

Force:

The force acting on each object on the screen is calculated using Newton's universal law of gravitation as follows,

.

The number of objects whose mass is above the minimum threshold is denoted by 'n' and "current" indicates the object upon which the calculated force is acting. The summation is necessary so that all objects above the threshold mass can be taken into consideration for the calculation. And finally, the separation between the two objects in question is represented by 'r'.

Acceleration:

Using F = ma, we can calculate the acceleration of any particular object with mass m:

.

Velocity:

Since

,

we get

.

In order to obtain a numerical value for the velocity we assign an appropriate value to the infinitesimal time interval 'dt' (such as 1s or 0.01s) and assume the acceleration calculated above is constant during this interval. While the program is running, this interval is essentially the duration of the updatePosition procedure. Thus the new velocity of the current object is given by

.

Position:

 

Similarly, we get

.

And once again, we assume that the velocity calculated above is constant during this infinitesimal time interval. So we get .

NOTE: Keep in mind that a new value for acceleration is calculated during each call to updatePosition and that the duration of this call represents . The amount of error that creeps into the model can be minimized by making .

Level 2 (Terrestrial)

In this level, all objects are on a planetary surface and there is no gravitational interaction between these objects. As a result, the acceleration of all objects on the screen is the same - both in terms of direction (downward) and magnitude (9.8m/s). The equations are the same as above.

Procedures

updatePosition

Sound Overview

Functions