#2: Basic 2D Shooter Mini-tutorial

Started by
17 comments, last by Evil_Greven 20 years ago
This builds upon the bitmapobject class that we enjoyed previously in the Tetris-Clone In An Hour minitutorial. You need the bitmapobject.h and bitmapobject.cpp for this tutorial. What we want is a simple 2D shooter. We will approach this from the Top-down scroller that you should all be familiar with... even though it doesn't really scroll On with the program! The basics. We need windows.h, stdlib.h, and the bitmapobject.h... also you can name your game here.

//2d space shooter
//main.cpp
//tell compiler not to include many header files.
#define WIN32_LEAN_AND_MEAN
//////////////////////////////////////////////////////////////////////////////
//INCLUDES
//need this for windows stuff
#include <windows.h>
//need this for srand/rand
#include <stdlib.h>
//now our bitmapobject class
#include "bitmapobject.h"

//////////////////////////////////////////////////////////////////////////////
//DEFINES
//we'll give our window a name
#define WINDOWCLASS "HYPERBLAST"

//now for a title
#define WINDOWTITLE "HyperBlast!"
   
Ok, now we need to think about a few things. -What size do we want the ships (player/enemy)... hmm.. 45x45 is a good size. -What about the bullets? 5x10 would go well with the 45x45 -How many max enemies should be possible? Eh.. 20, to leave us some room. -Bullets? Well, let's use 20x5per enemy, so 100 is good.. -Fire delay for the player? 1/3rd of a second sounds good. -Fire delay on the enemy? Let's make it 2x the player's. -Spawn delay on enemies? Hmm.. how about 6 seconds or so. -Player's speed? How about 7pixels per 1/30s -Enemy speed? 2pixels per 1/30s -Bullet speed? 10pixels per 1/30s -Window width/height... 640x480 is good.

//now we need a standard ship size.. how about 45x45?
#define SHIPSIZE 45
//and projectile size.. hmm how about 10x5?
#define BULLETHEIGHT 10
#define BULLETWIDTH 5

//max number of enemies?  hmm how about 20
#define MAXENEMY 20

//max number of bullets? 100's a good one.
#define MAXBULLET 100

//delay before player is allowed to fire? in 1/3sec
#define FIREDELAY 10
//delay before enemy is allowed to fire in 1/3sec
#define ENEMYFIREDELAY 20

//enemy spawn delay in 1/3sec
#define NEWENEMYDELAY 100

//define player agility speed
#define SPEED 7

//define enemy speed
#define ENEMYSPEED 2

//define bullets' speed
#define BULLETSPEED 10

//let's make our window size 640x480
#define WINDOWWIDTH 640
#define WINDOWHEIGHT 480
   
Now, we need a macro to determine if a key is down or up.. actually two macros.

//////////////////////////////////////////////////////////////////////////////
//MACROS
//we need this to determine if the key is up or down
#define KEYDN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
#define KEYUP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)
   
