Author: James McCue
Author's e-mail: aloiterer@juno.com
Author's homepage: Jim the loiterer's PC games, programming, & stuff
First off, I guess I'll talk about building this project, because even though this is just a small project, a part of big projects is being able to send them (or parts of them) to other coders you may be working with.
Change the names of the bitmap resources to whichever BMP they are. (pssst! keep the quotes)
/////////////////////////////////////////////////////////////////////////////////////////////
// All the tile drawing stuff is done in here.
// This isn't the best algorithm for drawing tiles, because
// it's checking every dang tile to see if it needs to be clipped
// along the right hand side of the screen...
//
// but anyway, here goes...
void Game_Main(void)
{
// process your game logic and draw next frame
// right now, it's just a cheesy clipping tile engine!
static int px = SCREEN_WIDTH, // upperleft corner of screen
py = SCREEN_HEIGHT;
DWORD new_frame_time;
int movementfactor;
LPDIRECTDRAWSURFACE pdds; // an alias we'll use to the
// tile to be used in BltFast!
RECT rcRect; /// used for clipping tiles
HRESULT ddrval;
HDC hdc;
int start_left, start_top;
int start_right, start_bottom;
int start_x_cell, start_y_cell;
int render_it_x,render_it_y;
int clip_right, clip_bottom;
int which_cell,cell_x_save;
int num_x_tiles, num_y_tiles;
int x_offset, y_offset;
int orig_left_clip,orig_top_clip;
// record starting time of frame. I don't really use this
// but it could be used to let your current framerate dictate
// how much to move in response to keystrokes...
// It's a timing paradigm that WILL make for some choppy
// play on slow machines, but at the same time will have
// for example. 2 people who are running this, one on a
// P200 and one on a P100, who start it up at the same time,
// seeing the same image 30 seconds later... more or less...
new_frame_time = GetTickCount();
movementfactor = new_frame_time - last_frame_time;
// get input, note how keyboard is accessed with constants "VK_"
// these are defined in "winuser.h" are basically the scan codes
// for letters just use capital ASCII codes
if (KEY_DOWN(VK_ESCAPE))
PostMessage(main_window_handle,WM_CLOSE,0,0); // this is how you exit you game
// which way is player moving
if (KEY_DOWN(VK_RIGHT))
if ((px+=4)>(X_BOUND)) px=X_BOUND;
if (KEY_DOWN(VK_LEFT))
if ((px-=4)<SCREEN_WIDTH) px=SCREEN_WIDTH;
if (KEY_DOWN(VK_UP))
if ((py-=4)<SCREEN_HEIGHT) py=SCREEN_HEIGHT;
if (KEY_DOWN(VK_DOWN))
if ((py+=4)>=Y_BOUND) py=Y_BOUND-1;
// my muddleheaded setup for the tile engine
start_left = px-SCREEN_WIDTH;
start_top = py-SCREEN_HEIGHT;
start_x_cell = start_left / CELL_X_SIZE;
start_y_cell = start_top / CELL_Y_SIZE;
orig_left_clip = rcRect.left = start_left%CELL_X_SIZE;
orig_top_clip = rcRect.top = start_top%CELL_Y_SIZE;
start_right = (start_left+CELL_X_SIZE)%CELL_X_SIZE;
start_bottom = (start_top+CELL_Y_SIZE)%CELL_Y_SIZE;
if (start_right<=start_left)
rcRect.right = CELL_X_SIZE;
else
rcRect.right = start_right;
if (start_bottom<=start_top)
rcRect.bottom = CELL_Y_SIZE;
else
rcRect.bottom = start_bottom;
// get starting texture to draw with
cell_x_save = start_x_cell + (start_y_cell<<TILE_Y_NUM_SHIFT);
which_cell = cell_x_save;
x_offset = 0;
y_offset = 0;
num_x_tiles = ((SCREEN_WIDTH+start_left)/ CELL_X_SIZE) + 1;
num_y_tiles = ((SCREEN_HEIGHT+start_top)/ CELL_Y_SIZE) + 1;
// loop thru entire scene left to right, top to bottom
for (render_it_y = 0; render_it_y<num_y_tiles; render_it_y++)
{
x_offset = 0;
// get original start left clip for each x loop
rcRect.left = orig_left_clip;
// assume, for now, that we've got the whole tile
// to work with horizontally
rcRect.right = CELL_X_SIZE;
// draw tiles left to right
for (render_it_x = 0; render_it_x<num_x_tiles; render_it_x++)
{
pdds = lpddstextures[terrain_texture[which_cell]];
ddrval = lpddsback->BltFast( x_offset, y_offset, pdds, &rcRect, DDBLTFAST_WAIT );
// the following will only evaluate to a value other
// than 64 once for every row of tiles
x_offset+=CELL_X_SIZE-rcRect.left;
++which_cell;
rcRect.left = 0;
rcRect.right = CELL_X_SIZE;
// determine where to kick out of the loop
// or just clip the right hand side of the rect
clip_right = SCREEN_WIDTH - x_offset;
if ((clip_right)<0)
break;
if ((clip_right)<64)
if ((clip_right)==0)
{
++which_cell;
break;
}
else
rcRect.right = clip_right;
// sync time (optional)
} // end for render_it_x
// this will only evaluate to a value other than 64
// once...
y_offset+=CELL_Y_SIZE-rcRect.top;
cell_x_save += TILE_X_NUM;
which_cell = cell_x_save;
rcRect.top = 0;
rcRect.bottom = CELL_Y_SIZE;
// clip tiles w/ respect to Y screen boundary
clip_bottom = SCREEN_HEIGHT - y_offset;
if ((clip_bottom)<CELL_Y_SIZE)
if ((clip_bottom)<=0)
break;
else
rcRect.bottom = SCREEN_HEIGHT-y_offset;
} // end for render_it_y
// print a message on the backbuffer
if (lpddsback->GetDC(&hdc) == DD_OK)
{
SetBkColor( hdc, RGB( 0, 0, 255 ) );
SetTextColor( hdc, RGB( 255, 255, 0 ) );
TextOut( hdc, 0, 0, "Keys: LEFT, RIGHT, UP, DOWN, .. and ESC", 39 );
lpddsback->ReleaseDC(hdc);
}
// flip back buffer to primary buffer
lpddsprimary->Flip(NULL,DDFLIP_WAIT);
last_frame_time = new_frame_time;
// return and let windows have some time
} // end Game_Main
This is a muddleheaded approach to doing a 2d tile scroller, to be sure, and I'm clearly using a couple of variables I don't need. But enough bagging, here's how it works. First off, I'm doing my drawing to a back buffer, which is set up in the DDInit() routine in tiled.cpp.
while(1)
{
if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
// test if this is a quit
if (msg.message == WM_QUIT)
break;
// translate any accelerator keys
TranslateMessage(&msg);
// send the message to the window proc
DispatchMessage(&msg);
} // end if
else
{
// do asynchronous processing here
// call main logic module
Game_Main();
} // end else
} // end while
You can change some of the defines, like, for example, changing SCREEN_WIDTH and SCREEN_HEIGHT to 800 and 600 respectively... or changing the tile sizes...