Functions again. Well, we need a Program Initialization, a Program Loop, and a Program Done. Not listed here, but we also must have a TheWndProc and a WinMain function. What else do we need? -Well, we need a function to handle if someone dies to restart the level... NewLevel. -We need a function to draw the playing field.. DrawMap. -We need a function to handle normal user input, so Keyhandler. -We also need a function to Move the player ship. -Then, we need functions to handle firing a bullet and moving it, so FireBullet and MoveBullet respectively. -Where we have bullets, we must have collisions, so CheckHit. -Next, we need a function to make enemies, and have them fire, so NewEnemy and EnemyFire. With the FireBullet, the function takes an xoff(set) and yoff(set) based on the origin of the ship (in pixel terms in 0-44 (45 total) it'd be 22x22 origin)

//////////////////////////////////////////////////////////////////////////////
//FUNCTION PROTOTYPES

bool Prog_Init(void); //game data initializer
void Prog_Loop(void); //main game loop
void Prog_Done(void); //game cleanup
void NewLevel(void); //when player dies etc
void DrawMap(void);	  //draw the playfield
void Keyhandler(void);//function to handle specific input
void Move(int x, int y);//move player ship
void FireBullet(int type, int offx, int offy, int ship);//fire bullet
void MoveBullet(void);//move bullets
int CheckHit(int x, int y, int type);//anyone hit?
void NewEnemy(void);//make a new enemy if able!
void EnemyFire(void);//enemies fire if they can!
   
Now for some globals. We must have an main app handle and a handle to a main window. Then of course we need three BitMapObjects.. one for the buffer, one for the player, and one for the bullet. Last, we need a RECT to black out the screen.

//////////////////////////////////////////////////////////////////////////////
//GLOBALS
HINSTANCE hInstMain=NULL;//main application handle
HWND hWndMain=NULL;      //handle to our main window

//the playfield
BitMapObject bmoMap;
//the player
BitMapObject bmoPlayer;
//the bullets
BitMapObject bmoBullet;
//used for clearing screen
RECT rcTemp;
   
Now, we need a few structures.. a coordinate structure, an enemy structure, and a bullet structure. The bullets need a type and coordinate. The enemies need a type, coordinate, hitpoints, firedelay, and lastly something different - another BitMapObject! This way, you can load different .BMPs into enemies with the exact same structure, and even give them different healths. You could also build further and change the speeds / fire delay / number of guns etc.

//first, a coordinate structure.
struct coord
{
	int x;
	int y;
};

//we need this for our enemy structure.
struct enemy
{
	coord Pos; //where is it?
	int FireDelay; //time to next shot?
	BitMapObject bmoEnemy; //the enemys
	int Hp; //how much health does it have?
	int Type; //type of enemy
};

//now for the bullets!
struct bullet
{
	coord Pos;
	int Type;
};
   
Now for an array to handle the enemies and bullets

//our enemy array
enemy Enemy[MAXENEMY];
//our bullet array
bullet Bullets[MAXBULLET];
   
Next, we need to know where the player is, how much health he or she has, the fire delay of his or her guns, if the game is paused, and if the player is alive. We also need to keep track of timing, as well as enemy spawning. Last, let's add something fun. What we'll do is increase the spawn rate (decrease the time between spawns) by means of another variable known as CurrentEnemyDelay. When an enemy is destroyed by the player, we'll decrement the CurrentEnemyDelay... so we'll do that later.

coord PlayerPos;//where is the player?
int PlayerHp; //player's health
int FireDelay; //player's fire delay
bool PAUSED; //game paused?
bool ALIVE;
DWORD start_time; //used for timing
int SpawnTimer; //used for enemy spawn
int CurrentEnemyDelay; //respawn timer counts down per enemy destroyed
   
Blah, the stupid Windows Message Handler is back... You Can Never Escape It! However, other than the basic Esc key exits / Rendering / Pause game, we also have one other thing here. We have a check for VK_CONTROL being pressed. This only works if the player is dead, it will be used to restart the level. The game will be paused when the player is killed.

//////////////////////////////////////////////////////////////////////////////
//WINDOWPROC
LRESULT CALLBACK TheWindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
	//which message did we get?
	switch(uMsg)
	{
	case WM_KEYDOWN:
		{
			//check for escape key
			if(wParam==VK_ESCAPE)
			{
				PostQuitMessage(0);
				return(0);
			}
			else if(wParam==VK_CONTROL)
			{
				if(!ALIVE)
				{
					NewLevel();
				}
				return(0);
			}
			else if(wParam==VK_PAUSE) //check for pause key
			{
				if(!PAUSED)
				{
					PAUSED=true;
				}
				else if(PAUSED && ALIVE)
				{
					PAUSED=false;
				}
				return(0);//handled message
			}
		}break;
	case WM_DESTROY://the window is being destroyed
		{

			//tell the application we are quitting
			PostQuitMessage(0);

			//handled message, so return 0
			return(0);

		}break;
	case WM_PAINT://the window needs repainting
		{
			//a variable needed for painting information
			PAINTSTRUCT ps;
			
			//start painting
			HDC hdc=BeginPaint(hwnd,&ps);

			//redraw the map
			BitBlt(hdc,0,0,WINDOWWIDTH,WINDOWHEIGHT,bmoMap,
			       0,0,SRCCOPY);

			//end painting
			EndPaint(hwnd,&ps);
					
			//handled message, so return 0
			return(0);
		}break;
	}

	//pass along any other message to default message handler
	return(DefWindowProc(hwnd,uMsg,wParam,lParam));
}
   
Look, it's WinMain() again... and it's identical to the Tetris-clone tutorial!

//////////////////////////////////////////////////////////////////////////////
//WINMAIN
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd)
{
	//assign instance to global variable
	hInstMain=hInstance;

	//create window class
	WNDCLASSEX wcx;

	//set the size of the structure
	wcx.cbSize=sizeof(WNDCLASSEX);

	//class style
	wcx.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;

	//window procedure
	wcx.lpfnWndProc=TheWindowProc;

	//class extra
	wcx.cbClsExtra=0;

	//window extra
	wcx.cbWndExtra=0;

	//application handle
	wcx.hInstance=hInstMain;

	//icon
	wcx.hIcon=LoadIcon(NULL,IDI_APPLICATION);

	//cursor
	wcx.hCursor=LoadCursor(NULL,IDC_ARROW);

	//background color
	wcx.hbrBackground=(HBRUSH)GetStockObject(BLACK_BRUSH);

	//menu
	wcx.lpszMenuName=NULL;

	//class name
	wcx.lpszClassName=WINDOWCLASS;

	//small icon
	wcx.hIconSm=NULL;

	//register the window class, return 0 if not successful
	if(!RegisterClassEx(&wcx)) return(0);

	//create main window
	hWndMain=CreateWindowEx(0,WINDOWCLASS,WINDOWTITLE, 
	                        WS_BORDER | WS_SYSMENU | WS_CAPTION| WS_VISIBLE,0,0,
	                        WINDOWWIDTH,WINDOWHEIGHT,NULL,NULL,hInstMain,NULL);

	//error check
	if(!hWndMain) return(0);

	//if program initialization failed, then return with 0
	if(!Prog_Init()) return(0);

	//message structure
	MSG msg;

	//message pump
	for( ; ; )	
	{
		//look for a message
		if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
		{
			//there is a message

			//check that we arent quitting
			if(msg.message==WM_QUIT) break;
			
			//translate message
			TranslateMessage(&msg);

			//dispatch message
			DispatchMessage(&msg);
		}

		//run main game loop
		Prog_Loop();
		
	}
	
	//clean up program data
	Prog_Done();

	//return the wparam from the WM_QUIT message
	return(msg.wParam);
}
   
Now to initialize the game, we need to clear out the screen and set the size of the window to our desired specifications. Further, we will initialize the buffer (bmoMap) and clear it out with FillRect(), load the player ship graphic, and load the bullet graphic. Last, we'll hit up srand() and start us a NewLevel().

//////////////////////////////////////////////////////////////////////////////
//INITIALIZATION
bool Prog_Init(void)
{
	//set the client area size
	SetRect(&rcTemp,0,0,WINDOWWIDTH,WINDOWHEIGHT);
 
	//adjust the window size based on desired client area
	AdjustWindowRect(&rcTemp,WS_BORDER | WS_SYSMENU | WS_CAPTION| WS_VISIBLE,FALSE);
 
	//set the window width and height
	SetWindowPos(hWndMain,NULL,0,0,rcTemp.right-rcTemp.left,
	             rcTemp.bottom-rcTemp.top,SWP_NOMOVE);

	//create map image
	HDC hdc=GetDC(hWndMain);
	bmoMap.Create(hdc,WINDOWWIDTH,WINDOWHEIGHT);
	FillRect(bmoMap,&rcTemp,(HBRUSH)GetStockObject(BLACK_BRUSH));
	ReleaseDC(hWndMain,hdc);

	bmoPlayer.Load(NULL,"player.bmp");
	bmoBullet.Load(NULL,"bullet.bmp");

	srand(GetTickCount());

	NewLevel();
	return(true);//return success
}
   
Minor cleanup here. Destroying BMOs.

//////////////////////////////////////////////////////////////////////////////
//CLEANUP
void Prog_Done(void)
{
	//////////////////////////
	//clean up code goes here
	//////////////////////////

	bmoPlayer.Destroy();
	bmoBullet.Destroy();
	for(int j=0; j   

Ah, now for the ever-popular game loop!  This is the heart of the program - literally.  It keeps the game going until the unfortunate demise caused by exiting.

We will lock it to 30frames per second with the if(GetTickCount() - start_time) > 33) bit.  Now if we're not paused, we continue.

Then we render the playfield, and decrement the SpawnDelay (and possibly create a NewEnemy() ) and player's FireDelay if applicable.

Last, we have the make the game shoot back with EnemyFire(), we move all the bullets where they're supposed to go with MoveBullet(), and we check for arrow keys / control key presses with Keyhandler()


//////////////////////////////////////////////////////////////////////////////
//MAIN GAME LOOP
void Prog_Loop(void)
{
	if((GetTickCount()- start_time) > 33)
	{
		start_time=GetTickCount();
		DrawMap();

		if(!PAUSED)
		{
			if(SpawnTimer < 1)
				NewEnemy();
			else
				SpawnTimer--;

			if(FireDelay > 0)
				FireDelay--;

			EnemyFire();
			MoveBullet();
			Keyhandler();
		}
	}
}
      

Easy stuff! Right?  We use bmoMap as our buffer to 'print' stuff on, so we must clear it out before we do so with another FillRect() call.  Then we loop through the bullets and enemies and send them to bmoMap via the BitBlt() functions, along with the player.

We need calc and calc2 to halve the calculations done.  Good practice to not do the same thing more than once.  We have to change up how we draw the graphics because we use the origin for the coordinate reference.  But BitBlt() uses the top left, and ends at the bottom right.  So we must convert it by subtracting half of the ship's size from the x and y positions.

Get all that?  It's not that bad once you get the hang of it.  Also, since we're wanting not to overwrite things, we're using a 'shadow' format again, so the first BitBlt() call in each pair is a SRCAND plus a 'shadow' version of the ship to preserve what's at bmoMap, while the second SRCPAINTs over it with the actual graphic.


void DrawMap()
{
	int calc,calc2;

	FillRect(bmoMap,&rcTemp,(HBRUSH)GetStockObject(BLACK_BRUSH));

	for(int i=0; i.Type >= 0)
		{
			calc=Bullets.Pos.x-BULLETWIDTH/2;
			calc2=Bullets.Pos.y-BULLETHEIGHT/2;
			BitBlt(bmoMap, calc, calc2, BULLETWIDTH, BULLETHEIGHT, bmoBullet,
			       BULLETWIDTH, 0, SRCAND);
			BitBlt(bmoMap, calc, calc2, BULLETWIDTH, BULLETHEIGHT, bmoBullet,
			       0, 0, SRCPAINT);
		}
	}

	for(int j=0; j<MAXENEMY; j++)
		if(Enemy[j].Type >= 0)
		{
			calc=Enemy[j].Pos.x-SHIPSIZE/2;
			calc2=Enemy[j].Pos.y-SHIPSIZE/2;
			BitBlt(bmoMap, calc, calc2, SHIPSIZE, SHIPSIZE, Enemy[j].bmoEnemy,
			       SHIPSIZE, 0, SRCAND);
			BitBlt(bmoMap, calc, calc2, SHIPSIZE, SHIPSIZE, Enemy[j].bmoEnemy,
			       0, 0, SRCPAINT);
		}

	calc=PlayerPos.x-SHIPSIZE/2;
	calc2=PlayerPos.y-SHIPSIZE/2;

	BitBlt(bmoMap, calc, calc2, SHIPSIZE, SHIPSIZE, bmoPlayer, SHIPSIZE, 0, SRCAND);
	BitBlt(bmoMap, calc, calc2, SHIPSIZE, SHIPSIZE, bmoPlayer, 0, 0, SRCPAINT);

	//force window to redraw
	InvalidateRect(hWndMain, NULL, FALSE);
}
   </pre>   

Now to make the game start (and start again if the player dies)
Initialize all the variables.. positions, health, etc.

<pre>
void NewLevel(void)
{
	start_time=GetTickCount();
	PAUSED=false;
	ALIVE=true;
	PlayerPos.x=WINDOWWIDTH/2;
	PlayerPos.y=WINDOWHEIGHT-SHIPSIZE;
	FireDelay=0;
	PlayerHp=100;
	NewEnemy();
	SpawnTimer=NEWENEMYDELAY;
	CurrentEnemyDelay=NEWENEMYDELAY;

	for(int i=0; i<MAXENEMY; i++)
		Enemy.Type=-1;
	for(int j=0; j<MAXBULLET; j++)
		Bullets[j].Type=-1;
}
   </pre>   

Now the simple keyhandler.  We need to see if the up/down keys or left/right keys are pressed, as well as using the control key for firing bullets.

Of course, we check to see if the game is paused first.  If you fire a bullet (with a -1 for ship type, since it is the player), simply set the fire delay to the defined delay, and if you move use the move function.

<pre>
void Keyhandler(void)
{
	int x,y;
	x=y=0;

	if(PAUSED)
		return;

	if(KEYDN(VK_CONTROL))
	{
		if(FireDelay < 1)
		{
			FireBullet(0, 0, 0, -1);
			FireDelay+=FIREDELAY;
		}
	}

	if(KEYDN(VK_DOWN))
	{	y=1; }
	else if(KEYDN(VK_UP))
	{	y=-1;}

	if(KEYDN(VK_LEFT))
	{	x=-1; }
	else if(KEYDN(VK_RIGHT))
	{	x=1;}
	
	if(x != 0 || y != 0)
		Move(x,y);
}
   </pre>   

This tiny function does two things:  it checks if the player can move where he or she wants to, and if the player can, it is done.

<pre>
void Move(int x, int y)
{
	if(x>0)
	{
		if(PlayerPos.x < (WINDOWWIDTH - SPEED - (SHIPSIZE/2)))
			PlayerPos.x+=SPEED;
	}
	else if(x<0)
	{
		if(PlayerPos.x > (SHIPSIZE/2) + SPEED)
			PlayerPos.x-=SPEED;
	}

	if(y>0)
	{
		if(PlayerPos.y < (WINDOWHEIGHT - SPEED - (SHIPSIZE/2)))
			PlayerPos.y+=SPEED;
	}
	else if(y<0)
	{
		if(PlayerPos.y > (SHIPSIZE/2) + SPEED)
			PlayerPos.y-=SPEED;
	}
}
   </pre>   

Now we're getting to the fun part.  Firing bullets… mmm.  So, we need a type of bullet, a x-offset, a y-offset, and if the ship is an enemy ship we'll use that ship number to base the bullet from.

Then, we need to loop through the array to see if we have a bullet free to shoot.

<pre>
void FireBullet(int type, int xoff, int yoff, int ship)
{
	for(int i=0; i<MAXBULLET; i++)
		if(Bullets.Type < 0)
		{
			if(ship < 0) //player bullet
			{
				Bullets.Type = type;
				Bullets.Pos.x=PlayerPos.x+xoff;
				Bullets.Pos.y=PlayerPos.y+yoff;
				return;
			}
			else //enemy bullet
			{
				Bullets.Type = type;
				Bullets.Pos.x=Enemy[ship].Pos.x+xoff;
				Bullets.Pos.y=Enemy[ship].Pos.y+yoff;
				return;
			}
		}
}
   </pre>   

Yay! BAM BAM!  Bwahah… er sorry.  Now we make the bullets move.  More exciting than watching them sit there eh?

First we see if the bullet is alive (dead = -1).  We loop through the array to see if bullet is out of bounds (offscreen), and if so we destroy it.  If it is offscreen, we see if it hits anything.  If it doesn't, we just move it.

<pre>
void MoveBullet(void)
{
	for(int i=0; i<MAXBULLET; i++)
	{
		if(Bullets.Type >= 0)
		{
			if(Bullets.Pos.y > WINDOWHEIGHT || Bullets.Pos.y < 0)
				Bullets.Type=-1;
			else if(CheckHit(Bullets.Pos.x, Bullets.Pos.y, Bullets.Type))
				Bullets.Type=-1;
			else
			{
				if(Bullets.Type == 0) //player bullet goes up
					Bullets.Pos.y-=BULLETSPEED;
				else if(Bullets.Type == 1) //enemy bullet goes down
					Bullets.Pos.y+=BULLETSPEED;
			}
		}
	}
}
   </pre>   

Now, what fun would shooting bullets be if we had nobody to shoot them at?  Here's where the enemy comes in!

We check to see if there is a free spot in the array available, and if so, we set up a random start position, load up the bitmap, and set health at 1 (1 bullet kills), as well as the ship type being alive (-1 is dead).

<pre>
void NewEnemy(void)
{
	for(int i=0; i<MAXENEMY; i++)
		if(Enemy.Type <0)
		{
			SpawnTimer=CurrentEnemyDelay;
			Enemy.bmoEnemy.Load(NULL,"enemy.bmp");
			Enemy.FireDelay=FIREDELAY;
			Enemy.Hp=1;
			Enemy.Pos.x=(rand()%(WINDOWWIDTH-(SHIPSIZE*2))) + SHIPSIZE;
			Enemy.Pos.y=SHIPSIZE;
			Enemy.Type=0;
			return;
		}
}
   </pre>   

Now to make the enemy fight back!!!  We also move the enemies in this function as well as making them fire.

Loop through the array, and see if any of them are alive.  When you find an alive offe, we first check to see if it is offscreen.  If it is not, we destroy it.

Next, we move the enemy down the screen toward the player (unless of course the player is hiding at the top…).

Lastly, we check to see if the enemy can fire - if it can, it fires, and if it can't, it decrements the fire delay.

<pre>
void EnemyFire(void)
{
	for(int i=0; i<MAXENEMY; i++)
		if(Enemy.Type >= 0)
		{
			if(Enemy.Pos.y > WINDOWHEIGHT)
				Enemy.Type=-1;
			else
			{
				Enemy.Pos.y+=ENEMYSPEED;
				if(Enemy.FireDelay > 0)
					Enemy.FireDelay–;
				else
				{	
					FireBullet(1, 0, 0, i);
					Enemy.FireDelay=ENEMYFIREDELAY;
				}
			}
		}
}
   </pre>   

We're almost done!  One thing left!  This is the collision detection function… what fun would shooting and being shot at be if you couldn't damage/destroy each other?  In a game, of course.  We are using a collision check which was thought up in an <a href="http://www.gamedev.net/community/forums/topic.asp?topic_id=191097">old thread</a> where I posed a silly trick question.

It's very, very fast.  Fastest one I've yet seen, and this is VERY crucial because of how much time your game will be spending in the collision detection function.

<pre>
int CheckHit(int x, int y, int type)
{
	int calc=SHIPSIZE/2;
	if(type == 0)
	{
		for(int i=0; i<MAXENEMY; i++)
		{
			if(Enemy.Type >= 0)
				if(!!(0x8000000 & (Enemy.Pos.x-x-calc & 
			              Enemy.Pos.y-y-calc & 
				      -Enemy.Pos.x+x-calc & 
			              -Enemy.Pos.y+y-calc)))
				{
					Enemy.Hp-=1;
					if(Enemy.Hp < 1)
					{
						Enemy.Type=-1;
						CurrentEnemyDelay-=1;
					}
					return 1;
				}
		}
	}
	else if(type == 1)
	{
		if(!!(0x8000000 & (PlayerPos.x-x-calc & 
		      PlayerPos.y-y-calc & 
		      -PlayerPos.x+x-calc & 
		      -PlayerPos.y+y-calc)))
		{
			PlayerHp-=1;
			if(PlayerHp < 1)
				ALIVE=false;
			return 1;
		}
	}

	return 0;
}
   </pre>   

And that's it!

You won't see anything until you make 3 bitmaps - player.bmp, an enemy.bmp, and a bullet.bmp.

The player.bmp and enemy.bmp dimensions should be 90wide x 45tall.  The bullet.bmp dimensions should be 10wide x 10tall.

However, off each of those bitmaps they will need a split format - that is to say, they need to be split down the center at 45 and 5 respectively.  You'll need to have something like this (bullet.bmp example):

B=Black, R=Red, Y=Yellow, W=White, |=imaginary line
<pre>
BBBBB|WWWWW
BBRBB|WWBWW
BRYRB|WBBBW
BRYRB|WBBBW
BRYRB|WBBBW
BRYRB|WBBBW
BRYRB|WBBBW
BRYRB|WBBBW
BBRBB|WWBWW
BBBBB|WWWWW
   </pre>   

So that off the left side, you have a black background with the graphic off it.  off the right side, you need a shadow - that is, the background is white and the graphic is totally black.  This is for transparency purposes.

Again, there is no sound, however you can add a simple sound playing method easily.
1. #include &lt;mmsystem.h>
2. link the compiler to winmm.lib
3. use the PlaySound("yoursoundhere.wav", hInstMain, SND_ASYNC | SND_FILENAME) function wherever you want a sound played, ex. when bullets are fired/destroyed, ships destroyed.

Also, no score has been kept, and no health is being displayed.  You can do this a number of ways.  One way I did it in my Stormrunner game was make it so that ships changed hull color to redish (run through a Colorize filter in PSP).

Further, you could add a scrolling starfield pretty easily with a pair of fullscreen bitmaps (1 is a mirrored and flipped version of 1 in my little game), 4 BitBlt() calls, and a global counter.

Hope this is of some use.
-Greven

edit: and i thought i had it totally right this time.. stupid for(;<img src="wink.gif" width=15 height=15 align=middle>

<SPAN CLASS=editedby>[edited by - Evil_Greven on March 28, 2004 10:42:54 PM]</SPAN>

[Edit: Correcting "broadsheet".]

<SPAN CLASS=editedby>[edited by - Oluseyi on March 30, 2004 12:51:40 PM]</SPAN>   
Advertisement
amazing. not often this stuff gets posted. QUALITY thread. these should be thrown into the article/resources area of gamedev IMO
"The human mind is limited only by the bounds which we impose upon ourselves." -iNfuSeD
You really should stop posting those things in here and submit them as articles. I have a feeling that the GDNet staff will be happy to post these are articles on the frontpage. You can still post links to the articles in here then.

Toolmaker


-Earth is 98% full. Please delete anybody you can.

Hey great post. Like the others said, this would make a fantastic article.

"If you are not willing to try, you will never succeed!"

Grellin
"If you are not willing to try, you will never succeed!"GrellinC++ Game Programming
The only thing is, I don''t really think it''s worth a full article. It isn''t structured very formally, and it really didn''t take that long to make (~3 hours).

If one of the moderators thinks it is worthy of being a full-fledged article, then I can work from there... but I really don''t.

-Greven
Greven.

Can I ask you if you know how to handle that my Tetris-clone is available for play when Microsoft Visual C++ is open and less available to be opened as a unit?

Thorbjørn.

The code is available at www.tomsborg.no/Moleculesfiler
Thorbjørn.www.tomsborg.no
Uh, you mean you can''t run the .exe unless MSVC is open?

I haven''t really had that problem before... try compiling it for Release, it may be a Debug setting that you have active.

-Greven
quote:Original post by Evil_Greven
If one of the moderators thinks it is worthy of being a full-fledged article, then I can work from there... but I really don''t.
Remember that we have different types of articles. Sweet Snippets are tightly focused and mostly code; submit it, we''ll find a way to whinge it in (and I speak authoritatively since I''m on the Editorial Review Board).
and there you have it!

so go man! make the article already :D

...oh and thanks...

Beginner in Game Development?  Read here. And read here.

 

hey Greven,

could you go over the PAUSE function and how you got that to work.
i didn''t catch it from reading the code.

Beginner in Game Development?  Read here. And read here.

 

This topic is closed to new replies.

Advertisement